1.eurekaB站尚硅谷P15~P35
代码Gitee地址
虽然eureka已经过去了,但仍然需要学习。
1.1 概念传统的rpc远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,所以需要使用服务器治理,管理服务与服务之间的依赖关系。可以实现服务调用、负载均衡、容错等,实现服务发现于注册。
eureka就是Spring Cloud 封装了 Netflix 公司的eureka来实现服务治理。
eureka采用CS架构设计,作为服务注册功能的服务器,它是服务注册中心。系统中的其它微服务使用eureka的客户端连接到eureka server 并维持心跳连接。这样系统的维护人员就可以通过eureka server 来监控系统各个微服务是否正常运行。
在服务注册与发现中,有一个注册中心。当服务器启动的时候,会以别名的形式把当前自己服务器的信息注册到注册中心上。另一方可以去注册中心获取到实际的服务通讯地址,然后再实现本地RPC调用RPC远程调用框架核心设计思想:在注册中心上,因为注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。在任何RPC远程框架中,都会有一个注册中心存放服务地址信息。
- Eureka Server:提供服务注册
各个微服务节点通过配置启动后,会在Eureka Server中进行注册,这样Eureka Server中的服务注册表将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。 - Eureka Client:通过注册中心访问
是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询的负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认30秒),如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。
-
新建maven的module:cloud-eureka-server7001
-
加入依赖
org.example cloud-api-commons 1.0-SNAPSHOT org.springframework.cloud spring-cloud-starter-netflix-eureka-server org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-starter-test test junit junit org.springframework.boot spring-boot-devtools runtime true -
新增yml配置文件:
server: port: 7001 eureka: instance: hostname: localhost # 实例名称 client: register-with-eureka: false # 不向注册中心自己 fetch-registry: false # 不检索自己 service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ # 配置地址- 新建启动类
@SpringBootApplication @EnableEurekaServer public class EurekaMain7001 { public static void main(String[] args) { SpringApplication.run(EurekaMain7001.class,args); } } -
启动测试:
-
切换到cloud-provider-payment8001模块。
-
加入依赖:
org.springframework.cloud spring-cloud-starter-netflix-eureka-client -
新增yaml配置
eureka: client: register-with-eureka: true # 将自己注册进eurekaServer fetch-registry: true # 从eurekaServer抓取注册信息,多节点必须开启才能配合ribbon service-url: defaultZone: http://localhost:7001/eureka -
修改主启动类:
@SpringBootApplication @EnableEurekaClient public class PaymentMain8001 { public static void main(String[] args) { SpringApplication.run(PaymentMain8001.class,args); } } -
先启动注册中心,在启动当前模块,可以看到注册成功。
操作流程与上方相同。
-
切换到cloud-consumer-order80模块。
-
新增依赖:
org.springframework.cloud spring-cloud-starter-netflix-eureka-client -
修改yaml
spring: application: name: cloud-order-service eureka: client: register-with-eureka: true # 将自己注册进eurekaServer fetch-registry: true # 从eurekaServer抓取注册信息,多节点必须开启才能配合ribbon service-url: defaultZone: http://localhost:7001/eureka -
修改主启动类
@SpringBootApplication @EnableEurekaClient public class OrderMain80 { public static void main(String[] args) { SpringApplication.run(OrderMain80.class,args); } } -
启动测试:
微服务RPC远程调用最核心的就是高可用,如果只有一个注册中心,如果它出现故障会导致整个服务环境直接挂掉。
- 注册原理:互相注册,相互守望。,即多个eureka server之间是相互注册的,当前eureka server中需要有其它所有的eureka server信息。
-
以单机方式新建一个module:cloud-eureka-server7002,依赖,启动类也相同。
-
修改本机的hosts文件:C:WindowsSystem32driversetc
## cloud-22-06 ## 127.0.0.1 eureka7001.com 127.0.0.1 eureka7002.com
-
修改7001的yml配置文件:
server: port: 7001 eureka: instance: hostname: eureka7001.com # 实例名称 client: register-with-eureka: false # 不向注册中心自己 fetch-registry: false # 不检索自己 service-url: defaultZone: http://eureka7002.com:7002/eureka/ # 注入7002 -
修改7002的yml配置文件:
server: port: 7002 eureka: instance: hostname: eureka7002.com # 实例名称 client: register-with-eureka: false # 不向注册中心自己 fetch-registry: false # 不检索自己 service-url: defaultZone: http://eureka7001.com:7001/eureka/ # 注入7001 -
分别启动两个eureka测试:
通过映射的域名+端口访问,可以看到其它的注册中心。
分别修改两配置文件中的defaultZone:
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7001.com:7001/eureka/, # 注入7002 和 7001
- 查看两个eureka中注册情况。
-
新建module:cloud-provider-payment8002,除了配置文件的端口是8002之外其余都相同(名字都是将8001改为8002)。
-
修改8001和8002的controller:
@Slf4j @RestController @RequestMapping("/payment") public class PaymentController { @Autowired private PaymentService paymentService; @Value("${server.port}") private String serverPort; @PostMapping("/create") public CommonResult create(@RequestBody Payment payment){ boolean result = paymentService.create(payment) > 0; log.info("插入结果:" + result); return result ? new CommonResult(200,"插入成功,端口:" + serverPort): new CommonResult(444,"插入失败"); } @GetMapping("/{id}") public CommonResult create(@PathVariable("id") Long id){ Payment payment = paymentService.getPaymentById(id); log.info("查询结果:" + payment); return !Objects.isNull(payment) ? new CommonResult(200,"查询成功,端口:" + serverPort,payment): new CommonResult(444,"没有当前id=" + id + "对应数据"); } } -
搭建完成
根据上方的集群可以发现,使用80端口访问时,只能访问到8001服务。
-
修改cloud-consumer-order80的ApplicationContextConfig:
@Configuration public class ApplicationContextConfig { @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } } -
修改cloud-consumer-order80的controller:
@Slf4j @RestController @RequestMapping("/consumer") public class OrderController { private static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE"; @Autowired private RestTemplate restTemplate; @PostMapping("/payment/create") public CommonResult create(@RequestBody Payment payment){ return restTemplate.postForObject(PAYMENT_URL+"/payment/create" , payment , CommonResult.class); } @GetMapping("/payment/{id}") public CommonResult getPaymentById(@PathVariable("id") Long id){ return restTemplate.getForObject(PAYMENT_URL+"/payment/" + id , CommonResult.class); } } -
测试:可以看出两个端口轮询出现。
-
修改8001和8002的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 url: jdbc:mysql://localhost:3306/jm-cloud?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: root eureka: client: register-with-eureka: true # 将自己注册进eurekaServer fetch-registry: true # 从eurekaServer抓取注册信息,多节点必须开启才能配合ribbon service-url: # defaultZone: http://localhost:7001/eureka, defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7001.com:7001/eureka/, # 注入7002 和 7001 instance: instance-id: payment8001 mybatis: mapper-locations: classpath:mapper @PostMapping("/create") public CommonResult create(@RequestBody Payment payment){ boolean result = paymentService.create(payment) > 0; log.info("插入结果:" + result); return result ? new CommonResult(200,"插入成功,端口:" + serverPort): new CommonResult(444,"插入失败"); } @GetMapping("/{id}") public CommonResult create(@PathVariable("id") Long id){ Payment payment = paymentService.getPaymentById(id); log.info("查询结果:" + payment); return !Objects.isNull(payment) ? new CommonResult(200,"查询成功,端口:" + serverPort,payment): new CommonResult(444,"没有当前id=" + id + "对应数据"); } @GetMapping("/discovery") public Object discovery() { List services = discoveryClient.getServices(); services.forEach(System.out::println); List instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE"); instances.forEach(instance -> { System.out.println(instance.getServiceId() + "t" + instance.getHost() + "t" + instance.getPort() + "t" + instance.getUri()); }); return this.discoveryClient; } } -
修改启动类:
@SpringBootApplication @EnableEurekaClient @EnableDiscoveryClient public class PaymentMain8001 { public static void main(String[] args) { SpringApplication.run(PaymentMain8001.class,args); } } -
访问测试:
保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护。一旦进入保护模式Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册中的数据,即某时刻某个微服务不能用了,Eureka不会立即清理,依然会对该服务信息进行保存。
出现图中的红字表示进入模式。
1.14 禁用eureka自我保护
宁可保留错误的,也不会盲目的注销任何可能健康的服务实例。可以让eureka集群更加的健壮、稳定。
-
修改7001和7002的yml配置文件:
eureka: server: enable-self-preservation: false # 关闭自我保护机制 eviction-interval-timer-in-ms: 2000 # 检测时间 -
启动查看:表示已经关闭自我保护。
-
修改8001配置文件:
eureka: instance: lease-renewal-interval-in-seconds: 1 # 向客户端发送心跳的时间间隔。 lease-expiration-duration-in-seconds: 1 # 客户端等待收到最后一次心跳的时间上限。- 配置之后,当8001出现故障时马上就会被eureka客户端剔除。
2.1 使用docker安装zookeeper由于eureka2.0版本胎死腹中,且1.0不再更新,因此需要更多的来替代它。
-
拉镜像
docker pull zookeeper3.4.9
-
运行
docker run --name zookeeper -p 2181:2181 --restart always -d zookeeper:3.4.9
-
查看docker ps
-
新建服务提供者:cloud-provider-payment8003作为注册到zookeeper中的服务提供者。
-
修改pom:
org.example cloud-api-commons 1.0-SNAPSHOT org.springframework.cloud spring-cloud-starter-zookeeper-discovery org.apache.zookeeper zookeeper org.apache.zookeeper zookeeper 3.4.9 org.slf4j slf4j-log4j12 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.mybatis.spring.boot mybatis-spring-boot-starter mysql mysql-connector-java org.springframework.boot spring-boot-starter-jdbc org.springframework.boot spring-boot-starter-test test com.alibaba druid-spring-boot-starter 1.2.9 org.springframework.boot spring-boot-devtools runtime true -
以同样的方式将8001中的dao、controller、service、mapper.xml复制过来。
-
主启动类:
@SpringBootApplication @EnableDiscoveryClient // 向zookeeper或者consul注入 public class PaymentMain8003 { public static void main(String[] args) { SpringApplication.run(PaymentMain8003.class,args); } } -
修改yml
server: port: 8003 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/jm-cloud?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: root cloud: zookeeper: connect-string: ip:2181 mybatis: mapper-locations: classpath:mapper @PostMapping("/create") public CommonResult create(@RequestBody Payment payment){ boolean result = paymentService.create(payment) > 0; log.info("插入结果:" + result); return result ? new CommonResult(200,"插入成功,端口:" + serverPort): new CommonResult(444,"插入失败"); } @GetMapping("/{id}") public CommonResult create(@PathVariable("id") Long id){ Payment payment = paymentService.getPaymentById(id); log.info("查询结果:" + payment); return !Objects.isNull(payment) ? new CommonResult(200,"查询成功,端口:" + serverPort,payment): new CommonResult(444,"没有当前id=" + id + "对应数据"); } @GetMapping("/zk") public CommonResult paymentZK(){ return new CommonResult (200, serverPort + "t" + UUID.randomUUID().toString()); } } -
启动测试:在服务器中使用docker命令:
- 进入zookeeper
docker exec -it 容器id /bin/bash
- 进入zookeeper客户端
zkCli.sh -server 127.0.0.1:2181
- 查看运行的服务
ls /services
- 可以看到刚才启动的服务
-
新建module:cloud-consume-zk-order80,将原先80模块代码复制过来。
-
修改pom:
org.example cloud-api-commons 1.0-SNAPSHOT org.springframework.cloud spring-cloud-starter-zookeeper-discovery org.apache.zookeeper zookeeper org.apache.zookeeper zookeeper 3.4.9 org.slf4j slf4j-log4j12 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-devtools runtime true -
修改配置文件:
server: port: 80 spring: application: name: cloud-order-service cloud: zookeeper: connect-string: 8.140.23.138:2181 -
启动测试:可以看到了。
无论是注入到zookeeper中的服务者还是消费者都无法调用dao中的接口,总是说找不到方法,但是测试可以通过。不晓得啥问题。
3. consul一套分布式服务发现和配置管理系统,是HashiCorp公司用Go语言开发。
提供了微服务系统的服务治理、配置中心、控制总线等功能。每一个功能都可以根据需要单独使用,也可以一起使用以构建全方位的服务网格。特点:
- 服务发现:提供HTTP和DNS两种发现方式;
- 健康检测:支持多种方式,HTTP、TCP、Docker、Sheel脚本定制化;
- KV存储:类似于redis的k、v存储方式;
- 多数据中心:consul支持多数据中心;
- 可视化Web界面。
- 下载:consul官网,这里使用的是1.6.1版本。
- 配置环境变量到exe的父文件夹。
- 在cmd下运行consul agent -dev
- 访问测试:可以看到ui就可以了。
-
新建moudl:cloud-provider-consul-payment8006
-
pom:
org.example cloud-api-commons 1.0-SNAPSHOT org.springframework.cloud spring-cloud-starter-consul-discovery org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-starter-test test junit junit org.springframework.boot spring-boot-devtools runtime true -
配置yml:
server: port: 8006 spring: application: name: consul-provider-payment cloud: consul: host: localhost port: 8500 discovery: server-name: ${spring.application.name} -
主启动类
@SpringBootApplication @EnableDiscoveryClient public class PaymentMainConsul8006 { public static void main(String[] args) { SpringApplication.run(PaymentMainConsul8006.class,args); } } -
测试controller
@Slf4j @RestController @RequestMapping("/payment") public class PaymentConsulController { @Value("${server.port}") private String serverPort; @GetMapping("/consul") public String paymentConsul(){ return serverPort + "t" + UUID.randomUUID().toString(); } } -
启动测试:可以看到刚才启动的服务。
访问controller也可以看到对应接口情况。
-
新建moudle: cloud-consumer-consul-order80
-
pom:
org.springframework.cloud spring-cloud-starter-consul-discovery org.example cloud-api-commons 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test -
配置文件:
server: port: 80 spring: application: name: consul-consumer-order cloud: consul: host: localhost port: 8500 discovery: service-name: ${spring.application.name} -
restTemplate
@Configuration public class ApplicationContextConfig { @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } } -
主启动类
@SpringBootApplication @EnableDiscoveryClient public class OrderMainConsul80 { public static void main(String[] args) { SpringApplication.run(OrderMainConsul80.class,args); } } -
测试接口
@Slf4j @RestController @RequestMapping("/consumer") public class OrderController { private static final String PAYMENT_URL = "http://consul-provider-payment"; @Autowired private RestTemplate restTemplate; @GetMapping("/payment/consul") public String paymentConsul(){ return restTemplate.getForObject(PAYMENT_URL+"/payment/consul" , String.class); } } -
启动测试:可以看到新启动的服务,接口也可以访问成功。
CAP:
- C(consistdency):强一致性;
- A(Availability):高可用性;
- P(Partititon tolerance):分区容错性;
最多只能同时较好的满住两个。
CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性三个需求。
根据CAP原理将NoSQL数据库分成满足CA、CP和AP原则的三大类:- CA:单点集群,满足一致性,可用性的系统,通常在可扩展性上不强大;
- CP:满足一致性,分区容错性的系统,性能不是特别高;
- AP:满足可用性,分区容错性的系统,对一致性的要求较低。
-



