SpringCloud全家桶:SpringCloud01 – 初识
SpringCloud全家桶:SpringCloud02 – 服务注册 Eureka Zookeeper Consul Nacos
SpringCloud全家桶:SpringCloud03 – 服务调用 Ribbon OpenFeign
SpringCloud全家桶:SpringCloud04 – 服务降级熔断 Hystrix Sentinel
SpringCloud全家桶:SpringCloud05 – 服务网关 Gateway
SpringCloud全家桶:SpringCloud06 – 服务配置 Config Nacos
SpringCloud全家桶:SpringCloud07 – 消息总线 Bus
SpringCloud全家桶:SpringCloud08 – 消息驱动 Stream
SpringCloud全家桶:SpringCloud09 – 分布式请求链路追踪 Sleuth
SpringCloud全家桶:SpringCloud10 – Alibaba Nacos
SpringCloud全家桶:SpringCloud11 – Alibaba Sentinel
SpringCloud全家桶:SpringCloud12 – Alibaba 分布式事务 Seata
服务注册与发现
Eureka
基础知识
什么是服务治理?
SpringCloud 封装了Netflix公司开发的Eureka模块来实现服务治理
在传统的rpc远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务与服务之间依赖关系,可以实现服务调用
、负载均衡
、容错
等,实现服务发现与注册
。
什么是服务注册与发现?
Eureka采用了CS
的设计架构,EurekaService 作为服务注册
功能的服务器,它是服务注册中心
。而系统中其他微服务,使用eureka的客户端连接到Eureka Service 并维持心跳连接
。这样系统的维护人员就可以通过Eureka Server来监控
系统中各个微服务是否正常运行。
在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己的服务器信息 比如 服务器地址
、通讯地址
等以别名的方式注册到中心上。另一方(消费者|服务提供者
),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后在实现本地RPC调用RPC远程调用框架核心设计思想: 在于注册中心
,因为使用注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。在任何rpc远程框架中,都会有一个注册中心(存放服务地址相关信息(接口地址))。
组件
Eureka包含两个组件:Eureka Server 和 Eureka Client
Eureka Service: 提供服务注册服务
各个微服务节点通过配置启动后,会在Eureka Server中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
Eureka Client: 通过注册中心进行访问
是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向EurekaServer发送心跳(默认周期30s)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90s)。
单机搭建
服务中心搭建
建module
建立module cloud-eureka-server7001
改pom
<dependencies>
<!--eureka server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!--commons api-->
<dependency>
<groupId>com.jsu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--boot 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.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
写yml
# 端口号
server:
port: 7001
spring:
application:
name: cloud-eureka-server7001
# eureka
eureka:
instance:
# eureka 服务端的实例名称
hostname: localhost
client:
# false表示不再注册中心注册自己
register-with-eureka: false
# false表示自己端就是注册中心.我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
# 设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
启动类
// 表示注册信息
@EnableEurekaServer
@SpringBootApplication
public class EurekaMain7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7001.class);
}
}
测试
访问:http://localhost:7001
8001/80写入服务
cloud-provider-payment8001
改pom
<!-- eureka client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
yml
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
# 入住地址
defaultZone: http://localhost:7001/eureka
启动类
// eureka client端
@EnableEurekaClient
// mybatis-plus扫描包
@MapperScan("com.jsu.springcloud.dao")
@SpringBootApplication
public class PaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class);
}
}
cloud-consumer-order80 一样 对应修改
集群搭建
核心思想: 两个eureka之间相互注册
1、参考7001 搭建7002 除yml不一样以外其他一样
2、为了方便测试 修改hosts文件 C:\Windows\System32\drivers\etc
3、改yml
- 7001
# 端口号
server:
port: 7001
spring:
application:
name: cloud-eureka-server7001
# eureka
eureka:
instance:
# eureka 服务端的实例名称
# hostname: localhost
hostname: eureka7001.com
client:
# false表示不再注册中心注册自己
register-with-eureka: false
# false表示自己端就是注册中心.我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
# 设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
service-url:
# defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
defaultZone: http://eureka7002.com:7002/eureka/
- 7002
# 端口号
server:
port: 7002
spring:
application:
name: cloud-eureka-server7002
# eureka
eureka:
instance:
# eureka 服务端的实例名称
# hostname: localhost
hostname: eureka7002.com
client:
# false表示不再注册中心注册自己
register-with-eureka: false
# false表示自己端就是注册中心.我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
# 设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
service-url:
# defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
defaultZone: http://eureka7001.com:7001/eureka/
4、注册80/8001服务
- 8001
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
# 入住地址
# defaultZone: http://localhost:7001/eureka 单机版
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka # 集群版
- 80
server:
port: 80
spring:
application:
name: cloud-order-service
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
# 入住地址
# defaultZone: http://localhost:7001/eureka 单机版
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka # 集群版
提供者集群
参考8001建立8002
修改controller
8001/8002 修改一样内容!
@RestController
@Slf4j
public class PaymentController {
@Autowired
private PaymentService paymentService;
// 增加
@Value("${server.port}")
private String serverPort;
@RequestMapping(value = "/payment/create", method = RequestMethod.POST)
public CommonResult<Integer> createPayment(@RequestBody Payment payment) {
Integer result = paymentService.createPayment(payment);
log.info(" --- 插入结果 ---");
if (result > 0) {
// 修改
return new CommonResult<Integer>(200, "插入数据成功,serverPort" + serverPort, result);
}
return new CommonResult<Integer>(444, "插入数据失败");
}
@RequestMapping(value = "/payment/get/{id}", method = RequestMethod.GET)
public CommonResult<Payment> createPayment(@PathVariable("id") Long id) {
Payment payment = paymentService.getPaymentById(id);
log.info(" --- 插入结果 ---");
if (payment != null) {
// 修改
return new CommonResult<Payment>(200, "查询成功,serverPort" + serverPort, payment);
}
return new CommonResult<Payment>(444, "查询失败,没有对应记录");
}
}
80修改(负载均衡)
访问地址不能写死
@LoadBalanced 实现负载均衡
测试
Actuator微服务信息完善
当前问题:
主机名称服务名不规范 无ip地址显示
配置8001/8002yml
instance:
instance-id: 8001 # 8001项目就配8001 8002配8002 两个都配上
prefer-ip-address: true
修改后
Discovery服务发现
服务发现
对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息
修改8001/8002的controller
@Resource
private DiscoveryClient discoveryClient;
@RequestMapping(value = "/payment/discovery", method = RequestMethod.GET)
public Object discovery() {
// 获取所有服务
List<String> service = discoveryClient.getServices();
for (String element : service) {
log.info("**** element: " + element);
// **** element: cloud-payment-service
// **** element: cloud-order-service
}
// 根据服务名获取实例
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
for (ServiceInstance instance : instances) {
log.info(instance.getInstanceId() + "\t" + instance.getHost()
+ "\t" + instance.getPort() + "\t" + instance.getUri());
// 8001 192.168.21.1 8001 http://192.168.21.1:8001
// 8002 192.168.21.1 8002 http://192.168.21.1:8002
}
return this.discoveryClient;
}
启动类上加上@EnableDiscoveryClient
Eurake自我保护
概述
保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护。一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据,也就是不会注销任何微服务。
一句话:
某时刻某一个微服务不可用了,Eureka不会立刻清理,依旧会对该微服务的信息进行保存
为什么会产生Eureka自我保护机制?
为了防止EurekaClient可以正常运行,但是与EurekaServer网络不通情况下,Eurekaserver不会立刻将EurekaClient服务剔除
什么是自我保护模式?
默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例((默认90秒)。是当网络分区故障发生(延时、卡顿、拥挤)时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险了——因为微务本身其实是健康的,此时本不应该注销这个微服务。Eureka通过“自我保护模式”来解决这个问题——当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。
注意:
在自我保护模式中,Eureka Server会保护服务注册表中的信息,不再注销任何服务实例。
它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例
。一句话讲解:好死不如赖活着
综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留))也不盲目注销任何健康的微服务
。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。
禁止自我保护
注:一般开发过程中 开启自我保护
关闭8001时候 立刻消失!!
Zookeeper
准备工作
zookeeper详解以及安装:https://blog.csdn.net/qq_42432141/article/details/108454826
关闭linux防火墙后启动zookeeper服务器
# 停止firewall
systemctl stop firewalld.service
# 禁止firewall开机启动
systemctl disable firewalld.service
项目搭建
服务提供者
新建cloud-provider-payment8003zk
pom
<dependencies>
<!-- SpringBoot整合Web组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.jsu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- SpringBoot整合zookeeper客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<!--先排除自带的zookeeper3.5.3-->
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--添加zookeeper3.4.11版本-->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.11</version>
</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>
yml
server:
port: 8003
spring:
application:
name: cloud-provider-payment
cloud:
zookeeper:
connect-string: 192.168.21.122:2181
启动类
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentZk8003 {
public static void main(String[] args) {
SpringApplication.run(PaymentZk8003.class);
}
}
controller
@RestController
@Slf4j
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@RequestMapping(value = "/payment/zk")
public String paymentzk(){
return "SpringCloud with zookeeper: "+serverPort+"\t"+ UUID.randomUUID().toString();
}
}
服务消费者
新建cloud-consumer-order80
pom
<dependencies>
<!-- zookeeper -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.11</version>
</dependency>
<!-- 引入自己的api通用包-->
<dependency>
<groupId>com.jsu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--springboot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
</exclusions>
</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>
yml
server:
port: 80
spring:
# 服务注册 --注册zookeeper到注册中心名称
application:
name: cloud-consumer-order
cloud:
zookeeper:
connect-string: 192.168.21.122:2181
启动类
@SpringBootApplication
@EnableDiscoveryClient
public class OrderZkMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderZkMain80.class);
}
}
逻辑代码
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
@RestController
@Slf4j
public class OrderZkController {
private final static String PAYMENT_URL = "http://cloud-provider-payment";
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value = "/consumer/payment/zk", method = RequestMethod.GET)
public String paymentZk() {
String result = restTemplate.getForObject(PAYMENT_URL + "/payment/zk", String.class);
return result;
}
}
测试
进入zookeeper/bin目录下
# 开启服务
./zkServer.sh start
# 运行客户端
./zkClient.sh
求证: zookeeper是临时结点还是永久结点?
关闭运行的8003 发现service/cloud-provider-payment下的服务丢失了
重启后发现与之前的不一样 是新的zookeeper 说明是:临时结点
Consul
简介
是什么?
官网 :https://www.consul.io/docs/intro
能干嘛?
- 服务发现: 提供http和dns两种发现方式
- 健康监测:支持多种协议,HTTP,TCP,Docker,Shell脚本定制化
- KV存储:key,Value的存储方式
- 多数据中心:consul支持多数据中心
- 可视化web界面
怎么用?
官网:https://www.consul.io/downloads.html
解压 – 运行
搭建项目
服务提供者
建立项目cloud-provider-payment8004consul
pom
<dependencies>
<!-- 引入自己的api通用包-->
<dependency>
<groupId>com.jsu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--consul-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!--springboot-->
<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-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
yml
server:
port: 8004
spring:
application:
name: consul-provider-payment
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: ${spring.application.name}
启动类
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain8004 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8004.class);
}
}
逻辑代码
@RestController
@Slf4j
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@RequestMapping(value = "/payment/consul",method = RequestMethod.GET)
public String paymentConsul(){
return "SpringCloud with consul: "+serverPort+"\t "+ UUID.randomUUID().toString();
}
}
服务消费者
建立项目cloud-consumer-order80consul
pom
<dependencies>
<!-- consul -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!-- 引入自己的api通用包-->
<dependency>
<groupId>com.jsu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--springboot-->
<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>
</project>
yml
server:
port: 80
spring:
application:
name: consul-provider-payment
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: ${spring.application.name}
启动类
@SpringBootApplication
@EnableDiscoveryClient
public class OrderConsulMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderConsulMain80.class);
}
}
逻辑代码
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
@RestController
@Slf4j
public class OrderController {
private final static String PAYMENT_URL = "http://consul-provider-payment";
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value = "/consumer/payment/consul", method = RequestMethod.GET)
public String paymentConsul() {
String result = restTemplate.getForObject(PAYMENT_URL + "/payment/consul", String.class);
return result;
}
}
测试
访问:http://localhost:8500/
Nacos
SpringCloudAlibaba – Nacos:https://blog.csdn.net/qq_42432141/article/details/110438580