微服务的服务间通信与服务治理

微服务的服务间通信与服务治理是微服务架构的实现层面的两大核心问题。

本文希望通过结合扇贝生产环境中的实践(百万日活场景),给大家分享相关的经验心得。

服务间通信

从通信类型的角度看,大概有三种类型:同步调用,异步调用,广播。

在微服务的设计之初要想清楚调用关系以及调用方式,哪些需要同步,哪些可以异步,哪些需要广播,最好团队内部要有统一的认识。

然后就是要确定好调用协议了,例如常见的选择有:


  • 同步调用:HTTP REST, gRPC, thrift, etc.
  • 异步调用:Sidekiq, Celery, etc. 对应的backend有 Redis, 各类 AMQP 实现, Kafka 等等
  • 广播:各类 AMQP 实现, Kafka,etc.

对于如何选择,我们需要从很多角度去思考。网上有大量的 "X vs Y"的文章或问答。其实无非就是从几个角度:性能,成熟度,易用性,生态,技术团队。我们的建议是:优先选择社区活跃,以及和自己团队技术栈相融的。社区活跃的技术往往代表了趋势和生态,而团队技术栈的相容性则是能否落地成功的保证。

比如当时扇贝选择gRPC作为同步调用的接口协议。主要考虑的就是两点:1. 社区活跃,发展非常迅速;2.基于 HTTP/2,与我们的技术栈相容。

除了选择协议,更加重要的应该就是接口文档的管理了。最好接口文档和代码是强相关的,就像 gRPC 的 proto 和生成出来的代码那样。否则人往往是有惰性的,很有可能代码改了文档没改。

总之,关于服务间通信,我们需要做好:


  • 确定接口规范,什么用同步调用,什么用异步,什么用广播;同步调用用什么协议,异步用什么
  • 确定接口协议,以及思考接口文档的管理,接口文档与代码之间如何建立强联系

服务治理

服务治理是个非常大的话题,真的要铺开来讲,可能几篇文章的篇幅都讲不完。这里我们想先简单地看一下服务治理要解决的问题,以及现在的一些解决方案,发展趋势。
最后以扇贝为例,简单介绍下在一个真实的百万日活的生产环境中,服务治理是如何做的。

什么是服务治理

微服务化带来了很多好处,例如:通过将复杂系统切分为若干个微服务来分解和降低复杂度,使得这些微服务易于被小型的开发团队所理解和维护。然而也带来了很多挑战,例如:微服务的连接、服务注册、服务发现、负载均衡、监控、AB测试,金丝雀发布、限流、访问控制,等等。
这些挑战即是服务治理的内容。

现有方案

服务治理的问题由来已久,微服务化盛行之后尤为突出。主流的方案有:基于Spring Cloud或者Dubbo等框架。但是这些方案的问题是:1.对代码有侵入,也就意味着,如果想换框架,得改很多东西。2.语言特异性(Java),如果我们用的是 Go/Python,或者我们的微服务不全是 Java,就搞不定了。

Service Mesh

2017 Service Mesh 横空出世,让我们眼前一亮。网上有很多关于 Service Mesh 的介绍,大家可以去网上搜一搜。我的理解,Service Mesh 的核心思想就是“代理流量”。Service Mesh通过一个个"代理" 来为微服务转发/接收所有流量,通过控制这些代理,就可以实现服务连接,注册,发现,负载均衡,熔断,监控等等一系列服务治理相关功能,从而微服务的代码不再需要服务治理的实现,换句话说,也就是服务治理对于微服务开发者而言是透明的。如下图所示:绿色方块为微服务,蓝色方块为 service mesh 的代理,蓝色线条为服务间通讯。可以看到蓝色的方块和线条组成了整个网格。这个网络就是 Service Mesh。


目前普遍认为 Service Mesh 有两代:第一代的 Linkerd/Envoy 和第二代的 Istio/Conduit。第一代相对比较成熟稳定,可以直接用于生产环境,第二代目前(2018年初)都不完善,严重不推荐用于生产。

扇贝的 Service Mesh 是基于 Envoy 配合 Kubernetes 实现的。

gRPC 的服务发现,负载均衡和 RateLimit

这一节以扇贝为例,简单介绍下我们是如何做服务治理的。

首先介绍一些前提:扇贝的微服务全部容器化,编排系统基于 kubernetes,同步调用基于 gRPC,异步基于 celery[rabbitmq]。开发语言以 python3, nodejs, go 为主。 Service Mesh 基于 Envoy

总体的方案是:EnvoyDaemonSet 的形式部署到 kubernetes 的每个 Node 上,采用 Host 网络模式。 所有的微服务的 Pod 都将 gRPC 的请求发送到所在 NodeEnvoy,由 Envoy 来做负载均衡。如下图所示:


这里做一些详细的解释。


  1. Envoy 中的Route 类似于 NginxLocationCluster 类似于 NginxupstreamEndpoint 对应于 Nginxupstream 中的条目。
  2. 之所以选择 Envoy 而没有用 Linkerd,是因为当时 Envoy 是对 HTTP/2 支持最好的。且资源消耗更少。
  3. 之所以选择 Host 网络模式是为了最大化提高性能。
  4. 对于 Enovy 而言,服务发现就是告诉 Envoy,每个 Cluster 背后提供服务的实例的IP(对应于 Kubernetes 也就是 Pod 的IP)是什么。
  5. 最开始服务发现是利用 KubernetesDNS,因此,在创建Service的时候,要使用 ClusterIP: None
  6. 后来的服务发现是基于 KubernetesEndpoint API 实现了 EnovyEDS(这个项目我们日后会开源到 GitHub 上)。
  7. 对于 Envoy 而言,实现熔断,只要实现 rate limit service 就行了。我们基于Lyft/ratelimit 实现的 rate limit service。
  8. 微服务之间的调用情况都可以通过 Envoystatistic API反映出来,所以我们针对 statistic API做服务调用的监控报警。
  9. 同理,调用日志也可以利用 EnvoyAccess Log 来实现。

未完待续

关于服务治理,我们接下来的文章还会跟大家分享关于DevOps的实践, 监控报警系统,日志系统的构建等等内容。最后,如果你也对微服务,k8s, service mesh, devops 感兴趣,欢迎加入!

编辑于 2018-04-09

文章被以下专栏收录