先来个:Eureka、Consul、Zookeeper的异同点:
Eureka(AP):保证可用性。比如保证预售阶段点赞、喜欢、想要等的可用性,具体点赞、喜欢、想要的人数有多少,他不关心。
Zookeeper及Consul(CP):保证数据的一致性,比如确保数据是正确的,加入你服务器挂了也并不关心。
CAP:
1. Eureka
1.1 基础知识:
1.1.1 服务治理:
Spring Cloud 封装了Netflix公司开发的Eureka模块来实现服务治理。
在传统的rpc远程调用框架中,管理每个服务于服务之间依赖关系比较复杂,管理比较法咋,所以需要使用服务治理,管理服务与服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。
1.1.2 服务注册与发现
1.1.3 Eureka的两个组件:Eureka Server和Eureka Client
1.2. 单机Eureka
1.2.1 创建Eureka7001项目:
1.2.2 添加主要的pom依赖:
更多的不多说了,缺什么自己补
<dependencies>
<!-- eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- 引用自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>com.angenin.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</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>
<!-- 一般通用配置 -->
<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>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
1.2.3 编写yml文件:
server:
port: 7001
eureka:
instance:
hostname: localhost #eureka服务端的实例名称
client:
#false表示不向注册中心注册自己(想注册也可以,不过没必要)
register-with-eureka: false
#false表示自己端就是注册中心,职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
#设置与eurekaServer交互的地址查询服务和注册服务都需要依赖这个地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
1.2.4 Euraka服务主启动
在主启动类上添加@EnableEurekaServer //表示此项目是eureka的服务注册中心
,启动后访问http://localhost:7001/
:
1.2.5 将生产者消费者注册进euraka:
将payment8001和order8010注册进Eureka成为服务提供方:
- 在需要注册的项目pom文件中添加jar包,注意此处和server端不一样,是clinet
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- 在yml中添加Eureka相关配置
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true,不注册就是false
register-with-eureka: true
#是否从EurekaServer抓取已有的注册消息,默认为true,单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
#集群版
#defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
#单机版
defaultZone: http://eureka7001.com:7001/eureka/
- 在启动类上添加注解
@EnableEurekaClient
,表示此项目是euraka客户端,并启动:
- 其中我们在yml中设置的spring.application.name就是我们注册进eureka中的服务名
1.3. 集群Eureka
1.3.1 单服务注册会导致的问题: ![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/78165a7a856fb4af34c144a4844f7e5a.png)
1.3.2 Eureka集群的关键:互相注册,相互守望
1.3.3 搭建Eureka7002:
- 重复上述7001步骤
- 修改yml文件,设置为7001和7002互相注册
eureka:
instance:
#eureka服务端的实例名称,这么写需要去修改hosts文件
hostname: eureka7002.com
client:
# false表示不向注册中心注册自己
register-with-eureka: false
# false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要检索服务
fetch-registry: false
service-url:
# 设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址
# 单机
#defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
# 相互注册,7002中注册7001,7001中注册7002
#defaultZone: http://eureka7002.com:7002/eureka/
defaultZone: http://eureka7001.com:7001/eureka/
- 启动后可以看到,7001中注册了7002,7002中注册了7001
1.3.4 服务注册进集群eureka
- 将payment8001和order注册进集群,修改yml文件中eureka中service-url配置:
service-url:
#集群版
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
#单机版
#defaultZone: http://localhost:7001/eureka/
- 启动后查看并调用:
1.3.5 搭建服务提供方payment集群环境
- 新建payment8002项目,和8001一样配置,不多说
- 在项目Controller中放入自己的端口号,测试被调用的服务是哪个,业务代码不多说
@Value("${server.port}")
private String serverPort;
1.3.6 负载均衡
- 修改order中的调用,之前是写死的调用8001,现在改为调用在Eureka上对外暴露的微服务名称即:
application: # 注册进服务的名称
name: cloud-payment-service
// public static final String PAYMENT_URL = "http://localhost:8001";
public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";
- 然后在消费者的ApplicationContextConfig里的restTemplate方法上加上
@LoadBalanced
,开启负载均衡功能
@Bean
@LoadBalanced //赋予RestTemplate负载均衡的能力
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
- 启动项目后可以看到,调用payment服务时是按照轮询的方式调用8001和8002
1.4. actuator微服务信息完善:
存在的问题:
- 服务名称不规范
- 我们并不能知道这个服务在哪个服务器上即哪个ip,希望当我们把鼠标放在服务名上时,可以知道这个服务是哪台服务器上的
1.4.1 完善Eureka中注册的ip名称,设置访问路径可以显示ip地址:
修改yml文件,在eurela配置中添加:
# 跟client平级
instance:
instance-id: payment8001 # 设置服务名称
prefer-ip-address: true #访问路径可以显示ip
1.5 服务发现:
对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息
实现方式:
- 修改8001的Controller:
@Resource
private DiscoveryClient discoveryClient;
@GetMapping(value = "/payment/discovery")
public Object discovery() {
List<String> services = discoveryClient.getServices();
for (String service : services) {
log.info("*****element:" + service);
}
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
for (ServiceInstance instance : instances) {
log.info(instance.getServiceId()+"\t" + instance.getHost() +
"\t" + instance.getPort() +"\t" + instance.getUri());
}
return this.discoveryClient;
}
- 在启动类上添加
@EnableDiscoveryClient
注解 - 调用方法:
1.6 Eureka的自我保护:
1.6.1 什么是eureka自我保护机制
1.6.2 如何关闭自我保护模式
即:只要掉线了,立马删掉服务
- 在7001的yml中添加配置
server:
#关闭自我保护模式,保证不可用服务被及时删除
enable-self-preservation: false
#心跳的间隔时间,单位毫秒
eviction-interval-timer-in-ms: 2000
- 在8001的yml的instance中配置
instance:
instance-id: payment8001
prefer-ip-address: true #访问路径可以显示ip
#Eureka客户端向服务端发送心跳的实际间隔,单位为秒(默认为30秒)
lease-renewal-interval-in-seconds: 1
#Eureka服务端收到最后一次心跳后等待时间上线,单位为秒(默认为90秒) 超时将剔除服务
lease-expiration-duration-in-seconds: 2
2. zookeeper
2.1 注册中心Zookeeper
zookeeper也是有心跳机制,在一定时间能如果一直没心跳返回,Zookeeper就会把服务节点剔除掉。所以在Zookeeper上的服务节点是临时节点
2.2 zookeeper的启动:
- 关闭防火墙
systemctl stop firewalld
systemctl status firewalld
- docker启动Zookeeper
#拉取Zookeeper镜像
docker pull zookeeper
#启动Zookeeper
docker run --name zk01 -p 2181:2181 --restart always -d zookeeper
2.3 服务提供者注册进zookeeper
- 新建工程cloud-provider-payment8004
- pom文件:
<!--因为接下来不会用到数据库,所以不导入数据库相关的依赖(防止没配置而报错)-->
<!--替换掉eureka依赖,其他直接复制8001-->
<!--SpringBoot整合Zookeeper客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<!-- <exclusions>-->
<!--先排除自带的zookeeper3.5.3-->
<!-- <exclusion>-->
<!-- <groupId>org.apache.zookeeper</groupId>-->
<!-- <artifactId>zookeeper</artifactId>-->
<!-- </exclusion>-->
<!-- </exclusions>-->
</dependency>
<!--添加zookeeper3.4.9版本(引入对应版本的依赖)-->
<!-- <dependency>-->
<!-- <groupId>org.apache.zookeeper</groupId>-->
<!-- <artifactId>zookeeper</artifactId>-->
<!-- <version>3.4.9</version>-->
<!-- </dependency>-->
- 编写yml文件
#端口号
server:
port: 8004
spring:
application:
#服务别名——注册到zookeeper注册中心的名称
name: cloud-provider-payment
cloud:
zookeeper:
connect-string: ip:2181 #linux的ip加暴露的端口号
- 主启动类添加注解:
@EnableDiscoveryClient //该注解用于向使用consul或者Zookeeper作为注册中心时注册服务
@SpringBootApplication
public class PaymentMain8004 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8004.class, args);
}
}
- 新建controller测试:
@Slf4j
@RestController
public class PaymentController {
@Value("${server.port}") //获取端口号
private String serverPort;
@RequestMapping("/payment/zk")
public String paymentzk(){
return "springcloud with zookeeper:" + serverPort + "\t" + UUID.randomUUID().toString();
}
}
-
启动服务并调用接口:
-
进入zookeeper容器内部查看:
2.4 服务消费者注册进zookeeper
-
新建消费者模块cloud-consumerzk-order80。
-
pom和yml直接复制8004。(yml中端口号改为80,应用名改为cloud-consumer-order,其他都相同)
-
主启动类。(与8004相同)
-
在springcloud包下新建config.ApplicationContextConfig
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced //负载均衡
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
- 新建controller.OrderZKController
@RestController
@Slf4j
public class OrderZKController {
public static final String INVOKE_URL = "http://cloud-provider-payment";
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/payment/zk")
public String paymentInfo(){
String result = restTemplate.getForObject(INVOKE_URL + "/payment/zk", String.class);
return result;
}
}
- 启动项目
3. consul
Consul官网:https://www.consul.io/
Consul中文文档:https://www.springcloud.cc/spring-cloud-consul.html
3.1 简介
Consul是一种服务网格解决方案,提供具有服务发现,配置和分段功能的全功能控制平面。这些功能中的每一个都可以根据需要单独使用,也可以一起使用以构建完整的服务网格。Consul需要一个数据平面,并支持代理和本机集成模型。Consul附带了一个简单的内置代理,因此一切都可以直接使用,还支持Envoy等第三方代理集成
3.2 主要特点
-
服务发现:Consul的客户端可以注册服务,例如 api或mysql,其他客户端可以使用Consul来发现给定服务的提供者。使用DNS或HTTP,应用程序可以轻松找到它们依赖的服务。
-
健康检测:领事客户端可以提供任意数量的运行状况检查,这些检查可以与给定服务(“ Web服务器是否返回200 OK”)或本地节点(“内存利用率低于90%”)相关。操作员可以使用此信息来监视群集的运行状况,服务发现组件可以使用此信息将流量从不正常的主机发送出去。
-
KV存储:应用程序可以将Consul的分层键/值存储用于多种目的,包括动态配置,功能标记,协调,领导者选举等。简单的HTTP API使其易于使用。
-
安全的服务通信:领事可以为服务生成并分发TLS证书,以建立相互TLS连接。 意图 可用于定义允许哪些服务进行通信。可以使用可以实时更改的意图轻松管理服务分段,而不必使用复杂的网络拓扑和静态防火墙规则。
-
多数据中心:Consul开箱即用地支持多个数据中心。这意味着Consul的用户不必担心会构建其他抽象层以扩展到多个区域。
Consul旨在对DevOps社区和应用程序开发人员友好,使其非常适合现代,灵活的基础架构
3.3 docker启动
#拉取consul镜像
docker pull consul
#启动consul
docker run -d -p 8500:8500/tcp --name myConsul consul agent -server -ui -bootstrap-expect=1 -client=0.0.0.0
浏览器访问:http://ip/:8500
3.4 服务提供者注册进consul
- 新建服务提供者cloud-provider-consul-payment8006。
- pom复制8004。(用下面的依赖替换Zookeeper的依赖)
<!--SpringCloud consul-server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
- 修改yml文件
server:
port: 8006
spring:
application:
name: consul-provider-payment
cloud:
consul:
host: 10.211.55.17 #用linux的ip地址(consul在本机就填localhost)
port: 8500
discovery:
service-name: ${spring.application.name}
- 主启动类(与8004相同)
- controller
@RestController
@Slf4j
public class PaymentController {
@Value("${server.port}") //获取端口号
private String serverPort;
@RequestMapping("/payment/consul")
public String paymentConsul(){
return "springcloud with zookeeper:" + serverPort + "\t" + UUID.randomUUID().toString();
}
}
-
启动项目:
-
访问接口:
3.5 服务消费者注册进consul
- 新建模块cloud-consumer-consul-order80
- pom(与8006相同)
- yml(端口号为80,应用名为consul-consumer-order,其他和8006相同)
- 主启动类(与8006相同)
- config(和zk的消费者相同)
- controller.OrderConsulController
@RestController
@Slf4j
public class OrderConsulController {
public static final String INVOKE_URL = "http://consul-provider-payment";
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/payment/consul")
public String paymentInfo(){
String result = restTemplate.getForObject(INVOKE_URL + "/payment/consul", String.class);
return result;
}
}
-
启动项目
-
测试