目录
分布式 CAP 理论
一:Eureka 基础知识
1)什么是服务治理:
a:Spring Cloud 封装了 Netflix 公司开发的 Eureka 模块,来实现 服务治理。
b:在传统的 RPC 远程调用框架中,管理 服务与服务 之间的依赖关系比较复杂,所以需要服务治理,管理 服务与服务 之间的依赖关系。
c:实现服务发现与注册。和 Ribbon 组合,可以实现 服务调用、负载均衡、容错等,
2)什么是服务注册与发现:
a:Eureka 采用了 CS 的设计模式,Eureka 作为服务注册功能的服务器,它是服务注册中心。
b:而系统中的其他微服务,使用 Eureka 客户端,连接到 Eureka Server,并维持心跳连接。就可以通过 Eureka Server 来监控系统中的,每个微服务是否正常运行。
c:在 服务注册与发现 中,有一个注册中心,当服务器启动的时候,会把当前自己服务器的信息(服务地址、通讯地址等),以别名方式注册到注册中心上。
d:另一方(消费者|服务提供者),以该别名的方式,去注册中心上,获取到实际的服务通信地址,然后再实现本地 RPC 调用 RPC 远程调用框架和新设计思想。
e:在于注册中心,因为使用注册中心,管理每个服务与服务之间的依赖关系。
f:在任何 RPC 远程调用框架中,都会有一个注册中心(存放服务地址相关信息(接口地址))
3)Eureka 两组件:
a:Eureka Server:提供 服务注册 服务
- 各个微服务节点,通过配置启动后,会在 Eureka Server 中进行注册。
- 这样 Eureka Server 中的 服务注册表中,将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直接观察到。
b:Eureka Client:通过 注册中心 进行访问
- 是一个 Java 客户端,用于简化 Eureka Server 的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。
- 在应用启动后,将会向 Eureka Server 发送心跳(默认周期为 30s)。
- 如果 Eureka Server 在多个心跳周期内,没有接收到每个节点的心跳,Eureka Server 会把这个服务节点 在 服务注册表中 移除。(默认 90 s)(eureka保证 ap)
二:单机 Eureka 构建步骤
1)IDEA 生成 Eureka Server 端,服务注册中心:
a:建 Module:(cloud-eureka-server-7001)
b:改 POM:
<dependencies>
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
</dependency>
<!-- Eureka Server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- web 和 徒图形监控,必须要用 -->
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
c:写 YML:
server:
port: 7001
spring:
application:
name: cloud-eureka-server-7001
eureka:
instance:
# 服务端 实例名称
hostname: localhost
client:
# 表示 不向注册中心 注册自己
register-with-eureka: false
# 表示 自己端就是注册中心,职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
# 设置 与 Eureka Server 交互的地址查询服务
# 和 注册服务,都需要依赖这个地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
d:主启动:
@EnableEurekaServer
@SpringBootApplication
public class EurekaServer {
public static void main(String[] args) {
SpringApplication.run(EurekaServer.class, args);
}
}
e:测试:(没有服务注册进来)
2)Eureka Client 端 cloud-provider-payment 8001,将注册进 Eureka Server:成为服务提供者 Provider:
a:修改项目:(cloud-provider-payment-8001)
b:改 POM:(Eureka 客户端依赖)
<!-- Eureka 客户端依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
c:写 YML:
eureka:
client:
# 表示 是否将自己注册进 eureka ,默认为 true
register-with-eureka: true
# 是否从 Eureka 抓取自己的注册信息,默认为 true
# 单节点无所谓,集群必须设置为 true,才能配合 ribbon 使用 负载均衡。
fetch-registry: true
service-url:
# 注册到 Eureka 的地址
defaultZone: http://localhost:7001/eureka
d:主 启动:
@EnableEurekaClient
@MapperScan(basePackages = {"com.cloud.dao"})
@SpringBootApplication
public class ProviderPayment {
public static void main(String[] args) {
SpringApplication.run(ProviderPayment.class, args);
}
}
e:测试:
f:自我保护机制:(红色包错)
3)Eureka Client 端 cloud-consumer-order 80,将注册进 Eureka Server:成为服务消费者 consumer:
a:修改项目:cloud-consumer-order-80
b:改 POM:添加 Eureka 客户端依赖
c:改 YML:和 8001 一样
d:主启动:@EnableEurekaClient
e:测试:
4)bug:注意 yum 文件缩进
三:集群 Eureka 构建步骤
1)Eureka 集群原理说明:
a:微服务 RPC 远程服务调用,最核心的是 :高可用
b:高可用解决办法:搭建 Eureka 注册中心集群,实现 负载均衡 + 故障容错。
客户端拉取服务端服务信息是通过一个定时任务定时拉取的,每次拉取后刷新本地已保存的信息,需要使用时直接从本地直接获取。
2)Eureka Server 集群环境构建步骤:(两个 Server 互相注册)
a:参考 cloud-eureka-server 7001,新建 cloud-eureka-server 7002 module:
b:改 POM:添加 依赖文件
c:修改 映射配置:
- 找到 hosts 配置文件
- 修改映射配置,添加进 hosts 文件:127.0.0.1 eureka7001.com、127.0.0.1 eureka7002.com
d:写 yum(以前是 单机):
- server 7001 向 7002 注册:
eureka:
instance:
# 服务端 实例名称
hostname: eureka7001.com
client:
# 表示 不向注册中心 注册自己
register-with-eureka: false
# 表示 自己端就是注册中心,职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
# 设置 与 Eureka Server 交互的地址查询服务
# 和 注册服务,都需要依赖这个地址
# defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
defaultZone: http://eureka7002.com:7002/eureka
- server 7002 向 7001 注册:
eureka:
instance:
# 服务端 实例名称
hostname: eureka7002.com
client:
# 表示 不向注册中心 注册自己
register-with-eureka: false
# 表示 自己端就是注册中心,职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
# 设置 与 Eureka Server 交互的地址查询服务
# 和 注册服务,都需要依赖这个地址
# defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
defaultZone: http://eureka7001.com:7001/eureka
e:主启动:@EnableEurekaServer
f:测试 server 7001/7002 能否正常启动(http://eureka7001.com:7001/|http://eureka7002.com:7002/)
3)将 支付服务 8001 微服务,发布到上面 2台 Eureka Server 集群配置中:
配置:yum:
# (eureka 集群版)注册到 Eureka 的地址
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
4)将 订单服务 80 微服务,发布到上面 2台 Eureka Server 集群配置中:
配置 :yum:
# (eureka 集群版)注册到 Eureka 的地址
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
5)测试 01:(访问)
a:http://eureka7001.com:7001/
b:http://eureka7002.com:7002/
6)支付服务提供者 8001 ,集群环境构建:
a:参考 cloud-provider-payment 8001,新建 cloud-provider-payment 8002 的 module。
b:改 POM:和 8001 一致
c:写 YUM:port :8002
d:主启动:和 8001 一致
e:业务类和 8001 一致
f:修改 8001/8002 的 Controller:调用不同的支付服务,返回时,做出标记(自测都要成功)
@Value("${server.port}")
private String serverPort;
@GetMapping(value = "/getPayment")
public CommonResult getPayment(@RequestParam(value = "id") Long id) {
log.info("传入的数据为:" + id);
Payment payment = paymentService.getPayment(id);
if (payment != null) {
log.info("查询成功");
return new CommonResult(200, "成功111:", payment.toString() + "--" + "提供者 :端口号为 :" + serverPort);
} else {
log.info("查询失败");
return new CommonResult(400, id + "没找到");
}
}
7)负载均衡:
a:bug:订单 访问 支付服务的路径写死,就只能一直访问一个服务,不能进行轮训访问。
b:使用 @LoadBalanced 注解,赋予 RestTemplate 负载均衡的能力:(改完之后也不能访问,因为没有加入访问规则)
@Slf4j
@RestController
public class OrderController {
// public static final String PAYMENT_URL = "http://localhost:8001";
public static final String PAYMENT_URL = "http://CLOUD-PROVIDER-PAYMENT";
@Autowired
private RestTemplate restTemplate;
@GetMapping(value = "/getPayment/get")
public CommonResult<Payment> getPayment(@RequestParam(value = "id") Long id) {
log.info("传入的数据为 :" + id);
CommonResult<Payment> paymentCommonResult
= restTemplate.getForObject(PAYMENT_URL + "/getPayment?id=" + id, CommonResult.class);
log.info("远程调用 得到的数据为 : " + paymentCommonResult);
return paymentCommonResult;
}
}
c:ApplicationContextBean:(加入:@LoadBalanced 注解)(轮询访问,类似 Ribbon 负载均衡功能)
@Configuration
public class ApplicationContextConfig {
@LoadBalanced
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
8)测试 02:
a:先启动 Eureka Server ,在启动 80、7001、7002、8001、8002
b:访问:http://127.0.0.1:80/getPayment/get?id=1
c:结果:能够做到 轮询访问
d:Ribbon 和 Eureka 整合以后,Consumer 可以直接调用服务,而不用关心地址和端口号。
四:actuator 微服务信息完善(建议配)
1)主机名称:服务名称的修改:
a:修改 cloud-provider-payment 8001 和 8002 的YML:
eureka:
instance:
instance-id: cloud-provider-payment-8002
2)访问信息有 IP 信息提示:
eureka:
instance:
instance-id: cloud-provider-payment-8002
prefer-ip-address: true
五:服务发现 Discovery
1)就是一个 注解标签:(@EnableDiscoveryClient)花活很多,之后还介绍,长期用。
2)对于 注册进 Eureka 里面的微服务,客户端 可以通过服务发现 来获得 每一个服务的信息。
3)修改 cloud-provider-payment 8001 的 Controller:(增加接口)
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping(value = "/discover")
public Object discover() {
//获得服务列表的清单
List<String> services = discoveryClient.getServices();
for (String s : services) {
System.out.println(s);
}
//根据 为服务具体点 注册名称,获取该名称的所有 实例节点 信息
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PROVIDER-PAYMENT");
for (ServiceInstance i : instances) {
System.out.println(i.getInstanceId() + " : " + i.getHost() + " : " + i.getPort());
}
return this.discoveryClient;
}
3)8001 主启动类:(注解:@EnableDiscoveryClient:花活很多,之后还介绍,长期用)
@EnableDiscoveryClient
@EnableEurekaClient
@MapperScan(basePackages = {"com.cloud.dao"})
@SpringBootApplication
public class ProviderPayment8001 {
public static void main(String[] args) {
SpringApplication.run(ProviderPayment8001.class, args);
}
}
4)自测:(localhost:8001/discover)
/*
* ncloud-provider-payment
* cloud-consumer-order-80
* cloud-provider-payment-8002 : 192.168.43.190 : 8002
* cloud-provider-payment-8001 : 192.168.43.190 : 8001
*/
{
"discoveryClients": [
{
"order": 0,
"services": [
"cloud-provider-payment",
"cloud-consumer-order-80"
]
},
{
"order": 0,
"services": []
}
],
"services": [
"cloud-provider-payment",
"cloud-consumer-order-80"
],
"order": 0
}
六:Eureka 自我保护(为了保证 :AP)
1)故障现象:
2)导致原因:
a:一句话概述:某时刻某一个 为服务不可用了,Eureka 不会立即清理,依旧会对该微服务的信息进行保存。
b:属于 CAP 里面的 AP 分支。(有一定的分区容错性,并且为了保证高可用,好死不如赖活着的 设计思想)
3)默认开启,怎么禁止 Eureka 自我保护:
a:注册中心 Eureka Server 端 7001:
eureka:
server:
# 关闭 自我保护机制,保证 不可用的服务,被及时剔除(默认为 true,不剔除)
enable-self-preservation: false
# 间隔时间 两秒钟(默认 90秒)
eviction-interval-timer-in-ms: 2000
b:生产者客户端 Eureka Client 端 8001|8002:
- 默认:
- 配置:
eureka:
instance:
instance-id: cloud-provider-payment-8001
prefer-ip-address: true
# Eureka 客户端,向服务端发送心跳端时间间隔,单位为秒(默认 30 秒)
lease-renewal-interval-in-seconds: 1
# Eureka 服务端,在收到最后一次心跳后,等待的时间上线,单位为秒(默认 90 秒)
lease-expiration-duration-in-seconds: 2
- 测试:
1. 8001 入住到 7001 ,假设 8001 发生故障,看 8001 是存在 还是立刻被剔除。(马上剔除)
七:Eureka 停更说明
1)见 Zookeeper: