为什么需要服务治理
在未引入服务治理模块之前,服务之间的通信是服务间直接发起并调用来实现的。只要知道了对应服务的服务名称、IP地址、端口号,就能够发起服务通信。比如A服务的IP地址为192.168.1.100:9000,B服务直接向该IP地址发起请求就可以获取对应的数据。但是,随着集群架构的搭建,服务的增多,集群架构的搭建,手动配置各个服务的实例清单就会变得越来越复杂,当集群规模发生改变、服务实例发生改变(数量或者ip等)改变,维护这种硬编码的配置内容需要消耗太多的开发资源。如果使用nginx等中间件,确实也可以实现,但是也会出现问题,比如两个服务集群之间无法直接通信,进而导致调用成本的增加,因为增加了一次网络消耗。还有当集群规模发生改变时,维护起来也是十分麻烦。
服务治理包含很多方面,在这主要讲解服务治理中的三个核心问题:服务注册、服务发现和服务的健康检查机制。
注册中心原理,见下图:
- 服务启动时会将自己的服务信息(服务名、IP、端口)注册到注册中心, 并向注册中心发送心跳信息来报告自己的健康状态。
- 注册中心会存储服务提供者上报的信息,并通过服务提供者发送的心跳来更新服务提供者最后的存活时间,如果超过一段时间没有收到服务提供者上报的心跳信息,则注册中心会认为服务提供者不可用,会将对应的服务提供者从服务列表中剔除。
- 服务订阅者会向注册中心订阅自身监听的服务,注册中心会保存服务订阅者的信息,也会向服务订阅者推送服务提供者的信息。
- 服务订阅者从注册中心获取到服务提供者的信息时,会直接调用服务提供者的接口来实现远程调用。
服务的注册与发现
服务注册
每个服务实例在启动时都将自己的服务名称、服务IP地址等信息提交到注册中心,注册中心将这些信息维护到实例清单中,这个过程就是服务注册。
有了注册中心之后,当集群规模发生改变、服务实例数量的增加和减少、服务名称发生改变、服务实例部署的IP地址或端口号发生改变,这些情况发生时都会通知注册中心,注册中心会自动修改实例清单中的内容。
服务发现
服务订阅者会从服务的注册中心获取服务提供者的服务列表,或者由服务的注册中心将服务提供者的服务列表变动信息推送给服务订阅者,这个过程叫作服务发现。
比如,订阅者可以在请求一次后就将可用的服务数据缓存到本地,直到注册中心主动将被调用方的服务实例清单变更信息推送过来,再进行本地缓存数据的变更。
服务的健康检查机制
所有的服务实例在注册中心注册成功后,每个服务实例都需要定时发送请求,告诉注册中心自己的健康状态。如果服务实例能够持续发送"心跳"信息,则表示一切正常,服务会被标记为可用的、可发现的。如果注册中心在一段时间内没有收到某个服务实例的"心跳"信息,就会将这个服务实例标记为不可用或不可达的状态,进而从可用的服务列表中剔除该服务实例的信息,在订阅者查询可用的服务实例清单时,该服务实例的信息不会返回给订阅者。
什么是Nacos
Nacos是 Dynamic Naming and Configuration Service的首字母简称,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
Nacos 致力于发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,能快速实现动态服务发现、服务配置、服务元数据及流量管理。
Nacos的核心特性:
- 服务发现和服务健康监测
- 动态配置服务
- 动态 DNS 服务
- 服务及其元数据管理
Nacos环境搭建(单机)
以 MacOS 为例,搭建单机测试环境。
先安装好 JDK,需要 JDK 1.8+ 及其以上版本。
Nacos安装包下载
https://github.com/alibaba/nacos/releases/download/2.2.3/nacos-server-2.2.3.tar.gz
解压安装包
vbscript
tar -xzvf nacos-server-2.2.3.tar.gz
如果是window系统直接解压即可。
单机模式启动
bash
sh ./startup.sh -m standalone
停止Nacos
arduino
sh ./nacos/bin/shutdown.sh
验证是否成功
在浏览器中请求http://ip:8848/nacos,如果可以进入控制台,说明启动成功。
集成Nacos注册中心
创建父项目
- 打开开发工具(idea)
- 点击create new project
- 选择maven
- 选择好jdk,点击下一步
- 输入名称(比如:aa-springcloud)和项目地址(~/xxx/develop)
- pom引入如下:
xml
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>c-springcloud</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.2</version>
</parent>
<dependencies>
<!-- lombok 工具通过在代码编译时期动态的将注解替换为具体的代码,
IDEA 需要添加 lombok 插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.11</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
</plugins>
</build>
<!-- 项目依赖管理 父项目只是声明依赖,子项目需要写明需要的依赖(可以省略版本信息) -->
<dependencyManagement>
<dependencies>
<!-- spring cloud 依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2022.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- spring cloud alibaba 依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2022.0.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
依赖说明
spring-boot-dependencies:Spring Boot 基础依赖,包括各种 Java 后端框架技术栈、组件、插件等等。
spring-cloud-dependencies:Spring Cloud 相关依赖,比如:Netflix、openfeign、Gateway 等等。
spring-cloud-alibaba-dependencies:Spring Cloud Alibaba 相关依赖,比如:Nacos、Sentinel、RocketMQ 等等。
其他技术、插件依赖等都在父项目中维护版本号,其他子项目继承即可,不需要重复维护版本号。
Spring Cloud Alibaba & Spring Cloud & Spring Boot 之间的依赖关系
Spring Cloud Alibaba | Spring Cloud | Spring Boot |
---|---|---|
2022.0.0.0 | Spring Cloud 2022.0.0 | 3.0.2 |
2021.0.4.0 | Spring Cloud 2021.0.1 | 2.6.11 |
2021.0.1.0 | Spring Cloud 2021.0.1 | 2.6.3 |
2.2.7.RELEASE | Spring Cloud Hoxton.SR12 | 2.3.12.RELEASE |
2021.1 | Spring Cloud 2020.0.1 | 2.4.2 |
2.2.6.RELEASE | Spring Cloud Hoxton.SR9 | 2.3.2.RELEASE |
2.1.4.RELEASE | Spring Cloud Greenwich.SR6 | 2.1.13.RELEASE |
2.2.1.RELEASE | Spring Cloud Hoxton.SR3 | 2.2.5.RELEASE |
2.2.0.RELEASE | Spring Cloud Hoxton.RELEASE | 2.2.X.RELEASE |
2.1.2.RELEASE | Spring Cloud Greenwich | 2.1.X.RELEASE |
spring-boot-maven-plugin(Spring Boot 打包插件,在 Spring Boot 父项目中定义了,只需要 Spring Boot 微服务自行引入)
maven-compiler-plugin(Maven 编译插件,所有项目继承)
用 pluginManagement 和 plugins 分开管理的区别是,pluginManagement 里面的插件是需要项目自行引入的,而 plugins 中的插件是自动继承的
创建子模块
- 在父项目名称上右键,依次点击 -> new -> Module...,进入子模块创建界面
- 填写模块名称和存储地址,选择jdk等
- 子模块pom引入如下:
xml
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example.cloud</groupId>
<artifactId>c-springcloud</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>c-service-nacos-client</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- nacos -->
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- spring boot web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
</project>
- 创建主入口
在src下创建项目路径比如:com.example.cloud,在此路径下新增一个类,起名为NacosClientApplication。
java
@EnableDiscoveryClient
@SpringBootApplication
public class NacosClientApplication {
public static void main(String[] args) {
SpringApplication.run(NacosClientApplication.class, args);
}
}
- 添加配置文件 在resource文件夹下创建一个名称为application.yml的配置文件,文件内容如下
json
server:
port: 8000
servlet:
context-path: /c-service-nacos-client
spring:
application:
name: c-service-nacos-client # 应用名称也是构成 Nacos 配置管理 dataId 字段的一部分 (当 config.prefix 为空时)
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
enabled: true
namespace: c3bee016-66ca-4a73-a1f8-af0390e5ce08
- 启动项目,验证是否注册成功 在主启动类中,右键->点击run NacosClientApplication启动项目,打开nacos控制台,在服务管理->服务列表中如果出现了启动的服务,说明注册成功了。
- 服务发现代码 在开发路径下右键点击 -> New -> Package,新建一个名为service的文件夹,在文件夹下创建一个名为NacosClientService的类
java
@Service
@Slf4j
public class NacosClientService {
private final DiscoveryClient discoveryClient;
public NacosClientService(DiscoveryClient discoveryClient) {
this.discoveryClient = discoveryClient;
}
/**
* 打印Nacos Client中的信息
* @return
*/
public List<ServiceInstance> getNacosClientInfo(String serviceId) {
log.info("请求Nacos Client获取服务实例的信息:[{}]", serviceId);
return discoveryClient.getInstances(serviceId);
}
}
在开发路径下右键点击 -> New -> Package,新建一个名为controller的文件夹,在文件夹下创建一个名为NacosClientController的类
java
@Slf4j
@RestController
@RequestMapping("/nacos-client")
public class NacosClientController {
private final NacosClientService nacosClientService;
public NacosClientController(NacosClientService nacosClientService) {
this.nacosClientService = nacosClientService;
}
@GetMapping("service-instance")
public List<ServiceInstance> getNacosClientInfo(
@RequestParam(defaultValue = "c-service-nacos-client") String serviceId) {
log.info("打印nacos client信息:[{}]", serviceId);
return nacosClientService.getNacosClientInfo(serviceId);
}
}
右键resouce -> New -> Directory,创建一个名为http的路径,然后右键点击http文件夹 -> New -> File,创建一个名为Nacos-client.http的文件
java
### 查询服务实例信息
GET http://127.0.0.1:8000/c-service-nacos-client/nacos-client/service-instance
Accept: application/json
启动NacosClientApplication项目,进入nacos控制台查看是否注册成功,然后进入nacos-client.http,点击左边的绿色三角请求信息,如果返回如下信息,说明服务实例可以被发现。
json
[
{
"serviceId": "c-service-nacos-client",
"instanceId": null,
"host": "192.168.1.235",
"port": 8000,
"secure": false,
"metadata": {
"nacos.instanceId": null,
"nacos.weight": "1.0",
"nacos.cluster": "DEFAULT",
"nacos.ephemeral": "true",
"nacos.healthy": "true",
"preserved.register.source": "SPRING_CLOUD"
},
"uri": "http://192.168.1.235:8000",
"scheme": null
}
]