我所理解的 Spring Cloud 就是微服务系统架构的一站式解决方案,在平时我们构建微服务的过程中需要做如 服务发现注册配置中心消息总线负载均衡断路器数据监控 等操作,而 Spring Cloud 为我们提供了一套简易的编程模型,使我们能在 Spring Boot 的基础上轻松地实现微服务项目的构建。

服务发现框架—Eureka

服务注册Register

当Eureka客户端向Eureka Server注册时,它提供自身的元数据,比如IP地址、端口、运行状态指示符URL,主页。

服务续约Renew

Eureka客户端每隔30秒发送一次心跳来续约。通过续约来告知Eureka Server该Eureka客户仍然存在,没有出现问题。正常情况下,如果Eureka Server在90秒内没有收到Eureka客户的续约,它会将实例从注册表中删除。

获取注册列表信息Fetch Registes

官方解释:Eureka 客户端从服务器获取注册表信息,并将其缓存在本地。客户端会使用该信息查找其他服务,从而进行远程调用。

该注册列表信息定期(每30秒钟)更新一次。

每次返回注册列表信息可能与 Eureka 客户端的缓存信息不同, Eureka 客户端自动处理。如果由于某种原因导致注册列表信息不能及时匹配,Eureka 客户端则会重新获取整个注册表信息。 Eureka 服务器缓存注册列表信息,整个注册表以及每个应用程序的信息进行了压缩,压缩内容和没有压缩的内容完全相同。Eureka 客户端和 Eureka 服务器可以使用JSON / XML格式进行通讯。在默认的情况下 Eureka 客户端使用压缩 JSON 格式来获取注册列表的信息。

服务下线

Eureka客户端在程序关闭时向Eureka服务器发送取消请求。发送后,该客户端的实例信息将从服务器的实例注册表中删除。该下线请求不会自动完成,需要调用一下内容:DiscoveyManager.getInstance().shutdownComponent

服务剔除Eviction:

当Eureka客户端连续90秒没有想Eureka服务器发送服务续约,Eureka服务器会将该服务实例从服务注册列表删除。

可以充当服务发现的组件有很多:ZookeeperConsulEureka

负载均衡Ribbon

ribbon为一个客户端负载均衡器,运行在消费端。

Nginx和Ribbon的对比

Nginx是一种集中式的负载均衡器:将所有请求都集中起来,然后再进行负载均衡

image-20200809205347000.png

而Eureka则是在消费端进行负载均衡

image-20200809205656975.png

ribbon的几种负载均衡算法

Nginx使用的是轮询和加权轮询算法,而Ribbon有更多的负载均衡算法,默认使用的是RoundRobinRule轮询策略

  1. RoundRobinRule轮询策略。默认采用的策略,若经过一轮轮询没有找到可用的provider,最多轮询10轮,若最终没有找到则返回null。
  2. RandomRule:随机策略,从所有可用的provider中随机选择一个。
  3. RetryRule:重试策略。先按照RoundRobinRule策略获取provier,若获取失败,则在指定的时限内重试。默认时限为500毫秒。

Open Feign

看不懂后面再看 = =

Hystrix熔断和降级

Hystrix是一个库,可通过添加等待时间容限和容错逻辑来帮助您控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点,停止服务之间的级联故障并提供后备选项来实现此目的,所有这些都可以提高系统的整体弹性。

image-20200810085251258.png

例如服务A调用服务B,服务B调用服务C,如果服务C阻塞了,那么A和B都会阻塞,此时会产生服务雪崩的情况。

熔断

熔断就是服务雪崩的一种有效解决方案。当指定时间窗内的请求失败率达到阈值,系统将通过断路器直接将次请求链路断开。这里的熔断就是指Hystrix中的断路器模式,可以使用简单的@HystrixCommand注解来标注某个方法,这样Hystrix就会使用断路器来包装这个方法

  1. @HystrixCommand(
  2. commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1200")}
  3. )
  4. public List<Xxx> getXxxx() {
  5. // ...省略代码逻辑
  6. }

降级

降级是为了更好的用户体验,当一个方法调用异常时,通过执行另一种代码逻辑来给用户友好的回复,这也就对应着Hystrix的后备模式。

可以设置fallbackMethod来给一个方法设置备用的代码逻辑。

举个例子,比如这时有一个热点新闻出现,大量用户的访问导致系统崩溃,这时进行对服务进行降级,比如说当前人数太多请稍后查看。

  1. // 指定了后备方法调用
  2. @HystrixCommand(fallbackMethod = "getHystrixNews")
  3. @GetMapping("/get/news")
  4. public News getNews(@PathVariable("id") int id) {
  5. // 调用新闻系统的获取新闻api 代码逻辑省略
  6. }
  7. //
  8. public News getHystrixNews(@PathVariable("id") int id) {
  9. // 做服务降级
  10. // 返回当前人数太多,请稍后查看
  11. }

微服务网关Zuul

Zuul作为边界服务应用,实现了动态路由、监视、弹性和安全性而构建。

Eureka Server作为服务提供者的统一入口。消费者可以直接访问Eureka Server,但这样不便于访问与管理,而Zuul就是这样的一个对于消费者的统一入口。

网关的功能大概就是鉴权限流路由监控、网关有的功能,Zuul基本都有。Zuul最主要的功能是路由和过滤了

路由功能

image-20200810094538612.png

Zuul在Eureka Server注册之后可以获得所有的Consumer数据,这样就能够实现路由映射了。

比如原来用户调用Consumer1的接口:localhost:8001/studentInfo/update

现在可以通过Zuul来映射:localhost:9000/consumer1/studentInfo/update

另外还有其他的功能:

  1. 添加一个统一的前缀yaml zuul: prefix: /zuul
    localhost:9000/zuul/consumer1/studentInfo/update
  2. 路由策略配置
    服务名暴露给用户会存在安全性问题yaml zuul: routes: consumer1: /FrancisQ1/** consumer2: /FrancisQ2/**
    localhost:9000/zuul/FrancisQ1/studentInfo/update
  3. 服务名屏蔽
    之前带有服务名的路径还是可以访问的,所以要屏蔽掉之前的路径yaml zuul: ignore-services: "*"

  4. 路径屏蔽
    Zuul还可以屏蔽掉指定路灯的URI,只要请求包含指定的路径,那么都将过滤掉。yaml zuul: ignore-patterns: **/auto/**

注意:**代表匹配多级路径路径,*代表匹配一级任意路径

  1. 敏感请求头屏蔽
    默认情况下,像 CookieSet-Cookie 等敏感请求头信息会被 zuul 屏蔽掉,我们可以将这些默认屏蔽去掉,当然,也可以添加要屏蔽的请求头。

Zuul过滤功能

因为所有请求都经过Zuul网关,那么我们可以进行各种过滤,这样我们就能实现限流,灰度发布,权限控制。

image-20200810102536880.png

过滤器类型:PreRoutingPost。前置Pre就是在请求之前进行过滤,Routing路由过滤器就是我们上面所讲的路由策略,而Post后置过滤器就是在 Response 之前进行过滤的过滤器。

下面是一个简单实现时间日志打印的demo

  1. // 加入Spring容器
  2. @Component
  3. public class PreRequestFilter extends ZuulFilter {
  4. // 返回过滤器类型 这里是前置过滤器
  5. @Override
  6. public String filterType() {
  7. return FilterConstants.PRE_TYPE;
  8. }
  9. // 指定过滤顺序 越小越先执行,这里第一个执行
  10. // 当然不是只真正第一个 在Zuul内置中有其他过滤器会先执行
  11. // 那是写死的 比如 SERVLET_DETECTION_FILTER_ORDER = -3
  12. @Override
  13. public int filterOrder() {
  14. return 0;
  15. }
  16. // 什么时候该进行过滤
  17. // 这里我们可以进行一些判断,这样我们就可以过滤掉一些不符合规定的请求等等
  18. @Override
  19. public boolean shouldFilter() {
  20. return true;
  21. }
  22. // 如果过滤器允许通过则怎么进行处理
  23. @Override
  24. public Object run() throws ZuulException {
  25. // 这里我设置了全局的RequestContext并记录了请求开始时间
  26. RequestContext ctx = RequestContext.getCurrentContext();
  27. ctx.set("startTime", System.currentTimeMillis());
  28. return null;
  29. }
  30. }
  1. // lombok的日志
  2. @Slf4j
  3. // 加入 Spring 容器
  4. @Component
  5. public class AccessLogFilter extends ZuulFilter {
  6. // 指定该过滤器的过滤类型
  7. // 此时是后置过滤器
  8. @Override
  9. public String filterType() {
  10. return FilterConstants.POST_TYPE;
  11. }
  12. // SEND_RESPONSE_FILTER_ORDER 是最后一个过滤器
  13. // 我们此过滤器在它之前执行
  14. @Override
  15. public int filterOrder() {
  16. return FilterConstants.SEND_RESPONSE_FILTER_ORDER - 1;
  17. }
  18. @Override
  19. public boolean shouldFilter() {
  20. return true;
  21. }
  22. // 过滤时执行的策略
  23. @Override
  24. public Object run() throws ZuulException {
  25. RequestContext context = RequestContext.getCurrentContext();
  26. HttpServletRequest request = context.getRequest();
  27. // 从RequestContext获取原先的开始时间 并通过它计算整个时间间隔
  28. Long startTime = (Long) context.get("startTime");
  29. // 这里我可以获取HttpServletRequest来获取URI并且打印出来
  30. String uri = request.getRequestURI();
  31. long duration = System.currentTimeMillis() - startTime;
  32. log.info("uri: " + uri + ", duration: " + duration / 100 + "ms");
  33. return null;
  34. }
  35. }

Spring cloud Config

背景:当微服务系统开始庞大起来,每个部件都会持有自己的配置,如果不进行配置的统一管理,我们只能去每个应用下一个一个寻找配置文件再修改再重启应用。

对于分布式系统,不应该去每个应用下分别修改配置文件,也不应该重启应用,这样会使服务无法访问。

所以Spring Cloud Config主要解决的就是两点:

  1. 对配置文件统一地进行管理。
  2. 能在项目运行时动态修改配置文件。

简单来说,Spring Cloud Config 就是能将各个 应用/系统/模块 的配置文件存放到 统一的地方然后进行管理(Git 或者 SVN)。

Spring Cloud Config 就暴露出一个接口给启动应用来获取它所想要的配置文件,应用获取到配置文件然后再进行它的初始化工作。

image-20200810104305285.png

而实现动态修改配置文件用到了Bus

Spring Cloud Bus

Spring Cloud Bus的作用就是管理和广播分布式系统中的消息。

当然作为 消息总线Spring Cloud Bus 可以做很多事而不仅仅是客户端的配置刷新功能。

而拥有了 Spring Cloud Bus 之后,我们只需要创建一个简单的请求,并且加上 @ResfreshScope 注解就能进行配置的动态修改了

image-20200810104534421.png