基于Spring Cloud的微服务架构

Spring Cloud

  • 发布于2015年3月
  • 引入NetflixOSS的开源技术栈
  • 基于Spring boot
  • 构建了分布式系统的公共模式

Spring boot是Spring框架对“约定优于配置”理念的最佳实践的产物,比如自动配置,它背后通过了Springfactoriesloader,它属于Spring框架私有的一种扩展方案(类似于Java的SPI-Service provider interface), 其主要的功能就是从指定的配置文件meta-inf/spring.factories加载配置。

常见的微服务组件

微服务组件.png

服务端负载均衡

  • HTTP重定向负载均衡(类似DNS域名解析负载均衡)
    • 优点
      • 实现比较简单
    • 缺点
      • 浏览器需要两次请求才能完成一次访问
      • 性能较差
      • 重定向服务器自身的处理能力有可能成为瓶颈

重定向服务器.png

  • 反向代理均衡器的优点(以Nginx,HA Proxy为代表)
    • 比较简单
    • 可以利用反向代理缓存资源
    • 改善网站的性能
  • 反向代理均衡器的缺点
    • 所有请求和响应的中转站
    • 其性能可能会成为瓶颈

反向代理.png

  • 数据链路层的负载均衡优点
    • 不需要修改数据包的源地址
    • 响应数据不需要通过负载均衡器

使用三角传输模式的链路层负载均衡是目前大型网站使用最为广泛的一种负载均衡手段,在Linux平台上面最好的链路层负载均衡开源产品是LVS(Linux Virtual Server)。

以上都是服务端的负载均衡,代表的产品有F5,LVS,Nginx,HA Proxy和ELB/ALB等。

客户端的负载均衡

在Spring Cloud中提供了Ribbon 作为客户端的负载均衡,一般Ribbon服务注册与发现Eureka一起协同实现对请求的负载均衡,如图:
clientside-loadblancer.png
从一个服务注册器中查询,获得其中所有可用实例。然后使用负载均衡算法(常见的算法:可支持最小连接数、轮询、比例、最快响应、哈希、预测、观察、动态比例等)从多个服务实例中选择出一个,发出请求。

如果在有客户端的负载均衡情况下,是否还有与服务端的负载均衡(比如AWS ELB)混用的情况呢? 在服务消费者与服务提供者之间增加了ELB类的服务器端负载均衡,反而增加了额外的网络跳转,所以并不推荐,但是可以考虑在Edge Server外添加,如下图:
ELB+Ribbon.png

服务注册与发现

一个服务如何发现另外一个服务的IP和端口呢? 你可能会说,Hardcode式的静态配置,但是当服务按需动态伸缩之后呢?这时我们就可以引入服务治理里面非常重要的一个组件 - 服务注册于发现

Spring Cloud内可选的服务注册与发现Starter很多,这里简单聊下Eureka和Consul。

  • Eureka
    • 提供REST接口
    • 支持多点的同步(支持集群实现高可用,P2P的注册信息同步)
    • 支持客户端缓存(CAP原理中,它保证AP)
    • 自注册的方式(服务实例使用 POST 请求来注册网络地址,每三十秒使用 PUT 请求来刷新注册信息)

Eureka.png

  • Consul
    • 来自Hashicorp公司(旗下知名产品很多,e.g vagrant,valut
    • 提供REST接口(从服务注册,查询存储键值对到健康检查,都提供RESTful接口,使得集成不同的技术栈变的很容易)
    • 提供现成的DNS服务器
    • 第三方注册 - 服务管理器模式(服务注册器会通过查询部署环境或订阅事件的方式来跟踪运行实例的更改;一旦侦测到有新的可用服务实例,会向注册表注册此服务;服务管理器也负责注销终止的服务实例)

注册方式.png

  • 其他几个主流竞品对比图:

服务注册与发现.png

熔断器

Hystrix记录那些超过预设定的极限值的调用。它实现了circuit break模式,从而避免了无休止的等待无响应的服务。如果一个服务的错误率超过预设值,Hystrix将中断服务,并且在一段时间内所有对该服务的请求会立刻失效。Hystrix可以为请求失败定义一个fallback操作,例如读取缓存或者返回默认值(想象一个场景,一个下游系统响应非常慢,即使我们设置了超时,也需要等待很长的时间才能得到错误返回,假如现在是请求高峰,大量的等待,大量的消耗系统资源,从而让我们的整个系统变得非常慢。当我们使用断路器时,或超时,或者返回错误码,达到一定阈值后,它会自动停止向它发送消息,并调用相应的fallback函数,等同于启动了快速失败。当它恢复健康后,他会自动重新发送消息到下游服务)

熔断器.png

配置管理

我们如何做配置管理呢?12军规里面有描述:将配置一定要从代码中分离,原因很明显, 配置是最容易改变的部分, 比如不同regions, 像dev,sys,prod他们连接的第三方服务地址可能是不一样的, feature toggle也可能是不一样的。难道我们每更改一次配置,就需要重新部署吗?
config-server.png

Spring cloud推荐用配置服务器做配置管理:

  • 默认使用GIT,在版本控制之下
  • 支持PROPERTY和YAML
  • 中心化的动态配置
  • 当配置改变时,一些BEANS会被重新初始化,无须重启,但需要调用/POST /refresh触发

Spring cloud config server可以与Spring cloud bus(基于Spring cloud stream)配合使用,实现集群配置文件的动态更新:

config-server-cluster.png

configuration-sever.png

example-for-dev-region.png

API网关

  • 客户端与服务端直接通信又有那些弊端?

    • 客户端为了获取某个数据集,可能需要多次请求。
    • UI端和微服务直接耦合在一起。
  • API网关的好处?

    • 减少API请求次数
    • 限流量
    • 缓存
    • 认证
    • 监控
    • 对外提供统一的接口,使背后的微服务对UI端来说是透明的

Spring Cloud提供的组件是ZUUL看门人,一般它负责请求转发、聚合返回的数据集,所有来自客户端的请求都要先经过API Gateway,然后路由这些请求到对应的微服务。

分布式链式追踪

Spring Cloud里面对应的组件是Sleuth(埋点和发送) + zipkin(收集和展示),其实现原理都来至于Google Dapper: http://bigbully.github.io/Dapper-translation/
image.png

以下的操作都会被自动追踪:
image.png

案例演示

之前准备的一个用于演示的代码库,它采用了常见的微服务组件,比如我们讲过的服务注册与发现,负载均衡,分布式配置服务,网关,hystrix,并使用docker-compose来进行服务的编排,可以下来感受下。
Repo: https://github.com/qinnnyul/spring-cloud-with-docker-demo