1. Nacos注册中心简介
1.1 背景分析
- 在微服务中,首先需要面对的问题就是如何查找服务(软件即服务),其次,就是如何在不同的服务之间进行通信。如何更好更方便的管理应用中的每一个服务,如何建立各个服务之间联系的纽带,由此注册中心诞生(例如淘宝网卖家提供服务,买家调用服务)。
- 市面上常用的注册中心有
Zookeeper
(雅虎Apache)、Eureka
(Netfix)、Nacos
(Alibaba)、Consul
(Google)。 - 下面我们选择Nacos进行学习,Nacos它很好的支持了阿里的双11活动,不仅可以做注册中心,还可以作为配置中心,稳定性和性能都很好。
1.2 Nacos概述
- Nacos (DynamicNaming and Configuration Service)是一个应用于服务注册与发现、配置管理的平台。它孵化与阿里巴巴,成长于十年双十一的洪峰考研,沉淀了简单易用,稳定可靠,性能卓越的核心竞争力。这里是它的官网地址。
- 一个更易构建云原生应用的服务发现(
Nacos Discovery
)、服务配置(Nacos Config
)和服务管理的平台; - Nacos的关键特性:
- 服务发现和服务健康检测;
- 动态配置服务;
- 动态DNS服务;
- 服务及其元数据管理;
1.3 Nacos注册中心
- 管理所有微服务,解决微服务之间调用关系的错综复杂、难以维护的问题。
1.4 核心功能
- 服务注册:Nacos Client会通过发送REST请求的方式向Nacos Server注册自己的服务,提供自身的元数据,例如:IP地址、端口等信息。Nacos Server接收到注册请求后,就会把这些元数据信息存储在一个双层的内存Map中;
- 服务心跳:在服务注册后,Nacos Client会维护一个定时心跳来持续通知Nacos Server,说明服务一直处于可用状态,防止被剔除。默认5秒发送一次心跳。
- 服务同步:Nacos Server集群之间会相互同步服务实例,用来保证服务信息的一致性;
- 服务健康检查:Nacos Server会开启一个定时任务来检查注册服务实例的健康情况。
- 如果某个服务实例超过15秒没有收到心跳,则会将它的health属性置为false(客户端服务发现时就不会发现该服务);
- 如果某个服务实例超过30秒没有收到心跳,直接剔除该服务实例(被剔除的实例如果恢复发送心跳则会重新注册);
1.5 主流的注册中心
- C:一致性、A:可用性、P:分区容错性
2. 构建Nacos服务
2.1 下载与安装
第一步:Nacos下载,可在浏览器直接输入如下地址:
https://github.com/alibaba/nacos/releases
单机模式官方文档
第二步:选择对应的版本,直接下载,如图所示:
第三步:解压Nacos(不要解压到中文目录下),其目录结构如下:
2.2 初始化配置
第一步:找到conf/nacos-mysql.sql文件里的sql脚本,然后登录mysql,然后基于脚本文件中的描述,创建数据库(名字nacos_config,编码方式utf8mb4),并执行脚本文件,脚本执行成功之后会创建一些表,如图所示。
说明:在执行此文件时,要求mysql的版本大于5.7版本以上(MariaDB最好是10.5.11),否则会出现如下错误。
第二步:打开conf/application.properties的这个文件,并基于当前环境配置要连接的数据库。(假如前面有#,要将其去掉)
### If use MySQL as datasource:
spring.datasource.platform=mysql
### Count of DB:
db.num=1
### Connect URL of DB:
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&serverTimezone=UTC&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user.0=root
db.password.0=root
2.3 服务启动与访问
第一步:启动Nacos服务。
Windows启动命令(standalone代表着单机模式运行,非集群模式):
startup.cmd -m standalone
说明:
1)执行命令时,要么配置环境变量,要么直接在nacos/bin目录下通过cmd命令窗口执行。
2)nacos启动时需要本地环境变量中配置JAVA_HOME(对应jdk的安装目录)。
第二步:访问Nacos服务。
打开浏览器,输入http://localhost:8848/nacos地址,出现如下登录页面:
其中,默认账号:nacos,默认密码:nacos。
3. 服务注册与调用入门
3.1 业务描述
创建两个项目Module分别为服务提供者和服务消费者,两者都要注册到NacosServer中(这个本质上就是一个web服务,默认端口为8848),然后服务提供者可以为服务消费者提供远端调用服务(例如,支付服务是服务提供方,订单服务为服务消费方),如图所示。
3.2 创建项目父工程
- 第一步:创建父工程 springcloud-demo,此工程为我们后续项目模块的父工程,这个工程的作用就是来管理子模块中所需要的共性依赖。
- 第二步:导入需要的核心依赖,其pom文件的内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
</parent>
<groupId>com.example</groupId>
<artifactId>springcloud-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springcloud-demo</name>
<description>父工程</description>
<packaging>pom</packaging>
<!--声明集成的子模块-->
<modules>
<module>StockService</module>
<module>OrderService</module>
</modules>
<!--全局版本管理-->
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring.cloud-version>Hoxton.SR8</spring.cloud-version>
<spring.cloud.alibaba-version>2.2.5.RELEASE</spring.cloud.alibaba-version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!--依赖管理-->
<dependencyManagement>
<dependencies>
<!--spring cloud依赖,此依赖需要springboot的支持,依赖中定义了spring cloud微服务规范-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud-version}</version>
<!--import表示此工程下的子工程可以直接引用这个依赖中的版本-->
<scope>import</scope>
<!--当scope为import时,这里的类型必须为pom类型-->
<type>pom</type>
</dependency>
<!--spring cloud alibaba依赖,此依赖基于spring cloud,当前依赖中使基于微服务规范做了具体的实现-->
<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>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- 其中的版本可以参考如下网址(涉及到一个兼容性问题,不能随意指定其版本)。
https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E
- 最后,父工程资源初始化完成之后,将src目录删除,因为父工程只负责依赖管理。
3.3 服务提供者创建及注册
- 第一步:创建服务提供者继承父工程,其module名为StockService。
- 第二步:添加项目依赖。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>springcloud-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath/>
</parent>
<artifactId>StockService</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>StockService</name>
<description>StockService</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Nacos-服务注册与发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
</project>
- 第三步:创建并修改配置文件application.yml,实现服务注册。
server:
port: 8082
servlet:
context-path: /stock
spring:
application:
name: StockService #服务名称
cloud:
nacos:
server-addr: 127.0.0.1:8848
discovery:
username: nacos
password: nacos
- 第四步:定义处理请求的控制层方法
@RestController
@RequestMapping("/stockapi")
public class StockController {
@Value("${server.port}")
private String port;
@RequestMapping("/handle")
public String getStock() {
System.out.println("删减库存");
return "端口:"+port+"-->库存减一";
}
}
3.4 服务消费者的发现及调用
- 第一步:创建服务消费者继承父工程,module名为OrderService。
- 第二步:添加项目依赖。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>springcloud-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath/>
</parent>
<artifactId>OrderService</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>OrderService</name>
<description>OrderService</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Nacos-服务注册与发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
</project>
- 第三步:修改配置文件application.yml。
server:
port: 8081
servlet:
context-path: /order
spring:
application:
name: OrderService
cloud:
loadbalancer: #开启ribbon的负载均衡
ribbon:
enabled: true
nacos:
server-addr: 127.0.0.1:8848
discovery:
username: nacos
password: naocs
- 第四步:创建启动类并实现服务消费。
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
//构建RestTemplate对象,基于此对象访问远程服务
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder){
builder.setConnectTimeout(Duration.ofSeconds(100));
builder.setReadTimeout(Duration.ofSeconds(100));
return builder.build();
}
//构建负载均衡的RestTemplate对象
@Bean
@LoadBalanced
public RestTemplate loadBalancedRestTemplate(){
return new RestTemplate();
}
}
@RestController
@RequestMapping("/consumer")
public class ConsumerController{
@Autowired
private RestTemplate restTemplate;
//具有负载均衡功能的RestTemplate对象
@Autowired
private RestTemplate loadBalancedRestTemplate;
/**
负载均衡客户端对象:
基于此对象可以从注册中心获取服务实例
*/
@Autowired
private LoadBalancerClient loadBalancerClient;
@GetMapping("/doRestEcho1")
public String doRestEcho1(){
/**
* 说明:
基于RestTemplate进行远程服务调用,但是不具备负载均衡特性,这种方式不需要nacos,
是直接通过固定的ip和端口进行服务调用的,此方式不适合多服务实例调用。
*/
String url ="http://localhost:8082/stock/stockapi/handle";
//远程调用
//String.class是调用服务响应数据类型
return restTemplate.getForObject(url, String.class);
}
@GetMapping("/doRestEcho2")
public String doRestEcho2(){
/**
* 说明:
基于负载均衡的RestTemplate进行远程服务调用,但是在调用之前基于loadBalancedClient对象去从nacos注册中心基于服务名查找
服务实例(可能有多个),此时会在本地按照一定算法去选择服务实例,然后进行服务调用。
*/
//基于服务名(nacos中服务表中的名字)查找服务实例
ServiceInstance choose = loadBalancerClient.choose("StockService");
String ip = choose.getHost();//获取ip地址
int port = choose.getPort();//获取端口号
//构建远程调用的url
String url = "http://"+ip+":"+port+"/stock/stockapi/handle";
//远程调用-RPC
return loadBalancedRestTemplate.getForObject(url, String.class);
}
@GetMapping("/doRestEcho3")
public String doRestEcho3(){
//可以直接使用提供服务的服务名称来直接请求
String url = "http://StockService/stock/stockapi/handle";
return loadBalancedRestTemplate.getForObject(url , String.class);
}
}
}