微服务架构入门之服务注册发现与配置中心|Spring Cloud Alibaba Nacos---练气中期

2,153 阅读13分钟

微服务架构入门---服务注册与配置中心Nacos

什么是Nacos

定义

Nacos (Naming Configration Service)是一个 Alibaba 开源的、易于构建云原生应用的动态服务发现、配置管理和服务管理平台。

Nacos是服务中心与配置中心的结合。

官网

nacos.io/zh-cn/index…

下载与安装

环境:

  1. 64 bit OS,支持 Linux/Unix/Mac/Windows,推荐选用 Linux/Unix/Mac。
  2. 64 bit JDK 1.8+;
  3. Maven 3.2.x+;

下载安装

下载地址:github.com/alibaba/nac…

国内高速下载地址:gitee.com/mirrors/Nac…

CentOS安装Nacos

第一步 下载:

我们可以选择从 gitee.com/mirrors/Nac… 下载压缩包(里面提供的是百度网盘的下载源),然后通过xftp传输到服务器当中。

第二步 解压:

tar -zxvf nacos-server-1.3.2.tar.gz /opt/

第三步 修改application.properties配置文件

3.1 在配置文件当中解除以下三行配置(nacos选用mysql作为数据持久化需要)

在0.7版本之后支持使用mysql作为持久化数据源,在这之前,使用的是nacos的内置数据库。

spring.datasource.platform=mysql
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user=nacos
db.password=nacos

3.2 将里面的IP和Port改成自己的IP地址与端口号。

3.3 执行/nacos/conf/nacos-mysql.sql脚本,创建对应的数据。再然后创建一个nacos用户,并将nacos数据的权限分配给对应用户。

第四步 在防火墙当中开启nacos对应的端口号,默认为8848(建议改一下,同样在application.properties中修改)。

如果你的机器是购买的阿里云服务器,还需要在实例安全组当中,开启对应的端口号。

第五步 启动nacos

进入到nacos的bin目录下

执行sh startup.sh -m standalone,启动。standalone表示单机模式启动。

(提示:如果是在Windows下,在解压完毕后,需要编辑startup.cmd文件,修改以下参数,不然启动数据库会连接不上,如果数据库不做配置的话)。默认是集群模式启动的,我们改为单机启动。

即修改成单机模式。

浏览器打开http://IP:Port/nacos/index

输入用户名和密码(默认都是nacos),登录进去

即可看到nacos控制台。

至此,我们的nacos就完成安装了。

Nacos作为服务中心

创建服务发布者(服务提供方)

我们继续沿用上一篇文章的demo。我们新建一个支付模块service-publisher

添加nacos依赖

 	<dependency>
         <groupId>com.alibaba.cloud</groupId>
         <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>

编辑application.yml,添加服务配置

server:
  port: 9001
spring:
  application:
  # 服务名必须要有,这将区别其他服务的标识
    name: provider-payment9001
  cloud:
  # nacos服务中心配置
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        
management:
  endpoints:
    web:
      exposure:
        include: '*' # 监控端点全部打开

编辑启动类

添加springCloud原生注解EnableDiscoveryClient开启服务注册与发现功能

/**
 * @author 赖柄沣 bingfengdev@aliyun.com
 * @version 1.0
 * @date 2020/10/20 9:04
 */
@SpringBootApplication
@EnableDiscoveryClient
@ComponentScan(basePackages = "pers.lbf.spring.cloud.alibaba.demo.provider.payment9001")
public class Payment9001App {
    public static void main(String[] args) {
    	SpringApplication.run(PaymentApp.class,args);
  }
}

提供一个服务接口

/**服务发布者测试接口
 * @author 赖柄沣 bingfengdev@aliyun.com
 * @version 1.0
 * @date 2020/10/20 9:34
 */
@RestController
@RequestMapping("/test")
public class TestController {

    @GetMapping("/getInfo")
    public String doPay(){
        return "来自服务提供者的信息:后天刮台风";
    }

}

启动

启动之后,可以看到服务已经注册到了nacos当中。 对应控制台输出

创建服务订阅者(服务调用方)

我们创建一个服务消费者模块service-subscriber,来模拟调用服务服务。

添加nacos依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    <version>${latest.version}</version>
</dependency>

编辑application.yml

添加服务订阅者的相关配置,使其可以通过nacos的服务注册发现功能调用它所需的服务

server:
  port: 8001
spring:
  application:
    name: service-subscriber
  cloud:
    nacos:
      discovery:
        server-addr: 172.17.172.49:8848

management:
  endpoints:
    web:
      exposure:
        include: '*' # 监控端点全部打开
        

# 服务订阅者订阅的服务以及负载均衡策略配置
# 服务名
service-publisher:
  # 服务url
  service-url: http://service-publisher/

编辑启动类

/**
 * @author 赖柄沣 bingfengdev@aliyun.com
 * @version 1.0
 * @date 2020/10/20 9:04
 */
@SpringBootApplication
@EnableDiscoveryClient
@ComponentScan(basePackages = "pers.lbf.spring.cloud.alibaba.demo.service.subscriber")
public class SubscriberApp {
  public static void main(String[] args) {
    SpringApplication.run(SubscriberApp.class,args);
  }

    /**
     * 通过restTemplate进行服务调用
     * 通过@LoadBalanced注解开启负载均衡和主机地址端口映射
   * @return restTemplate
   */
  @LoadBalanced
  @Bean
  public RestTemplate restTemplate(){
    return new RestTemplate();
  }

}

编写一个调用接口,进行测试


/**
 * @author 赖柄沣 bingfengdev@aliyun.com
 * @version 1.0
 * @date 2020/10/20 9:34
 */
@RestController
@RequestMapping("/test")
public class TestController {

    @Autowired
    private RestTemplate rest;

  @Value("${subscriber.nacos.service.url}")
  private String serviceURL;

    @GetMapping("/getInfo")
    public String getInfo(){

        return rest.getForObject(serviceURL+"/test/getInfo",String.class);
    }

}

启动

如果nacos还没有启动就先启动nacos,然后启动服务。

然后访问服务订阅者的/test/getInfo

可以看到,我们访问了服务订阅者,然后服务订阅者调用了服务发布者提供的服务。

负载均衡

nacos本身就支持负载均衡(其内部整合了ribbon).为了演示负载均衡,我们修改一些服务发布者的测试接口,使其返回信息的同时,返回其端口号,并启动两个服务发布者的实例。

访问接口

9001

9002

nacos控制台

负载均衡算法

在目前的版本当中,默认使用的是基于轮询的负载均衡算法。

常见的几种负载均衡算法介绍

  1. 轮询(Round Robin)

    ​ 这是一种简单有效的负载均衡算法,负载均衡服务器会将请求循环分发给服务器集群中的每台机器。在这种模式下,需要有一个前提,那就是所有服务器的资源都在同一水平。这个前提接近于一个理想的情况。在实际部署当中,如果某台服务器较弱,那么这个算法将导致其疲于奔命。相反,如果某台服务器较强,采取这种算法,将浪费资源。

  2. 加权轮询(Weighted Round Robin)

    ​ 这种算法解决了轮询算法的缺点,它根据用户设定的服务器权重,进行轮询分发服务请求。例如A服务器配置比较高,设定权重值为20,B服务器配置大约是A服务器一半左右,设定其服务器权重值为10.那么每次循环分发请求时,一个循环内,服务器A收到的请求数为B服务器的2倍。

  3. 最少连接数(Least Connection)

    ​ 在上述的两种算法当中,都不能识别到服务器当前的连接数。一个极端的情况是,假设A服务器当中的连接数已经接近了其处理阈值,如果根据加权轮询算法,在一次循环下来,2/3的请求还是会分发给A。那么即使是A的资源比较多,也是处理不过来的。

    ​ 最少连接数算法在每次分发请求时,根据当前服务器的连接数来分配请求。即活跃连接数最少的服务器将收到下次请求。这种算法统样都一个前提,那就是各个服务器资源都差不多,基本处于统一水平。

  4. 加权最少连接(Weighed Least Connection)

    ​ 最少连接数算法同样没有考虑到服务器的具体配置情况。加权最少连接类似于加权轮询,它可以让用户根据服务器资源情况配置服务器连接权重。然后,它根据连接数和服务器权重比例,集群中比例最低的服务器,自动接收下一个请求。

  5. 最少连接数慢启动时间(Least Connection Slow Start Time)

    ​ 对最少连接和加权最少连接数调度算法来说,当一个服务器刚加入线上环境时,可以为其配置一个时间间隔。在这个时间间隔内,连接数是有限制的并且是缓慢增加的。这为服务器提供一个适应期,使其不会因为刚启动后因为分发的连接数过多而出现超载。

  6. 基于代理的自适应负载均衡(Agent Based Adaptive Balancing)

    ​ 这是一种在运行时通过动态地修改服务器权重值的方式,然后根据权重值分发请求。在这种策略下,负载均衡服务器可以根据服务器的运行时状态,动态地调整分发请求数,从而达到负载均衡的目的。

    ​ 在上述的权重值计算时,需要考虑的影响因素有:cpu利用率、内存利用率、磁盘利用率、缓存等。

  7. 源IP地址哈希(Source IP Hash)

    ​ 这种算法通过获取请求的IP地址并计算其哈希值与服务器IP进行匹配,然后分发请求。在这种方式下,可能由于地域等原因引起负载不平衡。

修改默认的负载均衡策略

我们知道,nacos集成了Ribbon,因此,Ribbon的负载均衡策略同样适用于nacos。

Ribbon支持的负载均衡策略配置类继承了AbstractLoadBalancerRule这个类。

规则名称特点
AvailabilityFilteringRule过滤掉一直连接失败的被标记为circuit tripped(电路跳闸)的后端Service,并过滤掉那些高并发的后端Server或者使用一个AvailabilityPredicate来包含过滤Server的逻辑,其实就是检查status的记录的各个Server的运行状态
NacosRole支持优先调用同一集群的功能区负载平衡规则实例。
BestAvailableRule选择一个最小的并发请求的Server,逐个考察Server,如果Server被tripped了,则跳过
RandomRule随机选择一个Server
ResponseTimeWeightedRule已废弃,作用同WeightedResponseTimeRule
RetryRule对选定的负责均衡策略机上充值机制,在一个配置时间段内当选择Server不成功,则一直尝试使用subRule的方式选择一个可用的Server
RoundRobinRule轮询选择,轮询index,选择index对应位置Server
WeightedResponseTimeRule根据相应时间加权,相应时间越长,权重越小,被选中的可能性越低
ZoneAvoidanceRule(默认是这个)负责判断Server所Zone的性能和Server的可用性选择Server,在没有Zone的环境下,类似于轮询(RoundRobinRule)

切换负载均衡算法

在服务调用者的配置当中添加负载均衡策略配置

server:
  port: 8001
spring:
  application:
    name: service-subscriber
  cloud:
    nacos:
      discovery:
        server-addr: 172.17.172.49:8848

management:
  endpoints:
    web:
      exposure:
        include: '*' # 监控端点全部打开
        

# 服务订阅者订阅的服务以及负载均衡策略配置
# 服务名
service-publisher:
  # 负载均衡算法配置
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
  # 服务url
  service-url: http://service-publisher/

Nacos与其他注册中心对比

CAP理论

概述

在一个分布式系统中最多只能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三项中的两项。这是针对分布式数据库提出的理论。

简单理解

假设存在两个数据库实例A、B,A与B之间通过网络进行数据同步(保持数据一致性)。此时,由于网络原因A、B之间无法通信,同时A的数据为最新数据,B的数据为过时数据(这里的最新数据和过时数据均指客户端即将访问的那部分数据)。

在CP架构下,如果客户端访问B,由于为了确保数据的一致性(读操作保证能够返回最新的写操作的结果),此时,只能向客户端返回错误提示,而非数据。

在AP架构下,如果客户端访问B,由于为了确保可用性,将会返回B中的数据信息,尽管它是过时的。

Nacos对这两种模式都支持,至于采用哪种策略,就要看具体的系统了。

何时采用何种模式?

一般来说,如果不需要存储服务级别信息且服务实例是通过nacos-client注册的,并且能够保持心跳上报,那么就可以选择AP模式。在AP模式下,为了满足服务的可用性,而牺牲了服务的一致性,因此在这种模式下仅支持注册临时实例。

如果需要在服务级别编辑或者存储配置信息,则需要采用CP模式。K8S和DNS服务则适用于CP模式。CP模式下支持注册持久化实例。此时则是以Raft协议为集群模式,该模式下,注册实例之前必须注册服务,不然会返回错误。

修改方法

curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switcher?entry=serverMode&value=CP'

Nacos作为配置中心

nacos不但可以替换Eureka作为服务注册中心,也可以替换spring-config作为配置中心。

在介绍Nacos作为配置中心的使用之前,我们先回顾一下传统的配置方式

几种配置方式的比较

1. 硬编码方式

在这种方式下,系统的配置信息保存在配置类里面,以常量的形式存在。

优势:

  1. 开发友好。。。

劣势:

  1. 修改时需要重新编译相关类,并重新发布
  2. 配置信息直接暴露给发开人员,增加了敏感信息泄密的风险

2. 配置文件的形式

在这种方式下,配置信息保存在xml、properties、yaml等文件中,例如springboot常用到的application.yml。这是大部分应用常用的配置方式。

优势:

  1. 实现了代码与配置分离,提供了更加灵活的配置方式
  2. 配置信息修改后,系统只需要重启,而不需要重新发布

劣势:

  1. 配置文件格式难以统一
  2. 配置文件多、维护困难

3. 使用数据库

在这种方式下,配置信息保存在数据库当中。

优势:

  1. 修改灵活
  2. 无需重启服务

劣势:

  1. 必须保证数据库的高可用
  2. 配置信息没有版本管理,难以回滚
  3. 成本较大

4. 采用配置中心

这是微服务架构当中必选的方式。同时,配置中心也是微服务架构中不可或缺的组件。

优势:

  1. 集中化管理,敏感配置可控
  2. 多版本存储,方便追溯
  3. 界面友好,修改配置一键发布

劣势:

  1. 必须要保证高可用
  2. 引入新的组件,增加了系统风险

总而言之,各种配置策略各有优缺点,至于采用哪种策略,还是得根据具体情况具体分析。对于单体应用来说,采用硬编码+配置文件的形式足以解决大部分场景下的问题。然而,对于分布式应用,集中化管理配置信息、最小代价的修改配置显得尤为重要。采用配置中心组件,成为了一个必选项。

创建配置服务模块

我们创建一个配置服务模块service-config ,提供配置服务。

添加依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

添加配置

bootstrap.yml添加如下配置

spring:
  cloud:
    nacos:
      server-addr: 172.17.172.49:8848
  application:
    name: service-config

我们可以把配置信息的存储的数据结构看成是一个集合Set,nacos通过配置信息的key,获取配置信息的value。而这个spring.application.name构成这个key的一部分。实际上这个key叫dataId.

在 Nacos Spring Cloud 中,dataId 的完整格式如下:

${prefix}-${spring.profiles.active}.${file-extension}
  • prefix 默认为 spring.application.name 的值,也可以通过配置项 spring.cloud.nacos.config.prefix来配置。

  • spring.profiles.active 即为当前环境对应的 profile。

    注意:当 spring.profiles.active 为空时,对应的连接符 - 也将不存在,dataId 的拼接格式变成 ${prefix}.${file-extension}

  • file-exetension 为配置内容的数据格式,可以通过配置项 spring.cloud.nacos.config.file-extension 来配置。目前只支持 propertiesyaml 类型。

编写启动类

/**配置服务
 * @author 赖柄沣 bingfengdev@aliyun.com
 * @version 1.0
 * @date 2020/10/22 9:03
 */
@SpringBootApplication
@ComponentScan("pers.lbf.spring.cloud.alibaba.demo.service.config")
public class ConfigApp {

  public static void main(String[] args) {
    SpringApplication.run(ConfigApp.class,args);
  }
}

编写配置信息接口

/**
 * @author 赖柄沣 bingfengdev@aliyun.com
 * @version 1.0
 * @date 2020/10/22 15:41
 */
@RequestMapping("/user")
@RefreshScope
@RestController
public class UserConfigController {

    @Value("${userCacheEnabled:false}")
    private boolean userCacheEnabled;

    @GetMapping("/getUserCacheEnabled")
    public boolean getUserCacheEnabled() {
        return this.userCacheEnabled;
    }

}

在这里,nacos通过springCloud原生注解@RefreshScope表示自动刷新配置信息。

测试

启动项目后获取默认的配置,此时值为false

调用nacos API修改配置信息,

然后再次访问,此时结果为false

查看nacos控制台的配置列表,可以看到相关的配置信息

我们可以通过Nacos控制台来编辑配置信息,并发布

MySQL的nacos数据库当中的config_info中将持久化存储相关配置信息

多环境、多DataId配置(Group的使用)

新建三个mysql的配置,我们通过Group来区分开发环境、测试环境和生产环境。

然后我们在前面的服务发布者模块中添加nacos配置中心的依赖,和其他依赖

<!--        nacos配置中心-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>

		<dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>

我们将通过这个模块测试nacos作为配置中心的使用

bootstrap.properties文件

# 通过命名空间来区分哪些配置属于哪些应用
spring.cloud.nacos.config.namespace=public
spring.cloud.nacos.config.server-addr=172.17.172.49:8848

# mysql配置
spring.cloud.nacos.config.extension-configs[0].data-id=mysql-datasource.properties
spring.cloud.nacos.config.extension-configs[0].group=dev
spring.cloud.nacos.config.extension-configs[0].refresh=true

# mybatis配置
spring.cloud.nacos.config.extension-configs[1].data-id=mybatis-config.properties
spring.cloud.nacos.config.extension-configs[1].group=dev
spring.cloud.nacos.config.extension-configs[1].refresh=true


​ 说明:

  • spring.cloud.nacos.config.extension-configs.refresh=true # 刷新配置,配置中心中的配置信息发生改变时将自动刷新

编写测试接口,查询数据库

(这一步代码省略,文末提供代码下载地址)

测试

如果能正常返回数据库数据,则证明配置加载成功

profile多环境配置

在nacos当中,如果需要多环境配置有两种方式,一种是如上所列,通过设置Group来区分不同的环境,然后在需要切换环境时修改bootstrap.properties里面的组(Group)信息。

这种方式有一个缺点就是,当配置文件多时,修改的地方也多,出错的概率酒增多了。而且通过Group区分环境,如果存在很多个服务的话,也将存在很多组。在我看来,组用来区分是哪一类的配置文件,区分是数据配置还是日志配置,这个更符合其含义。

其实,Nacos是支持profile粒度的配置的。

此时,完整的dataId格式为${spring.application.name}-${profile}.${file-extension}

file-extension默认为properties,你也可以通过在bootstrap.properties中显示声明spring.cloud.nacos.config.file-extension=yaml,使用.yml格式的配置文件。

我们根据已有的配置文件克隆生成新的配置文件

bootstrap.properties文件

spring.profiles.active=dev
# 通过命名空间来区分哪些配置属于哪些应用
spring.cloud.nacos.config.namespace=public
spring.cloud.nacos.config.server-addr=172.17.172.49:8848

# mysql配置
spring.cloud.nacos.config.extension-configs[0].data-id=mysql-datasource-${spring.profiles.active}.properties
spring.cloud.nacos.config.extension-configs[0].refresh=true
# mybatis配置
spring.cloud.nacos.config.extension-configs[1].data-id=mybatis-config-${spring.profiles.active}.properties
spring.cloud.nacos.config.extension-configs[1].refresh=true

测试

正常读取到了数据

持久化配置

在nacos当中,会将配置信息存储到数据库当中(如果没有显示地配置mysql数据源,将使用其内置的数据库)。并且其使用的是单一数据源,这很好地避免了分布式数据一致性的问题。服务持久化可以避免因为nacos重启导致配置信息丢失。

生产环境下,mysql起码是主备模式。

当我们配置了mysql做为nacos的持久化数据源后,我们可以在数据库当中找到相关的配置信息记录

config_info表记录

集群部署

我们在开发时,可能也就开了一台nacos。但是在生产环境下,起码要开3台以上。不然Nacos一旦出现宕机,将出现非常严重的单点故障事故。

一个简单的集群部署图

使用集中式的mysql存储很好的解决了数据一致性问题。

nacos支持的部署方式

  1. 单机部署:适用于开发和测试
  2. 集群部署:适用于生产环境
  3. 多集群部署:适用于多数据中心场景

部署步骤

(在这之前,默认你已经按照上文所述配置好了数据库)

修改nacos配置文件

修改cluster.conf配置文件,配置Nacso集群的虚拟IP地址

cp /opt/nacos/conf/cluster.conf.example /opt/nacos/conf/cluster.conf
vim /opt/nacos/conf/cluster.conf

添加虚拟ip,后面的端口可省略。保存退出(集群配置至少三个节点以上)

192.168.133.12:8848
192.168.133.11:8848
192.168.133.10:8848

注意:这里的IP是Linux能识别的ip,可以执行hostname -i进行查询,也可以直接输入你的公网IP

然后将cluster.confapplication.properties这两个配置文件同步到其他两个nacos节点上。

启动后随意打开一个节点的nacos控制台,可以看到nacos的集群信息

sh startup.sh

配置nginx集群与反向代理

Keepalived+nginx高可用集群配置

Nginx反向代理配置

MySQL主从配置或高可用配置

这三步已经超出了本文的讲解范围,读者可以自行查阅相关资料进行配置。

当配置完成之后,其他微服务节点的nacos-address都改为nginx的虚拟ip例如192.168.133.223即可。配置nginx反向代理时应当连同负载均衡一起考虑进去。

写在最后

在这篇文章当中,我们开始学习微服务架构中的常用(流行)组件,开始一步步走进微服务架构模式。任重而道远,诸君共勉。[/抱拳]

如果您觉得这篇文章能给您带来帮助,那么可以点赞鼓励一下。如有错误之处,还请不吝赐教。在此,谢过各位乡亲父老!

在下一篇Spring Cloud Alibaba系列的文章当中,我们将开始学习Sentinel。

本文代码链接: github.com/code81192/a…