我终于搞懂了微服务,太不容易了...( 二 )


但这种方式对业务代码入侵较高,XML 配置有变更时候,服务消费者和服务提供者都需要更新 。
IDL:是接口描述语言,常用于跨语言之间的调用,最常用的 IDL 包括 Thrift 协议以及 gRpc 协议 。
例如 gRpc 协议使用 Protobuf 来定义接口,写好一个 proto 文件后,利用语言对应的 protoc 插件生成对应 server 端与 client 端的代码,便可直接使用 。
但是如果参数字段非常多,proto 文件会显得非常大难以维护 。并且如果字段经常需要变更,例如删除字段,PB 就无法做到向前兼容 。
一些 Tips:不管哪种方式,在接口变更的时候都需要通知服务消费者 。消费者对api的强依赖性是很难避免的,接口变更引起的各种调用失败也十分常见 。
所以如果有变更,尽量使用新增接口的方式,或者给每个接口定义好版本号吧 。在使用上,大多数人的选择是对外 Restful,对内 XML,跨语言 IDL 。
一些问题:在实际的服务发布与引用的落地上,还会存在很多问题,大多和配置信息相关 。
例如一个简单的接口调用超时时间配置,这个配置应该配在服务级别还是接口级别?是放在服务提供者这边还是服务消费者这边?
在实践中,大多数服务消费者会忽略这些配置,所以服务提供者自身提供默认的配置模板是有必要的,相当于一个预定义的过程 。
每个服务消费者在继承服务提供者预定义好的配置后,还需要能够进行自定义的配置覆盖 。
但是,比方说一个服务有 100 个接口,每个接口都有自身的超时配置,而这个服务又有 100 个消费者,当服务节点发生变更的时候,就会发生 100*100 次注册中心的消息通知,这是比较可怕的,就有可能引起网络风暴 。
服务的注册与发现
假设你已经发布了服务,并在一台机器上部署了服务,那么消费者该怎样找到你的服务的地址呢?
也许有人会说是 DNS,但 DNS 有许多缺陷:

  • 维护麻烦,更新延迟
  • 无法在客户端做负载均衡
  • 不能做到端口级别的服务发现
其实在分布式系统中,有个很重要的角色,叫注册中心,便是用于解决该问题 。
我终于搞懂了微服务,太不容易了...

文章插图
 
使用注册中心寻址并调用的过程如下:
  • 服务启动时,向注册中心注册自身,并定期发送心跳汇报存活状态 。
  • 客户端调用服务时,向注册中心订阅服务,并将节点列表缓存至本地,再与服务端建立连接(当然这儿可以 lazy load) 。发起调用时,在本地缓存节点列表中,基于负载均衡算法选取一台服务端发起调用 。
  • 当服务端节点发生变更,注册中心能感知到后通知到客户端 。
注册中心的实现主要需要考虑以下这些问题:
  • 自身一致性与可用性
  • 注册方式
  • 存储结构
  • 服务健康监测
  • 状态变更通知
①一致性与可用性
一个老旧的命题,即分布式系统中的 CAP(一致性、可用性、分区容错性) 。
我们知道同时满足 CAP 是不可能的,那么便需要有取舍 。常见的注册中心大致分为 CP 注册中心以及 AP 注册中心 。
CP 注册中心:比较典型的就是 Zookeeper、etcd 以及 Consul 了,牺牲可用性来保证了一致性,通过 Zab 协议或者 Raft 协议来保证一致性 。
AP 注册中心:牺牲一致性来保证可用性,感觉只能列出 Eureka 了 。Eureka 每个服务器单独保存节点列表,可能会出现不一致的情况 。
从理论上来说,仅用于注册中心,AP 型是远比 CP 型合适的 。可用性的需求远远高于一致性,一致性只要保证最终一致即可,而不一致的时候还可以使用各种容错策略进行弥补 。
保障高可用性其实还有很多办法,例如集群部署或者多 IDC 部署等 。Consul 就是多 IDC 部署保障可用性的典型例子,它使用了 wan gossip 来保持跨机房状态同步 。
②注册方式
有两种与注册中心交互的方式,一种是通过应用内集成 SDK,另一种则是通过其他方式在应用外间接与注册中心交互 。
应用内:这应该就是最常见的方式了,客户端与服务端都集成相关sdk与注册中心进行交互 。
例如选择 Zookeeper 作为注册中心,那么就可以使用 Curator SDK 进行服务的注册与发现 。
应用外:Consul 提供了应用外注册的解决方案,Consul Agent 或者第三方 Registrator 可以监听服务状态,从而负责服务提供者的注册或销毁 。
而 Consul Template 则可以做到定时从注册中心拉取节点列表,并刷新 LB 配置(例如通过 Nginx 的 upstream),这样就相当于完成了服务消费者端的负载均衡 。


推荐阅读