经过多年的演进如今的微服务在我们看来更多是个设计范式,当然他的核心依然离开不了字面含义,将服务拆解为多个粒度更小的(可复用)服务。但他俨然已经是个庞大的知识体系,借鉴Spring Cloud简单归纳下微服务所包含的知识点。
我想强调的一点是,这些知识点领域与微服务的关系可以理解为:每一个知识点都几乎是一个可以深入学习的领域,也是完全脱离于微服务而存在的庞大体系,本身有着广阔的应用场景。但如今微服务体系亦已包含了这些领域

1.服务注册与服务发现

可以说服务注册与服务发现是微服务作为分布式架构的基本面。经典微服务体系目前依然采用集中式的注册中心对服务进行统筹管理,比如Spring Cloud的EurekaServer,比如Dubbo用的比较普遍的Zookeeper。这意味着每个服务实例必须有个client定时与注册中心进行交互,服务定期向注册中心汇报心跳,同时定期更新服务列表。而以istio为代表的新一代的微服务泛型service mesh则将服务中的这个client抽离成到机器节点中的一个进程中(sidecar),在保留原有client负载均衡、服务发现能力的同时,还包含了断路器(容错机制)等原本服务实例需要去关注的能力。

2.服务调用

以Spring Cloud为代表的基于HTTP,以Dubbo为代表的基于RPC,二者可以说各有千秋(当然Spring Cloud现在也有支持RPC的组件),在传输性能方面RPC通常被认为是优于HTTP,但RPC依赖于对象的序列与反序列化的本质,也为业务代码的升级带来了更多的繁琐,比如在Dubbo中一个新的服务调用需要引入对应服务的interface,为开发人员带来更多的代码文件与协同工作。
在网络请求模型方面,以Netty为代表的NIO网络请求模型,也应用在不少微服务架构中。Dubbo采用的就是NIO模型,而Spring Cloud某种程度上它的服务大部分即基于Spring Boot的Web容器,默认采用传统的BIO。当然了,NIO从请求到响应,往往伴随着线程上下文的切换,如果想保留请求时候的一些信息,比如traceID,就需要做一些额外的工作(不过NIO框架一般把这部分工作也做了)。从处理并发的能力上来看,NIO无疑具备天然的优势,其底层多路复用的实现,不论是select、poll或者epoll,又是一个值得好好学习的议题

3.配置中心

个人觉得这一部分更多的是使用级别的知识,当然像Spring Cloud Config也提供了热更新这样的能力,本质上配置中心在更新配置后通过消息队列通知到各个服务,基于IOC的Spring Boot容器对注入的依赖进行重载。

4.容错

创造冗余是保证服务高可用的根本,而健全的容错机制可以增强服务的弹性。从Spring Cloud所使用的Netflix Hystrix中,我们或多或少可以从中提炼出一些方法论。

  1. 为服务创建一个work线程池以确保调用仅消耗有限的资源,这一做法同时适用BIO和NIO模型。在确定线程数量后,也可创建一个等待队列,以便当线程池满负荷工作时候让更多的请求再等待一会。
  2. 明确一个超时时间,一旦请求超时即返回超时响应。
  3. 提供后备方法,让超时的处理更为优雅(应该属于服务降级的范畴)。
  4. 建立熔断机制(circuit breaker),检测时间窗口内的失败请求数量,一旦超过阈值便切断与下游的链路,并在新的时间窗口去抽样统计下游的响应,以确定是否恢复链路。

5.网关

网关作为前端接入层,应具备的功能包括:路由定义、过滤器(前置过滤器、后置过滤器、路由过滤器)、网络限流、验证等。这里暂未将负载均衡列入其中,原因是通常情况下在网关之上还有Nginx或者硬件F5来扮演负载均衡的角色,而网关到下游各个服务,则可以理解为属于服务间调用范畴,在诸多微服务框架中服务间调用都已包含了负载均衡能力。
过滤器是网关重要的一个环节,前置过滤器位于收到请求之后、处理请求之前,通常可以用于生成调用链所需要的traceID(如果客户端不生成的话);路由过滤器位于调用下游服务之前,可以用于服务灰度或者所谓的A/B测试;而后置过滤器位于处理完请求之后,返回响应之前,可以在响应中补充traceID,以确保整个调用链可跟踪。
至于验证,我们单独拎一小节来说说。

6.验证

微服务架构完全不同于早期的单体架构,客户端请求带上cookie与服务端session进行匹配校验,微服务总是强调“无状态”,因此需要一套新的验证体系来保护微服务,其中以auth2.0最为常见,而其核心即是以计算资源来代替存储资源
auth2.0的基础模型包含三个角色,用户所访问的应用程序、验证服务器、应用程序尝试访问的受保护资源。对应于微服务体系,网关可以理解为用户所访问的应用程序,网关所调用的下游服务可理解为受保护的资源,而验证服务器可以单独作为一个服务而存在。
auth2.0有包含几种模式:授权码模式、用户密码模式、implict模式、客户端凭据模式。其中授权码模式最为经典,其模型还包含了发起请求的客户端。我们分别用A、B、C、D代指用户所访问的应用程序、验证服务器、应用程序尝试访问的受保护资源、请求发起的客户端,授权码模式的大致过程即:

  1. D访问A,A需要请求C的资源;
  2. C授权B进行验证;
  3. D跳转访问B进行验证;
  4. 验证成功后D拿到授权码,跳转回A;
  5. A拿到授权码,再向B请求,拿到token;
  6. A拿到token,再向C请求资源,C根据token与B进行通信验证,验证通过,返回资源给A;
  7. A将资源返回给D。

这种模式常见于我们访问各类网站使用三方登录,比如访问知乎使用微博账号登录等等,可以拿到微博的昵称、头像等等,即受保护的资源。
而诸如用户密码模式、implict模式都是某种程度上的简化,但是我们也可以看出,auth2.0在某种程度上也属于范式,他并没有给出其核心部分token校验的具体规则。这里就涉及到各种token校验方案,比如JWT(JSON WEB TOKEN),当然这也是一个值得去学习的议题,受限于篇幅,就不做过多展开。JWT校验原理即拿摘要进行加密后与签名进行比较,如果相等即校验成功,这与HTTPS过程中证书的校验非常类似。这也是计算资源代替存储资源的内核。

7.消息

常见分布式消息中间件如kafka、rabbitMQ、rocketMQ。但不同的消息中间件侧重点有所区别,kafka始于日志数据传输,因此时至今日还可以看到kafka应用在日志采集分析ELK+kafka的经典标配之中。而如rabbitMQ、rocketMQ大量使用在诸如电商等场景,与分布式事务有着千丝万缕的联系。例如保证事务最终一致性,像阿里有基于rocketMQ可靠消息的方案,对于TCC这类方案是一个有效补充。
消息队列与分布式事务,是两个庞大的议题,同样值得好好学习。

8.日志与链路跟踪

日志方面如上述常见的kafka+ELK(elasticsearch、logstash、kibana)经典套餐,链路跟踪如基于opentracing标准的zipkin、jaeger等等。

9.容器化

容器与微服务有种天然的契合度。k8s的service-deployment-pod结构与微服务的理念非常接近,服务间的调用也天然具备负载均衡的能力,确实已经包含了微服务所需要实现的许多特性。但若说依靠k8s就能实现微服务还是不严谨,很多工作都需要深入到开发语言级别才能完成,比如服务容错的舱壁模式。
但是借助k8s,服务的多实例拉起、弹性扩容、版本回滚、配置地址域名化(基于service的名称)都变得非常简单。这也是为什么这么多年大家喜欢将微服务与k8s结合一起使用,他的便利性显而易见。

结语

陆陆续续概括了微服务所涉及的一些知识点,篇幅有限也只能概括出冰山一角,可以看得出,微服务是一个庞大的知识体系,每一个所涉及的细分领域都值得长篇大论好好研究,路漫漫其修远兮我们还要继续学习。