Sping Cloud组件扩展

1.Apollo

统一管理配置信息,增强配置管理的服务能力。

使用配置中心管理配置后,可以将配置信息从项目转移到配置中心,一般一个项目会有一个唯一的标识ID,通过这个ID从配置中心获取对应的配置内容。

  1. 拉取
    项目在启动的时候通过配置中心拉取配置信息。
  2. 推送
    在配置中心修改配置后,可以实时地推送给客户端进行更新。

解决的问题:每个节点都要重启、格式不规范、容易被错改、没有历史记录、安全性不高

1.1 主要功能

  • 统一管理不同环境、不同集群的配置
  • 配置修改实时生效,即热发布功能
  • 版本发布管理
  • 灰度发布
  • 权限管理、发布审核、操作审计
  • 提供Java和.Net原生客户端,轻松集成、操作简单
  • 提供开放平台API
  • 部署简单
    Apollo 和 Spring Cloud Config 对比

1.2 概念介绍

  • 应用
    应用指项目,标识用appId来指定,Spring Boot项目中建议直接配置在application.yml中。

  • 环境
    Apollo客户端在运行时除了需要知道项目当前的身份标识,还需要知道当前项目对应的环境,从而根据环境去配置中心获取对应的配置。可以通过Java System Property或配置文件指定,目前支持的环境有Local、DEV、FAT(测试环境)、UAT(集成环境)、PRO(生产环境)。

  • 集群
    不同的集群可以有不同的配置文件,可以通过Java System Property或配置文件来指定。

  • 命名空间
    可以用来对配置做分类,不同类型的配置存放在不同的命名空间中,如数据库配置文件、消息队列配置、业务相关配置等。命名空间还可以让多个项目共用一份配置,如Redis集群。

  • 权限控制

1.3 架构设计

  • Config Service
    服务于Client对配置的操作,提供配置的查询、更新接口(基于Http long polling)

  • Admin Service
    服务于后台Portal(Web管理端),提供配置管理接口

  • Meta Server
    Meta Server是对Eureka的一个封装,提供了HTTP接口获取Admin Service和Config Service的服务信息。部署时和Config Service是在一个JVM进程中的,所以IP、端口和Config Service一致。

  • Eureka
    用于提供服务注册和发现,Config Service和Admin Service都会向Eureka注册服务。Eureka在部署时和Config Service在一个JVM进程中,即Config Service包含了Meta Server和Eureka

  • Portal
    后台Web界面管理配置,通过Meta Server获取Admin Service服务列表(IP+Port)进行配置的管理,客户端做负载均衡。

  • Cilent
    Apollo提供的客户端,用于项目中对配置的获取、更新。通过Meta Server获取Config Service服务列表(IP+Port)进行配置的管理,客户端内做负载均衡。

工作流程:

  1. 注册、续约、取消,也就是服务注册的操作,Config Service和Admin Service都会注册到Eureka中。
  2. 服务发现的逻辑,Client需要指定所有的Config Service,Portal需要知道所有的Admin Service,然后才能发起对应的操作。查找服务列表是通过负载进行转发到Meta Server.
  3. Meta Server去Eureka中获取对应的服务列表。
  4. 当获取到对应的服务信息后,就可以直接发起远程调用了。

推送设计:

在Portal中进行配置的编辑和发布操作后,Portal会调用Admin Service提供的接口进行发布操作。Admin Service收到请求后,发送ReleaseMessage给各个Config Service,通知Config Service配置发生了变化。Config Service收到ReleaseMessage后,通知对应的客户端,基于HTTP长连接实现。

消息设计:

ReleaseMessage消息是通过MySQL实现了一个简单的消息队列。Admin Service在配置发布后会往ReleaseMessage表插入一条消息记录,Config Service会启动一个线程定时扫描ReleaseMessage表,去查看是否有新的消息记录。Config Service发现有新的消息记录,那么就会通知所有的消息监听器,消息监听器得到配置发布的信息后,则会通知对应的客户端。

客户端设计:

客户端和服务端保持了一个长连接,编译配置的实时更新推送。定时拉取配置是客户端本地的一个定时任务,默认5分钟1次,也可以通过在运行时指定System Property:apollo.refreshInterval来进行覆盖,单位是分钟,采用推送+定时拉取的方式就等于双保险。客户端从Apollo配置中心服务端获取到应用的最新配置后,会保存在内存中。客户端会把从服务取到的配置在本地文件系统中缓存一份,当服务或网络不可用时可以使用本地配置,也就是本地开发模式env=Local。

2.分布式链路跟踪


分布式链路跟踪的关键在于如何将请求经过的服务节点都关联起来。

2.1 核心概念

  • Span
    基本工作单元,如发送RPC请求是一个新的Span、发送HTTP请求是一个新的Span、内部方法调用也是一个新的Span。

  • Trace
    一次分布式调用的链路信息,每次调用链路信息都会在请求入口处生成一个TraceId。

  • Annotation
    用于记录事件的信息。在Annotation中会有CS、SR、SS、CR这些信息。

  • CS
    Client Sent,客户端发送一个请求,这个Annotation表示Span的开始。

  • SR
    Server Received,服务端获得请求并开始处理,用SR的时间减去CS的时间即网络延迟时间。

  • SS
    Server Sent,在请求处理完成时将响应发送回客户端,SR-SS,即服务端处理请求所需的时间。

  • CR
    Client Recevied,表示Span结束,客户端从服务端收到响应,CR-CS,即全部时间。

2.2 请求追踪过程分解

  1. 当一个请求访问SERVICE1时,此时没有Trace和Span,会生成Trace和Span,如图所示生成Trace ID是X,Span ID是A。
  2. 接着SERVICE1请求SERVICE2,这是一次远程请求,会生成一个新的Span,Span ID为B,Trace ID不变还是X。Span B处于CS状态,当请求到达SERVICE2后,SERVICE2有内部操作,生成了一个新的Span,Span ID为C,Trace ID不变。
  3. SERVICE2处理完后向SERVICE3发起请求,生成新的Span,Span ID为D,Span D处于CS状态,SERVICE3接收请求后,Span D处于RS状态,同时SERVICE内部操作也会生成新的Span,Span ID为E。
  4. SERVICE3处理完后,需要将结果响应给调用方,此时Span D处于SS状态,当SERVICE2收到响应后,Span D处于CR状态。

一次请求会经过多个服务,会产生多个Span,但Trace ID只有一个。

2.3 Spring Cloud Sleuth

  • 可以添加链路信息到日志中
  • 链路数据可直接上报给Zipkin
  • 内置了很多框架的埋点,如Zuul、Feign、Hystrix

2.4 Zipkin


收集数据、查询数据。

  • Collector
    Zipkin的数据收集器,进行数据验证、存储。

  • Storage
    存储组件,Zipkin默认在内存中存储数据,数据落地的话支持ElasticSearch和MySQL。

  • Search
    Zipkin的查询API,用于查找和检索数据,主要使用者为Web UI。

  • Web UI
    提供可视化的操作界面,直观的查询链路跟踪数据。

链路跟踪的信息会通过Transport传递给Zipkin的Collector,Transport支持的方式有HTTP和MQ进行传输。

2.5 Sleuth关联整个请求链路日志

集成Spring Cloud Sleuth后,会在原始的日志加上一些链路的信息。

  • application name
    应用名称,即application.yml里的spring.application.name参数配置的属性。

  • traceId
    为请求分配的唯一请求号,用来标识一条请求链路。

  • spanId
    基本的工作单元,一个请求可以包含多个步骤,每个步骤有自己的Span ID,一次请求只有一个Trace ID和多个Span Id。

  • export
    布尔类型,表示是否将该信息输出到Zipkin进行收集和展示。

2.6 使用技巧

  • 抽样采集数据
    spring.sleuth.sampler.probability=10
    请求次数:Zipkin数据条数=10:1

  • RabbitMQ代替HTTP发送调用链路数据
    删除配置spring.zipkin.base-url,在启动Zipkin服务时指定RabbitMQ信息:

    1
    java -DRABBIT_ADDRESSES=192.168.10.124:5672 -DRABBIT_USER=admin -DRABBIT_PASSWORD=123456 -jar zipkin.jar
  • ElasticSearch存储调用链数据
    启动Zipkin的时候指定存储类型为ES,指定ES的URL信息:

    1
    java -DSTORAGE_TYPE=elasticsearch -DES_HOSTS=http://localhost:9200 -jar zipkin.jar
  • 手动埋点检测性能

  • Hystrix埋点分析

3.微服务安全认证

3.1 常用的认证方式

  • session
    用户登陆后将信息存储在服务端,客户端通过cookie中的sessionId来标识对应的用户。

    缺点:

    1. 服务端需要保存每个用户的登录信息,如果用户量非常的,服务端的存储压力也会增大。
    2. 多节点时,通过负载均衡器进行转发,session可能会丢失。

    解决办法:
    session复制,Nginx可以设置黏性Cookie来保证一个用户的请求只访问同一个节点;session集中存储,如存储在Redis中。

  • HTTP Basic Authentication
    HTTP基本认证,客户端会在请求头中增加Authorization,Authorization是用户名和密码Base64加密后的内容,服务端获取Authorization Header中的用户名与密码进行验证。

  • Token
    与HTTP Basic Authentication类似,与session不同,session只是一个key,会话信息存储在服务端。而Token中会存储用户的信息,然后通过加密算法进行加密,只有服务端才能解密,服务端拿到Token后进行解密获取用户信息。

3.2 JWT认证

简介:
JWT(JSON Web Token)
如用户登录时,基本思路就是用户提供用户名和密码给认证服务器,服务器验证用户提交信息的合法性;如果验证成功,会产生并返回一个Token,用户可以使用这个Token访问服务器上受保护的资源。

JWT由三部分构成:头部(Header)、消息体(Payload)、签名(Signature)

1
token = encodeBase64(header) + '.' + encodeBase64(payload) + '.' + encodeBase64(signature)

头部信息:令牌类型、签名算法

1
{ "alg": "HS256", "typ": "JWT" }

消息体:应用需要的信息,如用户的id

1
{"id": "1234567890", "name": "John Doe"}

签名:用来判断消息在传递的路径上是否被篡改

1
HMACSHA256( base64UrlEncode(header)  + "." +  base64UrlEncode(payload), secret)

JWT认证流程:

客户端需要调用服务端提供的认证接口来获取 Token。获取 Token 的流程如图所示,客户端会首先发起一个认证的请求到网关,网关会将请求转发到后端的用户服务中,在用户服务中验证身份后,就会根据用户的信息生成一个 Token 返回给客户端,这样客户端就获取了后面请求的通行证。然后,客户端会将获取的 Token 存储起来,在下次请求时带上这个 Token,一般会将 Token 放入请求头中进行传递。当请求到达网关后,会在网关中对 Token 进行校验,如果校验成功,则将该请求转发到后端的服务中,在转发时会将 Token 解析出的用户信息也一并带过去,这样在后端的服务中就不用再解析一遍 Token 获取的用户信息,这个操作统一在网关进行的。如果校验失败,那么就直接返回对应的结果给客户端,不会将请求进行转发。

在网关中,验证过滤器会对 /oauth/token 这个认证 API 进行放行,不进行验证。
用户信息的全局传递扩展:
不需要加参数,直接通过请求头进行传递,在服务内部通过ThreadLocal进行上下文传递。主要流程:从网关传递到后端服务,后端服务接受数据后存储到ThreadLocal中,服务会调用其它服务,如果用Feign调用可以利用Feign的拦截器传递数据,如果用RestTemplate的拦截器传递数据也一样。

3.3 Token的使用

Token注销

Token的有效期存储在Token本身中,只有解析出Token的信息,才能获取到Token的有效时间,不能修改。Token的有效期越短,安全性越高。还可以在用户退出登录时,进行Token的注销操作,如将注销的Token放入Redis中进行一层过滤,即在网关中验证Token有效性时先从Redis中判断Token是否存在,如果存在,直接拦截。Token放入Redis的过期时间一般会设置为Token剩余的有效时间。

使用建议

  • 设置较短(合理)的过期时间
  • 注销的Token及时清除(放入Redis中做一层过滤)
  • 监控Token的使用频率
  • 核心功能敏感操作可以使用动态验证(验证码)
  • 网络环境、浏览器信息等识别
  • 加密密钥支持动态修改
  • 加密密钥支持动态修改

3.4 内部服务之间的认证

  1. IP白名单
    如用户服务只能某些IP或IP段访问,IP白名单可以采用配置中心来存储,具备实时刷新的能力。

  2. 内部同样使用Token进行验证
    服务在启动时就可以在统一的认证服务中申请Token,申请需要的认证信息可以放在配置中心。这样服务启动时就有了能够访问其他服务的Token,在调用时带上Token,被调用的服务中进行Token的校验。
    对于Token的失效更新:

    1. 在请求时如果返回的Token已经失效,那么可以重新获取Token后再发起调用,这种在并发量大时需要加锁,不然会发生同时申请多个Token的情况。
    2. 定时更新,如Token有效期1个小时,那么定时任务可以50分钟更新一次。
请作者喝瓶肥宅快乐水