为什么需要nacos

微服务架构是目前流行的企业技术架构,多个服务由之前部署在一个服务器,开发在同一个程序中,到目前各自部署到不同的服务器中,每个微服务与其他微服务解耦,微服务之间互相调用构成企业技术架构,协同完成企业业务需求;
那具体怎么调用是关键,某些微服务比较核心,比如支付服务,访问量很大,为了避免服务器负载过大夯机,一般会把服务部署在多台服务器实例中,那其他微服务调用时应该调用哪个实例?这是个问题
在这里插入图片描述

注册中心原理

为了解决这个问题,方便微服务之间互相调用,注册中心应运而生,简单来说注册中心就是微服务启动之后把服务名、服务器实例IP和端口注册到注册中心,其他服务调用时直接通过被调用的微服务名称从注册中心拿到被调用微服务的所有实例,然后通过负载均衡算法从其中选择一个实例去调用,就是这么简单。
在这里插入图片描述

  1. 服务提供者:提供接口供其它微服务访问
  2. 服务消费者:调用其它微服务提供的接口
  • 服务提供者会定期向注册中心发送请求,报告自己的健康状态(心跳请求)
  • 当注册中心长时间收不到提供者的心跳时,会认为该实例宕机,将其从服务的实例列表中剔除
  • 当服务有新实例启动时,会发送注册服务请求,其信息会被记录在注册中心的服务实例列表
  • 当注册中心服务列表变更时,会主动通知微服务,更新本地服务列表

nacos注册中心

注册中心的产品有很多,目前国内用nacos还是多一些,nacos是阿里的产品,中文文档会多一些。

nacos安装

建议使用docker安装

  1. docker pull nacos 下载nacos镜像
  2. 创建挂载目录
  3. docker run 创建容器,部署应用
  4. 配置mysql数据库,更改nacos配置,走配置的mysql数据库
  5. 重启nacos

服务注册/调用

nacos使用起来很简单,只要导入起步依赖,配置nacos地址即可

  1. 导入依赖
<!--nacos 服务注册发现-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
  1. 在yaml配置文件配置nacos地址
spring:
  application:
    name: item-service # 服务名称
  cloud:
    nacos:
      server-addr: 192.168.150.101:8848 # nacos地址
  • 在不用openFeign之前,spring cloud会自动为容器自动装配服务发现组件DiscoveryClient,我们通过这个组件和服务名称就能从nacos中找到这个服务的所有实例,然后通过
  • 均衡算法,确定一个实例,发送请求即可。
    在这里插入图片描述

nacos配置中心

  • Nacos不仅是所有微服务的注册中心,也可以作为所有微服务的共享配置中心,所有微服务启动的时候可以从nacos中读取配置文件的内容

  • nacos作为共享配置中心分为三步,依赖引入,在nacos服务界面添加配置文件,微服务的bootstrap.yaml配置文件中指定需要加载的配置文件名。

  • 首先,引入依赖bootstrap和nacos-config,使得服务启动的时候能够先加载bootstrap.yaml配置文件、从nacos配置中心中读取bootstrap.yaml指定的配置文件内容

  <!--nacos配置管理-->
  <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
  </dependency>
  
  <!--读取bootstrap文件-->
  <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-bootstrap</artifactId>
  </dependency>
  • 这样微服务启动后容器初始化的工作流程就如下图所示,程序先加载bootstrap.yaml配置文件,在bootstrap.yaml中配置的nacos服务地址和当前服务需要加载的共享配置文件名称,将这些文件从nacos中加载到程序中,然后再加载服务本地的application.yaml等配置文件,合并所有的配置,最后初始化、注入容器中的相关组件,服务启动成功。

在这里插入图片描述

  • 在nacos中创建共享配置文件,可以使用$参数占位符,和指定参数默认值

在这里插入图片描述

  • 在项目本地新建bootstrap.yaml,主要是指定nacos的地址、端口和需要加载的共享配置文件名称;如果nacos中创建了与当前服务名同名的配置文件,那么也会加载,不需在bootstrap.yaml指定,比如nacos配置中心中有一个配置文件名为cart-service.yaml,那么这个配置文件也会默认被当前微服务加载。
spring:
  application:
    name: cart-service # 服务名称
  profiles:
    active: dev
  cloud:
    nacos:
      server-addr: 192.168.150.101 # nacos地址
      config:
        file-extension: yaml # 文件后缀名
        shared-configs: # 共享配置
           <!--需要读取的配置文件名称-->
          - dataId: shared-jdbc.yaml # 共享mybatis配置
          - dataId: shared-log.yaml # 共享日志配置
          - dataId: shared-swagger.yaml # 共享日志配置

nacos配置热更新

  • 如果nacos配置中心的某一个配置改变,微服务容器中的相关组件可以直接注入新的配置,不需要重新启动,这就是热更新
  • nacos配置热更新要想生效有两个关键点,一是这个配置文件在nacos中需要开启更新自动推送,二是微服务端端使用正确的注解。
  • 服务端推荐两种方式@value+@RefreshScope,第二种方式@ConfigurationProperties注解
  • 当nacos配置文件发生改变时,改变后的内容会自动注入到容器组件中,实现了热更新

openFeign远程调用

上面这种方式虽然可行,但是实现方式比较繁琐,openFeign提供了一种更为简便的方式实现服务间远程调用,简单说就是写openFeign接口,将http协议最重要的请求方式、URL、请求参数、响应体类型写到接口上,然后基于动态代理将代理对象注册到IOC容器中,直接调用openfeign代理对象方法即可实现远程调用。
1. 引入依赖

  <!--openFeign-->
  <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-openfeign</artifactId>
  </dependency>
  <!--负载均衡器-->
  <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-loadbalancer</artifactId>
  </dependency>

2. 定义openfeign客户端接口

package com.hmall.cart.client;

import com.hmall.cart.domain.dto.ItemDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.List;

@FeignClient("item-service")
public interface ItemClient {
    @GetMapping("/items")
    List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
}
  1. @FeignClient("item-service") :声明服务名称
  2. @GetMapping("/items") :声明请求方式和请求路径
  3. @RequestParam("ids") Collection<Long> ids :声明请求参数
  4. List<ItemDTO> :响应体解析类型

3. 使用openfeign客户端

public class Service{
	@Autowired
	private ItemClient itemClient;
	public void test(){
		List<Long> items = Arrays.asList(1l,2l);
		List<ItemDTO> res = itemClient.queryItemByIds(items);
	} 
}

4. 日志管理
只有包的日志级别是debug时,openFeign的日志才会生效 ,此外,还需要单独配置openFeign的日志级别,openFeign的日志级别有4级,Feign默认的日志级别就是NONE,所以默认我们看不到请求日志。

  • NONE:不记录任何日志信息,这是默认值。
  • BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
  • HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
  • FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。
//1.定义openFeign配置类
import feign.Logger;
import org.springframework.context.annotation.Bean;

public class DefaultFeignConfig {
    @Bean
    public Logger.Level feignLogLevel(){
        return Logger.Level.FULL;
    }
}

// 单独配置到某个feignclient接口,或者全局配置
@FeignClient(value = "item-service", configuration = DefaultFeignConfig.class)
//全局配置
@EnableFeignClients(defaultConfiguration = DefaultFeignConfig.class)

openFeign拦截器

  • 微服务架构中很多时候需要传递用户信息等数据,我们可以配置openfeign拦截器实现这个功能,在openfeign客户端发送http请求时会执行拦截器中的逻辑。
  • 配置openfeign拦截器也很简单,直接向容器中注入实现接口RequestInterceptor 的组件(bean)即可
@Bean
public RequestInterceptor userInfoRequestInterceptor(){
    return new RequestInterceptor() {
        @Override
        public void apply(RequestTemplate template) {
            // 获取登录用户
            Long userId = UserContext.getUser();
            if(userId == null) {
                // 如果为空则直接跳过
                return;
            }
            // 如果不为空则放入请求头中,传递给下游微服务
            template.header("user-info", userId.toString());
        }
    };
}
Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐