SpringCloud-Alibaba学习(二):Nacos注册中心
1、服务注册发现
1.1 简述
微服务架构下,服务分别部署到不同机器上,服务之间通过网络完成数据通信。我们知道,网络种两个机器要想通信,必须得知道对方的 ip 和端口,因此服务调用的第一步,就是获取对方的 ip 和端口。
如何做呢?简单做法可以直接将 ip 和端口写死在调用方服务上。但是当服务重新部署 ip 和端口可能会发生变动,那么依赖方也需要修改重启。当服务很多的时候,服务间的依赖关系也无法通过硬编码来维护。
所以就引出了服务注册中心,通过注册中心来管理服务的 ip 和端口。
当服务提供方启动时,将自己的 ip 和端口发生给注册中心,注册中心将服务名与ip和端口保存起来,这个信息就叫做元数据信息,这个过程叫服务注册。
服务调用方启动时,通过服务名去注册中心获取 ip和端口信息,这个过程叫服务发现。
获取到服务提供方的地址信息后,就可以发起远程调用,如果有多个就需要进行负载均衡。
简单流程:
1.2 注册中心对比
注册中心是一个第三方的应用组件,有这么几种
- nacos:阿里开源
- eureka:netflix 公司的,闭源不维护了
- consul:HashiCorp 公司的开源产品
- zookeeper:apache 家的,一般用于 Dubbo 服务的注册发现
SpringCloud-Alibaba 使用 nacos 作为注册中心
2、Nacos 使用
2.1 简介
官网:
https://nacos.io/zh-cn/docs/quick-start.html
Nacos 两大核心功能:
-
Naming Service 名字服务
通过指定的名字来获取资源或服务的地址、服务提供者的信息。用于实现注册中心。 -
Config Service 配置服务
动态配置服务能够以中心化、外部化和动态化的方式管理所有环境的配置数据。动态配置能够实现配置变更时无需重新部署服务的需要。用于实现配置中心。
2.2 安装
下载地址:https://github.com/alibaba/nacos/releases
这里下载的是 1.4.1 版本
windows 版本安装
a)解压修改配置
打开目录下的 nacos\conf\application.properties
配置文件
b)创建数据库
创建名为nacos
的数据,可以在配置文件种修改
导入 nacos\conf\nacos-mysql.sql
sql 文件,创建表
c)配置 startup.cmd
以 standalone
方式启动
修改启动脚本 nacos\bin\startup.cmd
,默认集群模式启动,这里先在 windows 环境下运行,改成单机模式启动
也可以不修改脚本,使用命令 startup.cmd -m standalone
启动
d)双击 startup.cmd 启动
控制台默认访问路径 http://localhost:8848/nacos/index.html
(窗口最上面有)
登录默认用户名密码是:nacos/nacos
2.3 注册中心基本工作流程
注册中心是部署的 Nacos Server 服务,各个微服务通过客户端 Nacos Client 与 Nacos Server 通信
- 服务注册
Nacos Client 会通过发送REST请求,向 Nacos Server注册自己的服务,提供自身的元数据,包含服务名称、ip 和端口等信息。Server 接收到注册请求后,会把这些元数据信息存储到内存中。 - 服务心跳
服务注册后,Client 会维护一个定时心跳保持与 Server 的连接,说明处于可用状态,防止被剔除,默认 5s 发送一次心跳。 - 服务同步
Nacos Server 集群直接会互相同步服务实例,用于保证服务信息的一致性。 - 服务发现
服务消费方在调用提供者的服务时,会发送 REST 请求到注册中心,根据服务名查询服务列表,查询到的服务实例列表会缓存到消费方本地,下次再发送请求时直接从本地获取服务地址。也具有一定的容错性,即使注册中心挂了,只要服务方没有变动,依然可以正常发送请求。同时 Nacos client 会开启一个定时任务,定时拉取注册中心最新的服务信息并更新本地缓存。 - 服务健康检查
Server 会开启一个定时任务来检查注册服务的健康状态,对于超过 15s 没收到客户端心跳的实例,会将它的 healthy 属性置为 false(服务发现过程时该服务不会被发现);如果超过 30s 没有收到心跳,直接剔除该服务,服务恢复心跳则会重新注册。
简单流程
3、入门案例
3.1 版本选择
SpringBoot 版本查看:https://spring.io/projects/spring-boot#learn
SpringCloud 版本查看:https://spring.io/projects/spring-cloud#learn
版本对应 (springcloud-netflix 参考):https://start.spring.io/actuator/info
SpringCloud-alibaba 版本参考:https://github.com/alibaba/spring-cloud-alibaba/wiki/版本说明
版本选择:
项目 | 版本 |
---|---|
springcloud-alibaba | 2.2.5.RELEASE |
springcloud | Hoxton.SR8 |
springboot | 2.3.2.RELEASE |
3.2 搭建工程
3.2.1 创建父工程
创建一个普通 maven 工程 cloud-nacos-demo
作为父工程,打包为 pom
引入依赖
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring.boot.version>2.3.2.RELEASE</spring.boot.version>
<spring.cloud.version>Hoxton.SR8</spring.cloud.version>
<spring.cloud.alibaba.version>2.2.5.RELEASE</spring.cloud.alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- springboot依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- springcloud-alibaba依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--springcloud依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
3.2.2 公共模块
创建一个公共模块nacos-demo-common
,用于服务提供方暴露数据传输对象 DTO,服务消费方引入依赖使用。
该模块不作为服务,不需要引入Spring相关依赖。
依赖:
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
数据传输对象
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class GoodsDTO {
private String goodsName;
private Long price;
}
3.2.3 创建服务提供者
创建子工程nacos-demo-provider
引入依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 端点监控 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- nacos 注册中心场景依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>cc.yuanspace</groupId>
<artifactId>nacos-demo-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
基本应用配置:
# 服务端口
server.port=9001
server.servlet.context-path=/nacos-demo-provider
# 服务名称 必须有 保证唯一性
spring.application.name=nacos-demo-provider
# nacos server 的地址
spring.cloud.nacos.discovery.server-addr=localhost:8848
spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos
启动类
@SpringBootApplication
@EnableDiscoveryClient // 开启服务注册与发现功能
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
System.out.println("**** 启动成功 ****");
}
}
运行:
查看 nacos 控制台
服务 Controller 接口
@RestController
@RequestMapping("/goods")
public class GoodsController {
@GetMapping("/{id}")
public GoodsDTO findById(@PathVariable("id") Long id) {
System.out.println("goods id = " + id);
return GoodsDTO.builder().id(id).goodsName("小米").price(2998L * 100).build();
}
}
3.2.4 创建服务消费者
创建子工程nacos-demo-consumer
依赖:
<artifactId>nacos-demo-consumer</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 端点监控 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- nacos 注册中心场景依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>cc.yuanspace</groupId>
<artifactId>nacos-demo-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
基本应用配置:
注意修改端口、应用名,保持唯一
# 服务端口
server.port=9002
server.servlet.context-path=/nacos-demo-consumer
# 服务名称 必须有 保证唯一性
spring.application.name=nacos-demo-consumer
# nacos server 的地址
spring.cloud.nacos.discovery.server-addr=localhost:8848
spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos
启动类:
@SpringBootApplication
@EnableDiscoveryClient // 开启服务注册与发现功能
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
System.out.println("**** 启动成功 ****");
}
}
运行:
俩服务都有了
3.2.5 远程调用
不同于Dubbo,SpringCloud 采用 HTTP 协议发送请求,所以可以使用任何能发起 HTTP 请求的工具包实现远程调用,如 HttpClient、RestTemplate
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
@RestController
@RequestMapping("/test")
public class TestController {
private final RestTemplate restTemplate;
public TestController(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@GetMapping("")
public Map<String, Object> test() {
// 发起远程调用,(任何能发起http请求的都可以),使用RestTemplate,是spring对HttpClient的封装
String url = "http://localhost:9001/nacos-demo-provider/goods/1";
GoodsDTO goodsDTO = restTemplate.getForObject(url, GoodsDTO.class);
System.out.println(goodsDTO);
// 处理数据
System.out.println("处理数据完成");
return success(goodsDTO);
}
private Map<String, Object> success(Object data) {
Map<String, Object> result = new HashMap<>();
result.put("errcode", 0);
result.put("errmsg", "success");
result.put("data", data);
return result;
}
}
注意:
上面的请求 url 中服务的 ip 和端口还是硬编码,应用注册中心要使用服务名发起调用,但是服务名默认会被解析成 host 会出错,还需要使用 Ribbon 解析服务名做负载均衡。
nacos-discovery
已经引入了 Ribbon,无需再额外添加依赖,直接使用。
在注入 RestTemplate 的位置添加注解
@Bean
@LoadBalanced // 使Ribbon生效
public RestTemplate restTemplate() {
return new RestTemplate();
}
远程调用将 ip和端口 换成 服务名
@GetMapping("")
public Map<String, Object> test() {
// 发起远程调用,(任何能发起http请求的都可以),使用RestTemplate,是spring对HttpClient的封装
String serviceName = "nacos-demo-provider";
String url = String.format("http://%s/nacos-demo-provider/goods/1", serviceName);
GoodsDTO goodsDTO = restTemplate.getForObject(url, GoodsDTO.class);
System.out.println(goodsDTO);
// 处理数据
System.out.println("处理数据完成");
return success(goodsDTO);
}
3.2.6 服务集群调用
启动多个提供者服务,使用 idea 配置多个启动器
跑起来后,Nacos 控制台查看服务详情
发起调用后默认采用轮询的负载均衡策略调度
4、nacos 领域模型
nacos 的服务由三元组唯一确定(namespace、group、servicename)
nacos 的配置由三元组唯一确定(namespace、group、dataId)
不同的 namespace 是相互隔离的,相同的 namespace 但是不同的 group 也是相互隔离的
默认的 namespace 为 public,不能删除
默认的 group 是 DEFAULT_GROUP
4.1 创建 namespace
4.2 发布服务到指定 namespace
给提供者服务的配置文件中添加配置,注意配置上填的是 namespace 的 id。
# 服务发布到指定的namespace,不指定默认就是public
spring.cloud.nacos.discovery.namespace=qa
# 服务发布到指定的group,默认值是DEFAULT_GROUP
spring.cloud.nacos.discovery.group=my-group
给消费者服务添加配置,这里指定其他 namespace
# 服务发布到指定的namespace,不指定默认就是public
spring.cloud.nacos.discovery.namespace=pro
# 服务发布到指定的group,默认值是DEFAULT_GROUP
spring.cloud.nacos.discovery.group=my-group
服务发布到了不同的 namespace 中
调用服务报错,服务没有可用实例
java.lang.IllegalStateException: No instances available for nacos-demo-provider
官方示例
https://github.com/alibaba/spring-cloud-alibaba/tree/2.2.x/spring-cloud-alibaba-examples