尚硅谷周阳构建分布式微服务架构(Eureka篇)
B 站视频:https://www.bilibili.com/video/BV18E411x7eT?from=search&seid=13583080742381804215
原 Blog 笔记文档:https://blog.csdn.net/qq_41211642/article/details/104772140
本篇文章大部分图片用到了参考的 blog 笔记中的图片。
本人代码下载:下载
本人代码 Gitee 仓库地址:https://gitee.com/Sevattal/springcloud_project_eureka
技术 | 版本 |
---|---|
cloud | Hoxton.SR1 |
boot | 2.2.2.RELEASE |
cloud alibaba | 2.1.0.RELEASE |
java | java8 |
Maven | 3.5及以上 |
Mysql | 5.7及以上 |
整体工程项目架构
springcloud2021 父项目
cloud-api-commons 通用类项目
cloud-provider-payment8001 模拟支付接收接口项目
cloud-provider-payment8002 模拟支付接收接口项目(复制的cloud-provider-payment8001)
cloud-consumer-order80 模拟客户端调用后端接口项目
cloud-eureka-server7001 eureka注册中心项目
cloud-eureka-server7002 eureka注册中心项目 (复制的cloud-eureka-server7001)
Eureka url命令的补充知识点
Operation | HTTP action | Description |
---|---|---|
Register new application instance | POST /eureka/v2/apps/appID | Input: JSON/XML payload HTTP Code: 204 on success |
De-register application instance | DELETE /eureka/v2/apps/appID/instanceID | HTTP Code: 200 on success |
Send application instance heartbeat | PUT /eureka/v2/apps/appID/instanceID | HTTP Code:* 200 on success* 404 if instanceID doesn’t exist |
Query for all instances | GET /eureka/v2/apps | HTTP Code: 200 on success Output: JSON/XML |
Query for all appID instances | GET /eureka/v2/apps/appID | HTTP Code: 200 on success Output: JSON/XML |
Query for a specific appID/instanceID | GET /eureka/v2/apps/appID/instanceID | HTTP Code: 200 on success Output: JSON/XML |
Query for a specific instanceID | GET /eureka/v2/instances/instanceID | HTTP Code: 200 on success Output: JSON/XML |
Take instance out of service | PUT /eureka/v2/apps/appID/instanceID/status?value=OUT_OF_SERVICE | HTTP Code:* 200 on success* 500 on failure |
Move instance back into service (remove override) | DELETE /eureka/v2/apps/appID/instanceID/status?value=UP | (The value=UP is optional, it is used as a suggestion for the fallback status due to removal of the override) HTTP Code:* 200 on success* 500 on failure |
Update metadata | PUT /eureka/v2/apps/appID/instanceID/metadata?key=value | HTTP Code:* 200 on success* 500 on failure |
Query for all instances under a particular vip address | GET /eureka/v2/vips/vipAddress | * HTTP Code: 200 on success Output: JSON/XML * 404 if the vipAddress does not exist. |
Query for all instances under a particular secure vip address | GET /eureka/v2/svips/svipAddress | * HTTP Code: 200 on success Output: JSON/XML * 404 if the svipAddress does not exist. |
1. 新建Maven父工程 springcloud2021
1.1 maven架构不需要选择
1.2 pom.xml代码如下
1 | <?xml version="1.0" encoding="UTF-8"?> |
2. 建立支付 module: cloud-provider-payment8001
2.1 在 springcloud2021 主项目下建立 cloud-provider-payment8001 模块(maven)
2.2 pom.xml如下:
1 | <?xml version="1.0" encoding="UTF-8"?> |
2.3 application.yml
1 | server: |
2.4 主启动类PaymentMain8001
1 | package com.sevattal.springcloud; |
2.5 数据库
1 | CREATE TABLE `payment`( |
2.6 业务类
1 | // 主实体类 Payment |
1 | //Json封装体CommentResult,传给前端,判断编码是否成功,成功才显示 |
1 | //PaymentDao |
1 | <?xml version="1.0" encoding="UTF-8" ?> |
1 | //PaymentService |
1 | //PaymentServiceImpl |
1 | //PaymentController |
2.7 测试
Chrome 浏览器可能不支持 Post 请求,可以使用 PostMan 工具测试
总结: 1. 建 module 2. 改 pom 3. 写 yml 4. 主启动 5. 业务类
3. 建立消费者订单 module:
3.1 在 springcloud2021 主项目下建立 cloud-consumer-order80 模块(maven)
3.2 pom.xml如下:
1 | <?xml version="1.0" encoding="UTF-8"?> |
3.3 application.yml
1 | server: |
3.4 主启动类
1 | package com.sevattal.springcloud; |
3.5 业务类
订单也需要Payment、CommonResult实体类,但是不需要操作数据库,没有Service、Dao,只需添加Controller即可。
1 | //Payment |
1 | //CommonResult |
首说 RestTemplate: RestTemplate 提供了多种便捷访问远程 Http 服务的方法,是一种简单便捷的访问 restful 服务模板类,是 Spring 提供的用于访问 Rest 服务的客户端模板工具集,实现 80 到 8001 的远程调用。
使用:
使用 restTemplate 访问 restful 接口非常的简单粗暴,(url、requestMap、ResponseBean.class)这三个参数分别代表 REST 请求地址、请求参数、HTTP 响应转换被转换成的对象类型。
将 RestTemplate 对象注册到容器中
1 | package com.sevattal.springcloud.config; |
1 | package com.sevattal.springcloud.controller; |
3.6 启动 80、8001 服务,测试
80 服务调用 8001 服务,实现效果如下:
查询
添加
浏览器并没有返回错误,但是我们来看数据库:
可以看到数据库只插入主键,并没有插入内容,要在8001的PaymentController加@RequestBody注解。
然后就可以插入了
4. 工程重构
项目中存在相同的代码(entities 包下的 Payment.class 和 CommonResult.class),造成代码冗余,可以进行重构。
通过 Maven 聚合父工程,把相同重复的代码移到公开公用的工程里面,还可以放第三方接口、工具类,统一调配使用。
4.1 建立公共module在springcloud2021 主项目下建立 cloud-api-commons 模块(maven)
4.2 pom.xml 如下:
1 | <?xml version="1.0" encoding="UTF-8"?> |
4.3 将 entities 包复制到 cloud-api-commons
4.4 使用 Maven 打包发布上传到公用本地库里
打开 Maven 窗口,执行 clean 测试一下,无误后出现 BUILD SUCCESS,然后执行 install
注:若 Maven 运行操作失败,请查看 Maven 配置的环境是否正确
4.5 删除重复 entities,引入 maven install 的 jar 包坐标即可使用。
1 | <!--引入自己定义的api通用包,可以使用Payment支付Entity--> |
5. EurekaServer服务端安装
5.1 在 springcloud2021 主项目下建 cloud-eureka-server7001 模块(Maven)
5.2 pom.xml
1 | <?xml version="1.0" encoding="UTF-8"?> |
5.3 application.yml
1 | server: |
5.4 主启动类
1 | package com.sevattal.springcloud; |
这是个服务注册中心,主要干的活就是服务注册,不需要写业务类。
但是注意:Eureka有两个组件,一定要标清楚哪个是Server,哪个是Client。@EnableEurekaServer代表服务注册中心
5.5 测试
出现上面图标,表示 Eureka 服务端安装成功。No instances available 表示当前没有服务注册进来
6. 单机 Eureka 构建:支付微服务 8001 入驻进 eurekaServer
6.1 将 Eureka-client 依赖引入,便于使用注解@EnableEurekaClient标注这是个 Eureka Client 端
1 | <!--eureka-client--> |
6.2 在 application.yml 添加 Eureka 相关配置
1 | eureka: |
6.3 主启动类添加注解 @EnableEurekaClient
6.4 测试
这样就注册进来了,入住进 Eureka 服务器的名称就是 8001 yml 中配置的 spring.application.name。红色警告是 Eureka 的自我保护机制,后面会详细说。
7. 单机 Eureka 构建:订单微服务入驻进 eurekaServer (操作与支付微服务 8001 一样)
7.1 在 pom 添加 Eureka-client 依赖
1 | <!--eureka-client--> |
7.2 在 application.yml 添加相关配置
1 | spring: |
7.3 主启动类添加注解 @EnableEurekaClient
7.4 测试
PS: 先启动 EurekaServer,7001 服务,再启动服务提供者 provider,8001 服务
cloud-order-server 服务以入住,查询功能也可以正常执行
8. EurekaServer 集群环境构建
8.1 创建 module cloud-eureka-server7002 (与创建 cloud-eureka-server7001 相似,除了端口改为 7002 外)
8.3 写 yml 之前修改 hosts 文件
找到 C:\Windows\System32\drivers\etc 路径下的 hosts 文件
添加如下主机名
8.4 修改 7001 和 7002 的 application.yml
8.5 测试
同时看到 Eureka 图标,且 7001 指着 7002,7002 指着 7001,说明 Eureka 集群搭建成功。
9. 将两个微服务发布到 Eureka 集群配置中
9.1 只需修改 application.yml
9.1 测试
PS: 先启动 EurekaServer,7001/7002 服务;再启动服务提供者 provider,8001;再启动消费者,80
现在,就已经把支付服务 8001、订单服务 80 注册进 Eureka 集群环境,调用也 OK。
10. 支付提供者 8001 集群环境搭建
10.1 创建 module cloud-provider-payment8002 (与创建 cloud-provider-payment8002 一样不做过多介绍)
10.2 修改 8001 和 8002 的 controller,默认的负载均衡方式是轮询,看执行查询具体调用那台 provider
10.3 测试
PS: 启动顺序:7001、7002、8001、8002、80
8001 和 8002 也都访问正常,那如果我们用 80 访问呢?发现怎么刷新都是 8001,这是因为我们的源程序地址是写死的:
单机版写死是没有问题的,但是现在有 8001、8002 了,所有不应该再关注具体的 IP 和端口,而是只认服务名称。代码修改为一下在试。
1 | @RestController |
发现报错了,现在对外暴露的不再是地址和端口,只认微服务名称了,可是微服务并不知道下面有几个,找不到这个主机名称,需要使用 # LoadBalanced 注解开启 RestTemplate 负载均衡功能。
提前说一下:这个就是后面要介绍的 Ribbon 负载均衡功能。
然后测试,多次刷新,就会发现 8001、8002 端口交替出现。
这样 Ribbon 和 Eureka 整合后 Consumer 可以直接调用服务而不用再关心地址和端口号,且该服务还有负载均衡功能了。O(∩_∩)O
11. Eureka 服务发现
11.1 SpringBoot 启动主类配置 @EnableDiscoveryClient 注解启动服务发现
11.2 Controller 中配置服务发现测试代码
1 | /* |
11.3 测试
从上述 json 以及后台日志的返回信息中可以看出,已经将 Eureka 中注册的微服务的对应信息都查询出来了。
12. Eureka 的自我保护机制
默认情况下,如果 Eureka Server 在一定时间内没有接收到某个微服务实例的心跳,Eureka Server 将会注销该实例(默认90秒)。
但是当网络分区故障发生(延时、卡顿、拥挤)时,微服务与 Eureka Server 之间无法正常通信,以上行为可能变得非常危险了。
因为微服务本身其实是健康的,此时不应该注销该微服务。Eureka 通过”自我保护模式”来解决这个问题。
当 Eureka Server 节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。
在自我保护模式中,Eureak Server 会保护服务注册表中的信息,不再注销任何服务实例。
它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。– 好死不如赖活着
以下示例为解决 Eureka 的自我保护机制
12.1 配置 Eureka application.yml 文件
1 | server: |
12.2 测试查看
注:从上述信息可以看到,Erueka 已经关闭了自我保护机制
12.3 配置微服务 8001 application.yml 文件(以下只展示 eureka 部分,配置心跳间隔时间等)
1 | eureka: |
12.4 当关闭了微服务 8001 后,查看 Eureka Web 界面
注:可以很快的看到 8001 服务,已经在 Eureka 中剔除了。