Preface
Spring Cloud 是一系列框架的有序集合. 它利用 Spring Boot 的开发便利性巧妙地简化了分布式系统基础设施的开发, 如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等, 都可以用 Spring Boot 的开发风格做到一键启动和部署. Spring 并没有重复制造轮子, 它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来, 通过 Spring Boot 风格进行再封装屏蔽掉了复杂的配置和实现原理, 最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包.
至于各种框架组件的相关概念以及入门教程网上一大把, 此篇博文主要记录个人在使用Spring Cloud构建微服务的一些配置以及踩坑…
集成Docker部分请看 Spring Boot Docker Integration
Eureka
Eureka是Netflix开发的服务发现组件, 本身是一个基于REST的服务. Spring Cloud将它集成在其子项目
spring-cloud-netflix
中, 以实现Spring Cloud的服务发现功能.
单节点
核心依赖
1 | <dependency> |
application.yml
1 | spring: |
eureka.client.register-with-eureka
: 表示是否将自己注册到 Eureka Server, 默认为 true.eureka.client.fetch-registry
: 表示是否从 Eureka Server 获取注册信息, 默认为 true.eureka.client.service-url.defaultZone
: 设置与 Eureka Server 交互的地址, 查询服务和注册服务都需要依赖这个地址. 默认是 http://localhost:8761/eureka ;多个地址可使用英文逗号(,)分隔.
application-single.yml
1 | server: |
Security配置
开启basic
的认证需要添加依赖:
1 | <dependency> |
配置类:
1 | @EnableWebSecurity |
Docker构建HA Eureka Server
基于Compose运行高可用的Eureka
application.yml
1 | spring: |
application-cluster1.yml
1 | server: |
application-cluster2.yml
1 | server: |
application-cluster3.yml
1 | server: |
docker-compose.yml
1 | version: '3.4' |
Docker Compose启动
启动前确保创建好了网络:
1 | docker network create -d=overlay --attachable --subnet 10.10.0.0/16 backend |
此时在Portainer
中可以看到三个容器已经启动:
随意一个eureka端口都能看到另外两个服务:
Docker Swarm启动
由于目前使用stack方式启动是无法加载env_file
的, 所以需要预先加载一下:
1 | export $(cat .env) && docker stack deploy --compose-file=docker-compose.yml eureka-stack |
我们的app通过合适的network
交互应该是这样的:
注意事项(ip与hostname混乱)
之前使用Docker Compose方式启动服务没什么问题, 后来换成Docker Swarm方式启动, 在Eureka的面板中发现有些服务是ip, 有些是hostname, 但都注册成功, 不过某些服务相互之间又访问不了. Google一番后的解决方案:
Server端跟Client端都使用以下配置:
1 | spring: |
踩坑(容器中服务下线无法向注册中心注销服务)
在Docker中程序, 如果PID不是1, 是接收不到docker-compose down
发出的sigterm
信号从而导致只能等待被Kill, 不能向注册中心注销.
解决方法是在Dockerfile
中的入口使用ENTRYPOINT exec java -jar ...
这种方式
Eureka Edgware.RELEASE版本注册优化
在Edgware.RELEASE
版本中相比之前的步骤, 省略了在主函数上添加@EnableDiscoveryClient
注解这一过程. Spring Cloud默认认为客户端是要完成向注册中心进行注册的.
- 添加对应的
pom
依赖. properties
文件进行配置
添加pom依赖
1 | <dependency> |
properties文件进行配置
1 | spring.application.name=EUREKA-CLIENT |
启动Eureka Client客户端, 访问http://localhost:8761/eureka
可以看到EUEREKA-CLIENT已经注册到Eureka Server服务上了.
关闭自动注册功能
Spring Cloud提供了一个参数, 该参数的作用是控制是否要向Eureka Server发起注册. 具体参数为:
1 | //默认为true,如果控制不需要向Eureka Server发起注册将该值设置为false. |
可以在JUnit测试中通过该变量关闭服务发现:
1 | @BeforeClass |
Eureka的自我保护模式
当Eureka提示下面一段话的时候, 就表示它已经进入保护模式:
1 | EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. |
保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护. 一旦进入保护模式, Eureka Server将会尝试保护其服务注册表中的信息, 不再删除服务注册表中的数据(也就是不会注销任何微服务).
解决方法如下:
服务器端配置:
1 | eureka: |
客户端配置:
1 | eureka: |
注意:
更改Eureka更新频率将打破服务器的自我保护功能, 生产环境下不建议自定义这些配置.
修改Eureka界面UI
覆盖对应源码中的界面文件即可:
效果图:
注意事项:
如果pom.xml
中的parent
不是spring-boot-starter-parent
, 这些样式文件需要新建一个项目另外打包成jar包再引入方可生效.
RPC(Remote Procedure Call)
这里指针对Http协议调用
通过注册中心, 服务间的基本调用如下:
调用方式主要有三种(基本上在实际应用中都使用Feign)
前置条件: 集成服务注册中心
服务提供者(service-a
):
1 | @RestController |
LoadBalancerClient
初始化RestTemplate
, 用来发起 REST 请求.
1 | @Bean |
消费者(service-b
):
1 | @RestController |
访问http://127.0.0.1:8082/service-b/ybd
, 返回:
1 | ybd-PC/127.0.1.1: Hello 『ybd!』 , Date: Wed Aug 08 18:30:48 CST 2018 |
Spring Cloud Ribbon
它是一个基于 HTTP 和 TCP 的客户端负载均衡器. 它可以通过在客户端中配置 ribbonServerList 来设置服务端列表去轮询访问以达到均衡负载的作用. 当 Ribbon 与 Eureka 联合使用时, ribbonServerList 会被 DiscoveryEnabledNIWSServerList 重写, 扩展成从 Eureka 注册中心中获取服务实例列表. 同时它也会用 NIWSDiscoveryPing 来取代 IPing, 它将职责委托给 Eureka 来确定服务端是否已经启动.
为RestTemplate
添加@LoadBalanced
注解:
1 | @LoadBalanced |
Controller:
修改 controller, 去掉LoadBalancerClient
, 并修改相应的方法, 直接用 RestTemplate
发起请求
1 | @RestController |
Spring Cloud Feign
依赖(使用OkHttp组件):
1 | <dependency> |
配置:
1 | feign: |
在启动类上加上@EnableFeignClients
1 | @EnableFeignClients |
Feign:
1 | @FeignClient(value = "service-a", path = "/service-a") |
value
指被调用方的服务名path
请求指定前缀, 例如上面的sayHello
会请求/service-a/{name}
这个url
调用:
1 | @RestController |
踩坑
1 | Caused by: java.lang.IllegalStateException: PathVariable annotation was empty on param 0. |
这个大概的意思就是@PathVariable
的第一个参数为空. . .
因为之前的写法是这样的:
1 | @GetMapping("/{name}") |
正确姿势是这样的:
1 | @GetMapping("/{name}") |
@PathVariable
需要指定占位符的名字("name")
Spring Cloud Gateway
Spring Cloud Gateway 是 Spring Cloud 的一个全新项目, 该项目是基于 Spring 5.0, Spring Boot 2.0 和 Project Reactor 等技术开发的网关, 它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式.
Spring Cloud Gateway 作为 Spring Cloud 生态系统中的网关, 目标是替代 Netflix Zuul, 其不仅提供统一的路由方式, 并且基于 Filter 链的方式提供了网关基本的功能, 例如: 安全、监控、埋点和限流等.
Spring Cloud Gateway 的特征:
- 基于 Spring Framework 5, Project Reactor 和 Spring Boot 2.0
- 动态路由
- Predicates 和 Filters 作用于特定路由
- 集成 Hystrix 断路器
- 集成 Spring Cloud DiscoveryClient
- 易于编写的 Predicates 和 Filters
- 限流
- 路径重写
流程图:
Spring Boot Admin
依赖
1 | <dependencies> |
主类添加注解
1 | @EnableAdminServer |
配置
application.yml
:
1 | server: |
application-dev.yml
:
1 | eureka: |
SecuritySecureConfig
:
1 | @Configuration |
Finally
代码: https://github.com/masteranthoneyd/spring-cloud-learning