问题:微服务RPC远程服务调用最核心的要求是什么?
高可用,如果注册中心只有一个,那么它一旦出故障,那么所有的服务都会瘫痪;
所以,我们搭建Eureka集群是必须要做的,实现负载均衡 + 故障!
Eureka集群的原理:互相注册,相互守望
本节,我们将搭建如下集群:

一、Eureka Server集群环境的构建
1、参考cloud-eureka-server7001创建(复制)一个一模一样的cloud-eureka-server7002服务
2、因为是本机搭建集群,我们需要修改我们的 hosts 文件(一般在此目录下:C:\Windows\System32\drivers\etc )
增加以下映射配置项:
127.0.0.1 eureka7001.com 127.0.0.1 eureka7002.com
3、分别修改cloud-eureka-server7001和cloud-eureka-server7002的application.yml配置文件:
cloud-eureka-server7001:
server: port: 7001 eureka: instance: hostname: eureka7001.com #eureka服务端实例名称 client: register-with-eureka: false #表示不向注册中心注册自己 fetch-registry: false #false表示自己就是注册中心,我的职责就是维护服务实例,并不区检索服务 service-url: # defaultZone: http://eureka7001.com:7001/eureka/ # 不搭建集群 单机 指向自己 defaultZone: http://eureka7002.com:7002/eureka/ # 搭建集群 集群是指向其他eureka
cloud-eureka-server7002:
server: port: 7002 eureka: instance: hostname: eureka7002.com #eureka服务端实例名称 client: register-with-eureka: false #表示不向注册中心注册自己 fetch-registry: false #false表示自己就是注册中心,我的职责就是维护服务实例,并不区检索服务 service-url: # defaultZone: http://eureka7002.com:7002/eureka/ # 不搭建集群 单机 指向自己 defaultZone: http://eureka7001.com:7001/eureka/ # 搭建集群 集群是指向其他eureka
4、分别启动cloud-eureka-server7001和cloud-eureka-server7002服务


可见,集群版的EurekaServer已经搭建完成,测试通过!
二、将cloud-provider-payment8001入驻EurekaServer集群
修改application.yml配置文件(其他的不用改动,只需要修改 eureka.service-url.defaultZone配置项即可):
server: port: 8001 spring: application: name: cloud-payment-service datasource: type: com.alibaba.druid.pool.DruidDataSource #当前数据源操作类型 driver-class-name: org.gjt.mm.mysql.Driver url: jdbc:mysql://localhost:3306/cloud2020?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: 831121 eureka: client: register-with-eureka: true #表示向注册中心注册自己 默认为true fetch-registry: true #是否从EurekaServer抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡 service-url: # defaultZone: http://localhost:7001/eureka # 单机版 入驻的EurekaServer地址 defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka #集群版 mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.jiguiquan.springcloud.entities #所有entity别名所在包
三、将cloud-consumer-order80入驻EurekaServer集群
修改application.yml配置文件(其他的不用改动,只需要修改 eureka.service-url.defaultZone配置项即可):
server: port: 80 spring: application: name: cloud-order-service eureka: client: register-with-eureka: true #表示向注册中心注册自己 默认为true fetch-registry: true #是否从EurekaServer抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡 service-url: # defaultZone: http://localhost:7001/eureka # 单机版 入驻的EurekaServer地址 defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka #集群版
四、分别启动cloud-provider-payment8001和cloud-consumer-order80服务


由此可见,cloud-provider-payment8001和cloud-consumer-order80服务也都顺利注册进我们的EurekaServer集群了!
五、支付服务cloud-provider-payment8001服务的高可用集群搭建
1、参考cloud-provider-payment8001服务新建(或复制)一个cloud-provider-payment8002服务
pom.xml、application.yml、主启动类、业务结构;
2、为了后面的负载均衡,知道具体是调的那个payment服务,我们需要对payment8001和payment8002服务的Controller层做下标记:
在对应的返回数据 message 中带上各自的 serverPort端口号;

3、启动cloud-provider-payment8001和cloud-provider-payment8002服务,进行测试:
http://eureka7001.com:7001 和 http://eureka7002.com:7002

4、通过cloud-consumer-order80调用方式访问:

经过多次测试,端口一直都是访问的“8001”这个payment服务,所以,通过restTemplate的方式,并没有实现负载均衡;
六、通过Eureka注册中心,实现服务间调用及负载均衡
1、修改cloud-consumer-order80中调用的url:

2、修改ApplicationContextConfig.java 文件
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced //赋予RestTemplate一个默认的负载均衡能力
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
3、重启cloud-consumer-order80服务
再次查询的结果如下图:


经过多次尝试,确定实现了负载均衡,且默认的负载均衡方式为轮询(单次轮询)
—— 当然,默认的轮询方式可以修改,可以自定义。
七、actuator微服务信息完善
1、只暴露服务名,不要显示主机名:
修改前:

修改对应的微服务的 application.yml 配置文件,增加一个配置信息 eureka.instance.instance-id :
eureka: client: register-with-eureka: true #表示向注册中心注册自己 默认为true fetch-registry: true #是否从EurekaServer抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡 service-url: # defaultZone: http://localhost:7001/eureka # 单机版 入驻的EurekaServer地址 defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka #集群版 instance: instance-id: payment8001
修改后:

2、访问信息有IP信息显示:
修改前:

修改对应的微服务的 application.yml 配置文件,增加一个配置信息 eureka.instance.prefer-ip-address: true:
instance: instance-id: payment8002 prefer-ip-address: true
修改后:

同理,完成对 cloud-consumer-order80 服务的修改!
八、服务发现Discovery
每个微服务要向外提供一个功能 —— 类似于“关于我们”!
对于注册进Eureka里面的微服务,可以通过服务发现来获得该服务的信息。
以 cloud-provider-payment8001 为例:
1、在PaymentController 中引用 DiscoveryClient;
@RestController
@RequestMapping("/payment")
@Slf4j
public class PaymentController {
@Autowired
private PaymentService paymentService;
@Value("${server.port}")
private String serverPort;
@Autowired
private DiscoveryClient discoveryClient;
@PostMapping(value = "/create")
public CommonResult create(@RequestBody Payment payment){
int result = paymentService.create(payment);
log.info("****插入结果为:" + result);
if (result > 0){
return new CommonResult(200, "插入数据库成功, serverPort: "+ serverPort, result);
} else {
return new CommonResult(444, "插入数据库失败", null);
}
}
@GetMapping(value = "/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
Payment payment = paymentService.getPaymentById(id);
log.info("****~~~~查询结果为:"+payment);
if (payment != null){
return new CommonResult<>(200, "查询成功, serverPort: "+ serverPort, payment);
} else {
return new CommonResult<>(444, "没有对应记录, 查询id为:"+id, null);
}
}
@GetMapping
public Object discovery(){
List<String> services = discoveryClient.getServices();
for (String element:services){
log.info("*****element: "+ element);
}
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;
}
}
2、在主启动类上添加 @EnableDiscoveryClient 注解:
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
public class PaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class, args);
}
}
3、启动测试:
访问 http://localhost:8001/payment/discovery 的结果:

控制台的输出结果:

九、Eureka的自我保护模式
Eureka的自我保护模式,主要用于一组客户端 和 EurekaServer之间存在网络分区场景下的保护;
一旦进入保护模式,EurekaServer 将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据,也就是不会注销任何微服务;
如果在Eureka页面看到如下红色提示,则说明Eureka已经进入了保护模式:

简单总结:
某时刻某一个微服务不可用了,Eureka不会立即清理,依旧会对该微服务的信息进行保存;
属于 CAP理论 中的 AP!
1、什么是自我保护模式?
默认情况下,如果EurekaServer在一定时间内没有收到摸个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。
但是当网络分区故障发生时(延时、卡顿、拥挤)时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险——因为微服务本身其实是健康的,此时本不应该注销这个微服务。
Eureka通过“自我保护模式”来解决这个问题——当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式!

2、如何禁用Eureka的自我保护?
Eureka默认是开启了自我保护的,但是我们也有方式将其关闭:以cloud-eureka-server7001 和 cloud-provider-payment8001为例:
修改cloud-eureka-server的配置文件application.yml,增加:
eureka: server: #关闭Eureka的自我保护机制,保证不可用服务被立即剔除 enable-self-preservation: false eviction-interval-timer-in-ms: 2000
修改cloud-provider-payment8001的配置文件application.yml:
eureka: instance: #Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认为30秒) lease-renewal-interval-in-seconds: 1 #Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认为90秒) lease-expiration-duration-in-seconds: 2
再次测试,Eureka上面的提示信息已经改变,而且,只要8001服务一停,EurekaServer上的服务列表,在2秒后就立即将payment8001服务剔除了!
测试关闭Eureka自我保护模式成功!
个人此项目代码地址(持续更新):



