问题:微服务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自我保护模式成功!
个人此项目代码地址(持续更新):