Spring Cloud 封装了 Netflix 公司开发的 Eureka 模块来 实现服务治理
在传统的rpc远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。
Eureka采用了CS的设计架构,Eureka Server 作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用 Eureka的客户端连接到 Eureka Server并维持心跳连接。这样系统的维护人员就可以通过 Eureka Server 来监控系统中各个微服务是否正常运行。
在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息 比如 服务地址通讯地址等以别名方式注册到注册中心上。另一方(消费者|服务提供者),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现k本地RPC调用RPC远程调用框架核心设计思想:在于注册中心,因为使用注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。在任何rpc远程框架中,都会有一个注册中心(存放服务地址相关信息(接口地址))
下左图是Eureka系统架构,右图是Dubbo的架构,请对比
2.1.3 Eureka两组件Eureka包含两个组件:Eureka Server和Eureka Client
Eureka Server提供服务注册服务
各个微服务节点通过配置启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
EurekaClient通过注册中心进行访问
是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)
- 建Module—cloud-eureka-server7001
- 改POM
以前的老版本(当前使用2018)org.springframework.cloud spring-cloud-starter-eurekaorg.springframework.cloud spring-cloud-starter-netflix-eureka-server
- 写YML
server: port: 7001 eureka: instance: hostname: localhost #eureka服务端的实例名称 client: #false表示不向注册中心注册自己。 register-with-eureka: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务 fetch-registry: false service-url: #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
- 主启动
@SpringBootApplication @EnableEurekaServer //这个是Eureka的Server.. public class EurekaMain7001 { public static void main(String[] args) { SpringApplication.run(EurekaMain7001.class, args); } }
- 测试
访问 http://localhost:7001/, 显示:No application available 没有服务被发现. O(∩_∩)O .
因为没有注册服务进来当然不可能有服务被发现!
2.2.2 EurekaClient端cloud-provider-payment8001将注册进EurekaServer成为服务提供者provider,类似尚硅谷学校对外提供授课服务
- 建Module—cloud-provider-payment8001
- 改POM
cloud2021 com.rg.springcloud 1.0-SNAPSHOT 4.0.0 cloud-provider-payment8001org.springframework.cloud spring-cloud-starter-netflix-eureka-clientcom.rg.springcloud cloud-api-commons${project.version} org.springframework.boot spring-boot-starter-weborg.springframework.boot spring-boot-starter-actuatororg.mybatis.spring.boot mybatis-spring-boot-startercom.alibaba druid-spring-boot-starter1.1.17 mysql mysql-connector-javaorg.springframework.boot spring-boot-starter-jdbcorg.springframework.boot spring-boot-devtoolsruntime true org.projectlombok lomboktrue org.springframework.boot spring-boot-starter-testtest
- 写YML
server: port: 8001 spring: application: name: cloud-payment-service datasource: type: com.alibaba.druid.pool.DruidDataSource # 当前数据源 *** 作类型 driver-class-name: org.gjt.mm.mysql.Driver # mysql驱动包 url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: 123456 eureka: client: #表示是否将自己注册进EurekaServer默认为true。 register-with-eureka: true #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡 fetchRegistry: true service-url: defaultZone: http://localhost:7001/eureka mybatis: mapperLocations: classpath:mapper/*.xml type-aliases-package: com.rg.springcloud.pojo # 所有Entity别名类所在包
- 主启动
@SpringBootApplication @EnableEurekaClient public class PaymentMain8001 { public static void main(String[] args) { SpringApplication.run(PaymentMain8001.class,args); } }
- 测试
先要启动Eureka Server,再访问 http://localhost:7001/ ,可以看到有Payment服务已经被注册到Eureka7001中了
微服务注册名配置说明:
自我保护机制:
2.2.3 Eureka Client端cloud-consumer-order80将注册进EurekaServer成为服务消费者consumer,类似来尚硅谷上课消费的各位同学
- 建Module—cloud-consumer-order80
- POM
cloud2021 com.rg.springcloud 1.0-SNAPSHOT 4.0.0 cloud-consumer-order80org.springframework.cloud spring-cloud-starter-netflix-eureka-clientcom.rg.springcloud cloud-api-commons${project.version} org.springframework.boot spring-boot-starter-weborg.springframework.boot spring-boot-starter-actuatororg.springframework.boot spring-boot-devtoolsruntime true org.projectlombok lomboktrue org.springframework.boot spring-boot-starter-testtest
- YML
server: port: 80 spring: application: name: cloud-order-service eureka: client: #表示是否将自己注册进EurekaServer默认为true。 register-with-eureka: true #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡 fetchRegistry: true service-url: defaultZone: http://localhost:7001/eureka
- 主启动
@SpringBootApplication @EnableEurekaClient public class OrderMain80 { public static void main(String[] args) { SpringApplication.run(OrderMain80.class,args); } }
- 测试
先要启动Eureka7001服务,再启动服务提供者provider8001服务
**一个小bug:**Failed to bind properties under ‘eureka.client.service-url’ to java.util.Map
问题:微服务RPC远程服务调用最核心的是什么 ?
高可用,试想你的注册中心只有一个only one, 它出故障了那就呵呵( ̄▽ ̄)"了,会导致整个为服务环境不可用,所以
解决办法:搭建Eureka注册中心集群 ,实现负载均衡+故障容错
2.3.2 EurekaServer集群环境构建步骤- 建Module
参考cloud-eureka-server7001,新建cloud-eureka-server7002,7004 (不要问我为啥是4不是3,建的次数多了,出了点小虫子…)
- 改POM
org.springframework.cloud spring-cloud-starter-netflix-eureka-servercom.rg.springcloud cloud-api-commons${project.version} org.springframework.boot spring-boot-starter-weborg.springframework.boot spring-boot-starter-actuatororg.springframework.boot spring-boot-devtoolsruntime true org.projectlombok lombokorg.springframework.boot spring-boot-starter-testtest junit junit
- 修改映射配置
找到C:WindowsSystem32driversetc路径下的hosts文件,打开并添加映射配置添加进hosts文件.
修改完成后,刷新DNS缓存
- 写YML
以下是Eureka7001的yaml文件,Eureka7002,7003类似,只不过port和url需要做点修改.
server: port: 7001 eureka: instance: hostname: eureka7001.com #eureka服务端的实例名称 client: register-with-eureka: false #false表示不向注册中心注册自己。 fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务 service-url: defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7004.com:7004/eureka/
- 主启动
省略…
- 启动Eureka7001,7002,7004进行 测试
server: port: 8001 spring: application: name: cloud-payment-service datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/db2021?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: 186259 eureka: client: #表示是否将自己注册进EurekaServer默认为true。 register-with-eureka: true #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡 fetchRegistry: true service-url: # defaultZone: http://localhost:7001/eureka defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7004.com:7004/eureka # 集群版 mybatis: mapperLocations: classpath:mapper/*.xml # 配置Mapper所有接口配置文件的位置. type-aliases-package: com.rg.springcloud.pojo # 所有Entity别名类所在包2.3.4 将订单服务80微服务发布到上面3台Eureka集群配置中
server: port: 80 spring: application: name: cloud-order-service eureka: client: #表示是否将自己注册进EurekaServer默认为true。 register-with-eureka: true #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡 fetchRegistry: true service-url: #defaultZone: http://localhost:7001/eureka defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7004.com:7004/eureka # 集群版2.3.5 测试01
1.先要启动EurekaServer,7001/7002服务
2.再要启动服务提供者provider,8001
3.再要启动消费者,80
测试结果:
访问: http://localhost/consumer/payment/get/1
2.3.6 支付服务提供者8001集群环境构建- 建Module
参考cloud-provider-payment8001,建立cloud-provider-payment8002
- 改POM
- 写YML
- 主启动
- 业务类
- 修改8001/8002的Controller
1.先要启动EurekaServer,7001/7002/7004服务
2.再要启动服务提供者provider,8001/8002服务
3.访问 http://localhost/consumer/payment/get/1
- 访问结果
原因:订单服务访问地址不能写死,否则无法进行负载均衡的调用,修改OrderController上的访问地址
**再次进行测试:**依旧报错…
- @LoadBalanced注解赋予RestTemplate负载均衡的能力
再进行测试:成功!!!
2.3.8 对比官方给出的Eureka示意图 2.4 actuator微服务信息完善 2.4.1 主机名称:服务名称修改当前问题:含有主机名称.
修改cloud-provider-payment8001的yaml,添加instance配置
eureka: instance: instance-id: payment80012.4.2 访问信息有IP信息提示
当前问题:没有IP提示
修改cloud-provider-payment8001的yaml
eureka: instance: instance-id: payment8001 prefer-ip-address: true #访问路径可以显示IP地址2.5 服务发现Discovery
对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息
- 修改cloud-provider-payment8001的Controller
@Slf4j @RestController public class PaymentController { //... @Resource private DiscoveryClient discoveryClient; //... @GetMapping(value = "/payment/discovery") public Object discovery(){ Listservices = discoveryClient.getServices();//获取Eureka上所有的服务 for (String service : services) { log.info("*****service:"+service); } //获得某一个服务所有的实例 List 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; } }
- 为8001主启动类加上**@EnableDiscoveryClient**
@SpringBootApplication @EnableEurekaClient @EnableDiscoveryClient //服务发现 public class PaymentMain8001 { public static void main(String[] args) { SpringApplication.run(PaymentMain8001.class,args); } }
- 测试
先要启动EurekaServer,再启动8001主启动类,需要稍等一会儿,输入 http://localhost:8001/payment/discovery
2.6 Eureka自我保护 2.6.1 故障现象概述
保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护。一旦进入保护模式,
Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据,也就是不会注销任何微服务。
如果在Eureka Server的首页看到以下这段提示,则说明Eureka进入了保护模式:
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT.
RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE
为什么会产生Eureka自我保护机制?
为了防止EurekaClient可以正常运行,但是 与 EurekaServer网络不通情况下,EurekaServer不会立刻将EurekaClient服务剔除
什么是自我保护模式?
默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。但是当网络分区故障发生(延时、卡顿、拥挤)时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。Eureka通过“自我保护模式”来解决这个问题——当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。
在自我保护模式中,Eureka Server会保护服务注册表中的信息,不再注销任何服务实例
它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。一句话讲解:好死不如赖活着
综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留)也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。
一句话:某时刻某一个微服务不可用了,Eureka不会立刻清理,依旧会对该微服务的信息进行保存
属于CAP里面的AP分支
2.6.3 怎么禁止自我保护 ?注册中心eureakeServer端7001:
- 出厂默认,自我保护机制是开启的
eureka.server.enable-self-preservation=true
- 使用eureka.server.enable-self-preservation = false 可以禁用自我保护模式
eureka: server: #关闭自我保护机制,保证不可用服务被及时踢除 enable-self-preservation: false
生产者客户端eureakeClient端8001:
- 默认
eureka.instance.lease-renewal-interval-in-seconds=30 eureka.instance.lease-expiration-duration-in-seconds=90
- 修改配置
eureka: #心跳检测与续约时间 #开发时设置小些,保证服务关闭后注册中心能即使剔除服务 instance: #Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒) lease-renewal-interval-in-seconds: 1 #Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务 lease-expiration-duration-in-seconds: 2
测试:
7001和8001都配置完成,先启动7001,7002再启动8001,8002
2.7 Eureka停止更新了你怎么办?https://github.com/Netflix/eureka/wiki
3. Zookeeper服务注册与发现 3.1 SpringCloud整合Zookeeper代替Eureka 3.1.1 注册中心Zookeeper- zookeeper是一个分布式协调工具,可以实现注册中心功能
- 关闭Linux服务器防火墙后启动zookeeper服务器
- zookeeper服务器取代Eureka服务器,zk作为服务注册中心
-
建Module—cloud-provider-payment8004
-
改POM
org.springframework.boot spring-boot-starter-webcom.rg.springcloud cloud-api-commons${project.version} org.springframework.cloud spring-cloud-starter-zookeeper-discoveryorg.springframework.boot spring-boot-devtoolsruntime true org.projectlombok lomboktrue org.springframework.boot spring-boot-starter-testtest
- 建YML
#8004表示注册到zookeeper服务器的支付服务提供者端口号 server: port: 8004 #服务别名----注册zookeeper到注册中心名称 spring: application: name: cloud-provider-payment cloud: zookeeper: connect-string: 192.168.174.128:2181
- 主启动类
@SpringBootApplication @EnableDiscoveryClient //该注解用于向使用consul或者zookeeper作为注册中心时注册服务 public class PaymentMain8004 { public static void main(String[] args) { SpringApplication.run(PaymentMain8004.class, args); } }
- Controller
@RestController 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(); } }
- 启动8004注册进zookeeper
如果Linux下使用的zookeeper版本低于3.5.3, 则可能出现以下错误
原因在于SpringBoot2.2自带的zookeeper版本仲裁是3.5.3,而如果你使用的是zookeeper3.5.3以下的就会出现版本不兼容问题,大于3.5.3没问题.
解决zookeeper版本jar包冲突问题—排出zk冲突后的新POM
org.springframework.cloud spring-cloud-starter-zookeeper-discoveryorg.apache.zookeeper zookeeperorg.apache.zookeeper zookeeper3.4.9
- 验证测试
浏览器输入:http://localhost:8004/payment/zk
获得json串后用在线工具查看
思考:服务节点是临时节点还是持久节点???
3.1.3 服务消费者- 建Module—cloud-consumerzk-order80
- POM
org.springframework.boot spring-boot-starter-weborg.springframework.cloud spring-cloud-starter-zookeeper-discoveryorg.springframework.boot spring-boot-devtoolsruntime true org.projectlombok lomboktrue org.springframework.boot spring-boot-starter-testtest
- YML
server: port: 80 spring: application: name: cloud-consumer-order cloud: #注册到zookeeper地址 zookeeper: connect-string: 192.168.174.128:2181
- 主启动
@SpringBootApplication public class OrderZkMain80 { public static void main(String[] args) { SpringApplication.run(OrderZkMain80.class, args); } }
- 业务类
配置Bean:
@Configuration public class ApplicationContextConfig { @Bean @LoadBalanced //负载均衡 public RestTemplate getRestTemplate(){ return new RestTemplate(); } }
controller
@RestController public class OrderZKController { public static final String INVOKE_URL = "http://cloud-provider-payment"; @Resource public RestTemplate restTemplate; @RequestMapping(value = "/consumer/payment/zk") public String paymentInfo(){ String result = restTemplate.getForObject(INVOKE_URL + "/payment/zk", String.class); System.out.println("消费者调用支付服务(zookeeper)--->result:"+result); return result; } }
- 验证测试
**思路:**需要在每台zookeeper上注册服务就可,然后消费者需要服务时,根据负载均衡轮训获取服务.每个zookeeper之间应该也需要相互注册.
具体 *** 作等用到了再完善.
4.Consul服务注册与发现 4.1 Consul简介 4.1.1 是什么?https://www.consul.io/intro/index.html
4.1.2 能干嘛?- 服务发现
提供HTTP和DNS两种发现方式。
- 健康监测
支持多种方式,HTTP、TCP、Docker、Shell脚本定制化监控
- KV存储
Key、Value的存储方式
- 多数据中心
Consul支持多数据中心
- 可视化Web界面
https://www.consul.io/downloads.html
4.1.4 怎么玩?https://www.springcloud.cc/spring-cloud-consul.html
4.2 安装并运行Consul- 官网安装说明
https://learn.hashicorp.com/consul/getting-started/install.html
Linux下使用yum -y install consul 进行安装
- 下载完成后只有一个consul.exe文件,windows路径下双击运行.
- 使用开发模式启动
windows:consul agent -dev
Linux:consul agent -dev -ui -node=consul-dev -client=主机IP
- 验证是否安装成功
通过以下地址可以访问Consul的首页:http://localhost:8500
4.3 服务提供者- 建Module—cloud-providerconsul-payment8006
- POM
org.springframework.cloud spring-cloud-starter-consul-discoveryorg.springframework.boot spring-boot-starter-weborg.springframework.boot spring-boot-starter-actuatororg.springframework.boot spring-boot-devtoolsruntime true org.projectlombok lomboktrue org.springframework.boot spring-boot-starter-testtest
- YML
###consul服务端口号 server: port: 8006 spring: application: name: consul-provider-payment ####consul注册中心地址 cloud: consul: host: 192.168.174.128 # 用linux的IP地址 port: 8500 discovery: #hostname: 127.0.0.1 配置当前项目所在的主机IP, service-name: ${spring.application.name} heartbeat: # 如果心跳不打开,则服务上面可能出现一个红叉 enabled: true
- 主启动
@SpringBootApplication @EnableDiscoveryClient public class ConsulPaymentMain8006 { public static void main(String[] args) { SpringApplication.run(ConsulPaymentMain8006.class, args); } }
- 业务类Controller
@RestController public class PaymentController { @Value("${server.port}") private String serverPort; @RequestMapping(value = "/payment/consul") public String paymentConsul(){ return "springcloud with consul: "+serverPort+"t"+ UUID.randomUUID().toString(); } }
- 验证测试
- 建Module—新建Module消费服务order80
- POM
org.springframework.cloud spring-cloud-starter-consul-discoveryorg.springframework.boot spring-boot-starter-weborg.springframework.boot spring-boot-starter-actuatororg.springframework.boot spring-boot-devtoolsruntime true org.projectlombok lomboktrue org.springframework.boot spring-boot-starter-testtest
- YML
###consul服务端口号 server: port: 80 spring: application: name: cloud-consumer-order ####consul注册中心地址 cloud: consul: host: 192.168.174.128 port: 8500 discovery: #hostname: 127.0.0.1 service-name: ${spring.application.name} heartbeat: enabled: true
- 主启动类
@SpringBootApplication @EnableDiscoveryClient public class ConsulOrderMain80 { public static void main(String[] args) { SpringApplication.run(ConsulOrderMain80.class, args); } }
- 配置Bean
@Configuration public class ApplicationContextConfig { //向SpringBoot容器中加入该组件 @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } }
- Controller
@RestController @Slf4j public class OrderController { public static final String INVOKE_URL = "http://consul-provider-payment"; @Resource public RestTemplate restTemplate; @RequestMapping(value = "/consumer/payment/consul") public String paymentInfo(){ String result = restTemplate.getForObject(INVOKE_URL + "/payment/consul", String.class); System.out.println("消费者调用支付服务(consul)--->result:"+result); return result; } }
- 验证测试
-
C:Consistency(强一致性)
-
A:Availability(可用性)
-
P:Partition tolerance(分区容错性)
CAP理论关注粒度是数据,而不是整体系统设计的策略
最多只能同时较好的满足两个。
CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求, 因此,根据 CAP 原理分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三 大类:
CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。
CP - 满足一致性,分区容忍必的系统,通常性能不是特别高。
AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。
- AP(Eureka)
当网络分区出现后,为了保证可用性,系统B可以返回旧值,保证系统的可用性。
结论:违背了一致性C的要求,只满足可用性和分区容错,即AP
- CP(Zookeeper/Consul)
当网络分区出现后,为了保证一致性,就必须拒接请求,否则无法保证一致性
结论:违背了可用性A的要求,只满足一致性和分区容错,即CP
总结
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)