nacos使用-服务注册中心和配置中心
本例子基于 spring boot + spring cloud alibaba + spring cloud, nacos作为服务注册中心和配置中心。
简介
Nacos 是Spring Cloud Alibaba 的一个组件。致力于发现、配置和管理微服务。
Nacos名字:前四个字母分别为 Naming 和 Configuration 的前两个字母,最后的 s 为 Service。
下载启动
版本选择
如果是使用spring boot + spring cloud alibaba + spring cloud全家桶。那么版本选择一定要按照官网匹配,否则会因jar包冲突导致项目启动失败。
我们可以去spring cloud alibaba的版本说明查看
在此文中,我选择的版本为spring boot - 2.2.5.RELEASE, spring cloud alibaba - 2.2.1.RELEASE, spring cloud - Hoxton.SR3
下载
nacos下载地址 https://github.com/alibaba/nacos/releases
根据上面的版本说明,我这里选择1.2.1
启动
下载解压后,进入bin目录
可以看到启动命令。其中cmd是window系统使用,sh是linux系统使用。
我们本地测试使用windows环境,直接双击startup.cmd
即可。linux环境请执行./startup.sh -m standalone
(standalone代表单机启动,实际使用应做集群,集群数量三台起步)
查看控制台
nacos默认端口是8848,可不是某手机,8848代表珠穆朗玛峰的高度(但是现在高度好像是8844了)。
启动nacos后,访问localhost:8848/nacos/index.html,可以看到nacos后台。在这里可以对服务和配置做可视化管理(当然现在是空的,一会儿项目启动就有东西了)。
注册中心
先看看如何作为服务注册中心
服务注册
1 项目pom添加依赖
<!-- spring boot 项目 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 服务注册/发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- spring cloud alibaba 依赖管理 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2 application.properties添加配置
# 服务端口
server.port=8081
# 服务名
spring.application.name=service-provider
# 服务注册中心地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
3 启动类上添加服务发现注解
@EnableDiscoveryClient
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
PS:这里不加@EnableDiscoveryClient也行,Spring Cloud Dalston.SR4版本之后会自动向注册中心注册
4 启动查看
启动服务,刷新nacos后台就能看到这个服务了
服务调用
为了说明服务调用,我们先以同样的方式配置好另外一个服务
1 pom添加依赖
同上,略
2 application.properties添加配置
# 服务端口
server.port=9091
# 服务名
spring.application.name=service-consumer
# 服务注册中心地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
3 启动类
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
4 启动查看
可以看到,我们有两个微服务了
5 引入openfeign
openfeign是spring cloud的组件,为微服务之间的调用提供了解决方案。
openfeign的使用也非常简单,我们在service-consumer项目的基础上继续完善
1) pom添加依赖
<dependencies>
... //省略之前已添加
<!-- 服务调用 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
... //省略之前已添加
<!-- spring cloud 依赖管理 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2) 编写远程调用类
package com.test.consumer.feign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient("service-provider")
public interface ProviderFeignService {
@RequestMapping("provider/test")
public Result providerTest(@RequestBody TestEntity testEntity, @RequestParam String name);
}
映射说明: @FeignClient括号里是要调用的微服务名称,@RequestMapping括号里是被调用微服务的具体api路径。
传参说明: 实体类添加@RequestBody,字符串和普通类型添加@RequestParam。
这个service就和我们平时普通的service一样,在controller使用@Autowired依赖注入即可。
3) 启动类添加@EnableFeignClients
@EnableFeignClients(basePackages = "com.test.consumer.feign")
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
说明: @EnableFeignClients括号里是要实行远程调用服务所在的包路径,即上面远程调用类所在的包路径。
现在回到service-provider添加一个测试controller就可以调用成功了
@RestController
@RequestMapping("provider")
public class TestController {
@PostMapping("test")
public Result test(TestEntity testEntity, String name) {
Result result = new Result();
// do something, balabala
return result;
}
}
Result是自定义返回类,略。
原理简述
服务注册中心的实现,大概依赖三个重要的定时任务
- 客户端定时任务:定时发送心跳,告诉nacos我还活着
- 客户端定时任务:定时拉取可用服务,放在本地缓存。openfeign调用时自行选用缓存中服务
- 服务端定时任务,定时刷新可用列表,踢掉太久没发心跳的客户端【默认15秒未发心跳的会被踢出】
小结
以上,我们实现了服务注册与发现,服务间远程调用。
配置中心
问题说明
随着业务越来越复杂,我们会面临一些问题:
- 微服务项目越来越多,配置文件也越来越多,管理起更麻烦
- 改动一个配置参数时,需要重新打包微服务重新发布
- 有时候希望改动某个参数可以立即生效
接下来,我们使用nacos解决这些问题。
1 添加pom依赖
<dependencies>
... //省略之前已添加
<!-- 配置中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
</dependencies>
2 在application.properties设置运行环境
spring.profiles.active=dev
3 新建bootstrap.properties文件
# 服务名
spring.application.name=service-consumer
# 配置中心地址
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
4 添加注解 @Value + @RefreshScope
@RefreshScope // 自动感知配置文件变化
@RequestMapping("test")
@RestController
public class TestController {
// 从配置文件获取属性值
@Value("{message}")
private String message;
@RequestMapping("message")
public String message() {
return message;
}
}
5 去nacos配置中心添加配置文件
这里Data Id就是配置文件名
6 启动服务,验证结果
服务启动时会去配置中心找文件 ${prefix}-${spring.profile.active}.${file-extension}
prefix
默认为spring.application.name
,本例中即 service-consumer ;也可以通过配置spring.cloud.nacos.config.prefix
来指定spring.profile.active
就是当前环境对应的 profile,本例中即 dev ;如果spring.profile.active
不存在,这之前的连接符-
也将不存在,文件拼接格式变为${prefix}.${file-extension}
file-extension
就是文件后缀,默认为properties;可以通过配置spring.cloud.nacos.config.file-extension
来指定,但其值目前只支持properties
或yaml
根据上述说明,服务会去配置中心找的文件就是service-consumer-dev.properties,也即是第5步新增的配置文件。
浏览器访问localhost:9091/test/message
就可以看到返回结果为panda
,即我们配置文件中的值。
然后去配置中心修改配置文件内容:
再次访问localhost:9091/test/message
,可以看到返回结果变为panda2
如此,就解决了之前提到的三个问题:
- 配置文件和项目包分开,配置文件可统一管理
- 配置参数修改后,不需要重新打包项目,直接重启项目就能读到修改的值
- 对于一些需要立即生效的参数,使用@Value + @RefreshScope组合
7 小结
我们项目的启动脚本里一般会加上profile,如测试环境java -jar order.jar --spring.profile.active=test
,生产环境java -jar order.jar --spring.profile.active=prod
。那么,同一个jar包,我们完全不需要任何改动,发布到不同环境,它自己就能读取到不同环境的配置文件。
一"一个jar包,到处发布"。非常的方便。
namespace & group
我们已经把配置文件从项目中抽离出来了,然而这里仍有麻烦:如果我们有很多微服务,那么就对应有很多配置文件;这些文件还有各自不同环境的版本,也许还有不同业务场景下的版本。如果只是把这么多配置文件都单纯放在一起,那么维护起来也会很困难。
1 说明
为了解决配置文件太多不好管理的问题,我们可以使用nacos提供的命名空间(namesapce)和组(group)的概念。
namespace可以看成是一个独立的一级目录,namespace之间相互隔离;组可以看成namespace下面的二级目录,group之间同样隔离。
之前说,服务启动后会去配置中心找${prefix}-${spring.profile.active}.${file-extension}
,其实前面还得加上命名空间和组;即到底是去哪个命名空间下面的哪个组找配置文件。我们没有配置这两项,默认是去public下DEFAULT_GROUP找该文件。
而我们创建配置文件时并没有指定命名空间和组,默认就放在了public下的DEFAULT_GROUP组。
所以才能正常读取到配置文件。
2 解决方案
根据以上说明,我们可以使用微服务名来作为namespace。如一个商城项目,有订单微服务(order),商品微服务(product),库存微服务(stock)等,那我们就分别建立namespace为order, product, stock等。
然后,对于不同的业务场景,我们可以使用group来区分。比如双11对应的组,我们就叫1111;618对应的组,我们就叫618
我们去命名空间菜单,点击新建命名空间
依次添加三个命名空间后,回到配置列表,可以看到新建的命名空间
单击命名空间,可选中之。我们在其中新建配置文件,组也是在建立配置文件时指定的。
连续建立不同组不同环境的配置文件,最后应该看起来类似这样
然后其他微服务也如此,建立自己的不同组的不同环境的配置文件。
这里我们可以使用克隆功能,批量复制。
结果如下
如果微服务的配置差别不大的话,可以用这个方式批量复制,再去修改部分参数。
配置文件完成后,再去bootstrap.properties文件指定namespace和group
spring.cloud.nacos.config.group=1111
spring.cloud.nacos.config.namespace=12078634-abb1-4604-8678-45c11db8fe74
这里面的12078634-abb1-4604-8678-45c11db8fe74
就是order命名空间后面那一串id
3 小结
以上,我们分离了配置文件和项目。并且能根据微服务和业务分开管理。使得配置维护,项目发布都更加容易。
tip:这里其实有个问题,这个group切换,还是需要修改group后重新发布项目。所以,这里失去了之前说好的"一个jar包,到处发布"的特点。所以,实际使用中,如果没有特别的业务场景,就使用一个默认group。只指定namespace,那么依旧可以做到不重新打包项目。
ps: 如果有同学知道项目启动如何指定group,请指正,多谢。
自定义数据库
默认情况下,nacos是使用了一个嵌入式数据库。
如果想使用自己的数据库(目前只支持mysql数据库)
1 conf目录下,修改application.properties文件。
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root #使用的mysql用户名
db.password=xxx #使用的mysql连接密码
2 将conf/nacos-mysql.sql表结构导入到自己的数据库中
3 重新启动启动nacos,会发现之前的配置文件没有了。重新添加配置文件,在mysql表里看到自己的配置文件内容。
集群
为了保证nacos本身的高可用,可以用三台nacos组成集群。
集群首先要使用自定义数据库,不然三台集群的配置文件都不能保证一致,集群的基本意义就没了。
1 复制conf目录下的cluster.conf.example,重命名为cluster.conf,写入三台nacos的ip地址
192.168.0.101
192.168.0.102
192.168.0.103
如果在同一台机器上测试,可以设定不同端口
127.0.0.1:8848
127.0.0.1:8858
127.0.0.1:8868
2 复制两个nacos,放在不同的机器上(单机测试则去conf/application.properties修改端口)。启动三台nacos。
3 nginx代理配置
# 集群节点
upstream nacos {
server 192.168.0.101:8848 weight=1;
server 192.168.0.102:8848 weight=1;
server 192.168.0.103:8848 weight=1;
}
# 本地集群节点
#upstream nacos {
#server 127.0.0.1:8848 weight=1;
#server 127.0.0.1:8858 weight=1;
#server 127.0.0.1:8868 weight=1;
#}
server {
listen 80;
server_name localhost;
location /nacos/ {
#代理
proxy_pass http://nacos/nacos/;
}
}
4 访问
浏览器访问http://localhost/nacos/index.html
即可
tip: 保证了Nacos不会有单点故障后,其背后的Mysql最好也做一个主从备份或高可用。