一、什么是服务治理and服务治理技术选型
目录
- 什么是服务治理and服务治理技术选型
什么是服务治理
服务治理技术选型 - 深入了解Eureka
Eureka功能概述
搭建服务注册中心
了解注册中心UI界面
服务注册流程解读
创建服务提供者
创建服务消费者 - 心跳检测和服务续约
心跳监测
服务剔除
服务续约
服务自保
服务下线
(一)、什么是服务治理
1.概述
服务治理主脑一样,是整个微服务架构的第一关,是所有微服务应用都要思考的第一个问题。下面这我从服务治理能做什么(目标)和服务治理是怎么完成这些目标的,从这两个方面来给大家介绍一下什么是服务治理。
2.服务治理要实现什么目标
在整个分布式系统中,服务治理要实现4个目标
第一,实现要高可用
第二,要实现分布式调用
第三,实现生命周期管理
第四,健康度检查
高可用:即在服务治理麾下的所有节点,不论是因为服务器自身原因还是外部原因,即使只有一台节点存活,服务治理框架也要保证服务的可用性
分布式调用:在众多微服务节点中,必须保证准确的向对应的服务节点发起请求
生命周期管理:微服务从上线、运行、下线,整个生命周期得到管理
健康检查:对节点进行监听,准确判断服务是否需剔除。
3.服务治理实现上诉目标的手段–服务治理的解决方案
服务注册:服务提供方自报家门
服务发现:服务消费者从注册中心获取服务信息
心跳检查、服务续约和服务剔除
服务下线
(二)服务治理技术选型
1.分布式系统CPA定理
一致性:强一致性、弱一致性、最终一致性
C:一致性
A:可用性
P:分区容错性
CPA定理:分布式系统一般都只能3选2,不能同时满足
2.组件选择
实现服务治理的组件有三大选项
Eureka:出道最早,来自Netflix公司
弱一致性AP组合,性能较快,支持HTTP协议,目前市面主流
Consul:出道较晚,挂牌于SpringCloud
弱一致性AP组合,性能较慢,支持HPPT协议和DNS,市面使用较少
Nacos:晚辈,源自于Alibaba公司
支持AP/CP两种组合,性能较快,支持多种协议,包括HTTP,DNS,DUP,由于出道时间比较短暂,市面使用不是非常广泛
二、深入了解Eureka
(一)、Eureka功能概述
1.具体功能
服务注册、服务发现、心跳和续约、服务下线、剔除和自保
2.注册中心简述
注册中心是服务治理的第一步,也是整个服务治理最重要的部分
一般情况下注册中心有两种解决方案:
第一种:一一询问式,由注册中心注册中心主动访问网络节点中的所有机器
第二种:守株待兔式,注册中心等待各个服务节点前来注册
目前市面上普遍使用第二种模式,第一种模式,网络开销大,注册中心服务端压力增大,不仅需要响应客户端请求,还有寻找服务节点,压力增加;最主要的是网络环境复杂,甚至涉及到跨局域网的情况,这更增加了寻找服务节点的难度,所以普遍使用第二种模式,守株待兔。
第一种模式的优点:减少网络开心,减轻注册中心的压力,节省了主动寻找服务节点的时间
注册中心的日常任务:第一,记录每个服务节点的IP+端口;第二,记录没有服务节点提供什么服务;第三,记录服务节点的状态;第四,心跳检测和服务剔除;第五,同步注册信息(多注册中心的情况)
当然了,尽管呢注册中心是守住待兔的形式等待服务节点上门报道,所以每个服务节点上线前,就必须明确它应当去哪里报道,这必须由开发人员告诉它。
总结:
Eureka注册模式:守株待兔——等待服务节点上门报道;
服务节点注册信息主要包含地址(IP+端口)、服务、状态
注册中心功能:第一,记录每个服务节点的IP+端口;第二,记录没有服务节点提供什么服务;第三,记录服务节点的状态;第四,心跳检测和服务剔除;第五,同步注册信息(多注册中心的情况)
(二)、搭建服务注册中心
1.创建Demo顶层pom和子项目eureka-server
第一步:创建maven项目,目录结构如下:
第二步,spring-cloud-demo2项目pom文件引入依赖(最外层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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lys</groupId>
<artifactId>spring-cloud-demo2</artifactId>
<packaging>pom</packaging>
<version>1.0.0-SNAPSHOT</version>
<modules>
<module>eureka/eureka-server</module>
</modules>
<!--dependencyManagement 和 dependencies的区别:
dependencyManagement并不是真实的引入一个dependency,而是把依赖包的version管理起来,
这样的话可以不用指定依赖的version,它会从直接从父类项目或者从当前项目pom文件当中的
dependencyManagement里面读取version
-->
<dependencyManagement>
<dependencies>
<!--spring cloud 依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring boot 依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.子项目eureka-server 的pom文件引入eureka依赖
<?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>
<artifactId>spring-cloud-demo2</artifactId>
<groupId>com.lys</groupId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>eureka-server</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
</project>
3.设置启动类
**第一步:**配置文件
application.properties
spring.application.name=eureka-server
server.port=20000
eureka.instance.hostname=localhost
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
第二步:
文件名:EurekaServerApplication
package com.lys.springcloud;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* Created with IntelliJ IDEA.
* User: Administrator
* Date: 2021/2/2
* Time: 23:05
* Description: No Description
*/
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(EurekaServerApplication.class)
.web(WebApplicationType.SERVLET)
.run(args);
}
}
4.启动测试
访问:http://localhost:20000/
(三)、了解注册中心UI界面
1.System Status
Environment:环境,使用默认即可
Data center:默认即可
Current time:当前系统的时间
Uptime:注册中心从启动到至今运行的时间
Lease expiration enabled:是否启用租约过期,默认true
Renews threshold:每分钟的租约数,即每分钟至少发多少次请求到注册中心
Renews (last min):最后一分钟的续约数量
2.DS Replicas(注册中心副本)
3.Instances currently registered with Eureka
指在注册中心注册成功的应用
4.General Info
一般信息
total-avail-memory:总共可用内存
environment:环境
num-of-cpus:cpu个数,4核,8核这种
current-memory-usage:当前已用内存
server-uptime:注册中心从启动到至今运行的时间
registered-replicas:注册中心集群副本节点信息
unavailable-replicas:注册中心集群不可用的副本节点信息
available-replicas:可用副本5.Instance Info
注册中心本身
6.查看过去现在的租约信息
(四)、服务注册流程解读
1.注册起始流程
注册准备,起始流程如下:
网图,侵删
**注册起始流程解读:**由启动类中的@EnableDiscoveryClient注解开启自动注册流程 -> 到DiscoveryCLient类的register方法发起注册 -> SessionedEurekaHttpClient(父类EurekaHttpClientDecorator)进行装饰+代理 -> 通过代理给注册器加上装饰器
2.正式注册流程
网图,侵删(五)、创建服务提供者
1.创建eureka-client子项目
2.添加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>
<artifactId>spring-cloud-demo2</artifactId>
<groupId>com.lys</groupId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>eureka-client</artifactId>
<name>eureka-client</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
3.创建启动类和服务内容
第一步: 配置文件
application.properties
spring.application.name=eureka-client
server.port=30000
#指定使用IP注册
eureka.instance.hostname=192.168.1.5
eureka.instance.ip-address=192.168.1.5
#指定instance-id(ip + 应用名 + port)
eureka.instance.instance-id=${eureka.instance.ip-address}:${spring.application.name}:${server.port}
# 注册时使用ip而不是主机名
eureka.instance.prefer-ip-address=true
eureka.client.service-url.defaultZone=http://localhost:20000/eureka/
#每隔5秒钟,向服务中心发送一条续约指令
eureka.instance.lease-renewal-interval-in-seconds=5
#如果服务中心30秒没有收到续约请求,则判定服务过期
eureka.instance.lease-expiration-duration-in-seconds=30
第二步: 创建启动类
package com.lys.springcloud;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* Created with IntelliJ IDEA.
* User: Administrator
* Date: 2021/2/3
* Time: 22:44
* Description: No Description
*/
@SpringBootApplication
@EnableDiscoveryClient
public class EurekaClientApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(EurekaClientApplication.class)
.web(WebApplicationType.SERVLET)
.run(args);
}
}
第三步: 创建服务内容
package com.lys.springcloud.controller;
import com.lys.springcloud.model.Test;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
/**
* Created with IntelliJ IDEA.
* User: Administrator
* Date: 2021/2/3
* Time: 22:51
* Description: No Description
*/
@RestController
@Slf4j
public class TestController {
@Value("${server.port}")
private String port;
@GetMapping("/hello")
public String hello() {
return "hello" + port;
}
@PostMapping("/test")
public Test test(@RequestBody Test test) {
log.info("访问者:{}", test);
test.setName("test");
return test;
}
}
4.检查注册中心UI变化
(六)、创建服务消费者
1.创建eureka-consumer子项目
2.添加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>
<artifactId>spring-cloud-demo2</artifactId>
<groupId>com.lys</groupId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>eureka-consumer</artifactId>
<name>eureka-consumer</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<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>
</dependencies>
</project>
3.创建启动类和controller
第一步: 配置文件
application.properties
spring.application.name=eureka-consumer
server.port=40000
eureka.client.service-url.defaultZone=http://localhost:20000/eureka/
第二步: 创建启动类
package com.lys.springcloud;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
/**
* Created with IntelliJ IDEA.
* User: Administrator
* Date: 2021/2/4
* Time: 22:44
* Description: No Description
*/
@SpringBootApplication
@EnableDiscoveryClient
public class EurekaConsumerApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(EurekaConsumerApplication.class)
.web(WebApplicationType.SERVLET)
.run(args);
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
第三步: 创建controller
package com.lys.springcloud.controller;
import com.lys.springcloud.model.Test;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* Created with IntelliJ IDEA.
* User: Administrator
* Date: 2021/2/4
* Time: 22:51
* Description: No Description
*/
@RestController
@Slf4j
public class ConsumerTestController {
//简易的负载均衡器
@Autowired
private LoadBalancerClient loadBalancerClient;
@Autowired
private RestTemplate restTemplate;
@GetMapping("/hello")
public String hello() {
ServiceInstance serviceInstance = loadBalancerClient.choose("eureka-client");
if (serviceInstance == null) {
return "no available instance";
}
String target = String.format("http://%s:%s/hello", serviceInstance.getHost(), serviceInstance.getPort());
log.info("访问目标:{}", target);
String result = restTemplate.getForObject(target, String.class);
return result;
}
@PostMapping("/hello")
public Test helloPost() {
ServiceInstance serviceInstance = loadBalancerClient.choose("eureka-client");
if (serviceInstance == null) {
return null;
}
String target = String.format("http://%s:%s/hello", serviceInstance.getHost(), serviceInstance.getPort());
log.info("访问目标:{}", target);
Test test = new Test();
test.setName("Eureka Consumer");
Test result = restTemplate.postForObject(target, test, Test.class);
return result;
}
}
4.向eureka-client发起调用
三、心跳检测和服务续约
(一)、心跳监测
1.概念
心跳监测,即感知微服务集群节点健康状况的一种手段,避免服务节点宕机后不断出现404的状况。
2.心跳监测的特点:
(1)由客户端节点主动发起请求;
(2)同步当前节点状态;
(3)服务剔除,当客户端在指定的时间范围内,没有向注册中心发起请求时,注册中心就会主动将服务从注册列表剔除掉;
(4)服务续约,服务续约也是依赖心跳来实现的。
3.心跳检查
(1)访问地址:客户端需要知道注册中心的地址
(2)访问来宾:客户端在进行心跳请求时,需要告知注册中心一些自身的信息,包括注册时提供的服务名(app_name)、服务节点编号(instance_id)
(3)服务状态:服务自身的状态,包括UP、DOWN、STARTING、OUT_OF_SERVICE、UNKNOW;
(4)最后一次注册同步的时间:最后一次同步时间lastDirtyTimeStamp,表示当前服务节点最后一次与注册中心失去同步时的时间,它一般与isInstanceInfoDirty联用,isInstanceInfoDirty=true时,表示当前节点自从lastDirtyTimeStamp时间后都处于未同步状态。
客户端指标
(1)、发送心跳包的时间间隔
(2)、指明注册中心,如果多少时间内未收到我的请求,就认为我宕机了。
要求:第一个时间必须小于第二个时间
(二)、服务剔除
1.作用
为了让无心跳响应的服务节点自动下线。
2.剔除流程
启动定时任务:注册中心启动后会在后台默认同步开启一个后台定时任务,默认每间隔60秒触发剔除任务;
调用evict:服务剔除直接通过eviction方法进行;
自保开启:自保开启后,注册中心会中断服务剔除操作;
遍历过期服务:剔除服务会遍历所有注册了的服务节点,一旦满足一下条件,就会被认为是可剔除的服务—>1.已被标记为过期,2.最后一次心跳时间+服务端配置的心跳间隔时间<当前时间;
计算可剔除服务的总个数:为了保证整个集群的稳定性,注册中心是不可能把服务全部剔除完的,最多可剔除个数不能大于 总个数*0.85(系统默认);
乱序剔除:服务剔除是不安顺序的,是随机的。(三)、服务续约
1.作用
告知注册中心,当前服务节点还是可用的,还能继续工作。
2.续约步骤
第一步:
借助心跳请求将服务节点的状态同步到注册中心,通知注册中心我还能继续工作;
第二步:
注册中心收到请求后,判断服务节点状态,然后修改当前服务节点在注册中记录的同步时间
第三步:
服务节点发送续约请求
服务续约请求:由客户端的DiscoverClient类中的renew方法开始
发送心跳:服务续约借助发送心跳来完成,成功返回200,失败返回404
重新注册:1.设置lastDirtyTimeStamp,2.标记自身为脏节点
注册成功后:1.清除脏节点标记,2.lastDirtyTimeStamp保持不变,后续作为参数发送注册中心,便于注册中心判断服务节点状态。
第四步:
注册中心续约校验
(四)、服务自保
说到服务自保就不得不提到服务剔除,这两种逻辑一正一反,他们在同一时刻只能执行一种逻辑。
服务剔除: 即在在服务节点无心跳响应的情况下,将服务节点从注册列表中剔除;
服务自保: 即保留当前注册列表中的所有节点,一个都不能少。
服务自保应用场景: 实际工作中,网络波动等情况是很常见的,这种情况虽然会影响服务节点心跳响应,但服务节点之间的调用并未受到影响,此时如果大批量的剔除服务节点,就会引起大范围的业务停滞,所以这种方式肯定是不可取的,于是服务自保就开始发挥功用了。
服务自保的控制方式:
1.自动控制
开启时机:在一定时间范围内,已续约的节点个数与已注册总服务个数的比值,低于系统限定值,此时服务自保将会开启,所有的服务节点都不会过期(被剔除)。
2.手动控制
#关闭服务自保
eureka.server.enable-self-preservation=false
(五)、服务下线
服务下线通常由服务节点服务器关闭或主动调用shutdown方法来触发,是由服务节点主动向注册中心发起的资源释放命令。
下线代表着服务的生命周期走到尾声。
标记服务状态: 将服务自身状态标记为DOWN;
获取系统锁: 获取到系统锁后,进行服务下线动作,保证下线动作的线程安全;
释放资源: 由于服务注册时,同步启动了心跳任务,状态同步任务和监听器等后台任务,下线时需要释放对应的资源;
发送delete指令: 发送delete指令到注册中心,完成服务下线请求。
ok, spring cloud 初体验之服务注册Eureka, 大概就是这些了,有点冗长,也是我学习中的一些心得,希望大家多指教,后续还会有一些关于微服务的文章,希望大家多多指教。