概述

服务注册与发现是微服务的核心,否则新发布一个服务只能去调用方配置地址,不能接受的事。不管是rpc还是spring cloud这种Http调用,注册中心都不可少。


这是dubbo的基本结构,但几乎所有服务发现的注册中心都这样。服务提供方注册到注册中心,消费方订阅或者拉取提供者信息,发起调用。

客户端设计

客户端比较简单:
1. 从注册中心拉取服务信息
2. 维持服务信息缓存
3. 负载均衡和路由

不过说简单也不简单,根据注册中心使用的技术不同,实现方式不同。比如使用zk,consule等中间件自带通知功能,集成中间件客户端,做好订阅即可。eureka采用定时Http拉取的方式,可以自己开发多语言客户端,按照注册中心提供的接口实现。

不过,只是拉取(poll)并不是很好,频率低无法及时获取服务更新信息,频率太高增加注册中心的负载,而且大部分拉取都是没变化的。一直保持长连接接收推送也不是很好的方案,可以考虑长轮询的方式(实现参考nacos配置监听代码LongPollingService)。

long polling

另外,nacos服务发现部分的通知方式又作了升级,不再hold客户端请求,而是通过udp通知。和long polling类似,客户端每10s查询一次,不再hold,保存客户端信息后直接返回。在下一个10秒来临前,如果instance有变更,直接从缓存捞取所有相关客户端,发送udp通知。但需要客户端开启一个udp端口并且能被访问。

负载均衡一般有一致性hash、轮询、加权轮询等,比较成熟,暂且不表。路由比较重要,灵活的路由能实现分流、降级、灰度发布、金丝雀发布、容灾等能力,路由信息配置应该有个管理后台,动态修改并实时生效。有如下路由规则:

  • ip: 指定ip路由,方便有时候测试和定位问题。
  • 地域(Region)、区域(Zone): 多区域部署实现容灾,同时优先Zone内调用降低网络延迟。
  • 服务分组:有可能需要部署多组服务分别不同消费方使用。
  • 服务版本:版本化,支持灰度。
  • 其他可选:故障注入、熔断、流量镜像等。

有的服务发现通过注册中心的proxy代理实现负责均衡和路由,这种中心化的设计并不好,对proxy的性能要求极高,从而成为瓶颈点,一般都从客户端直接调用服务提供方。

有的服务发现为了实现多语言,在客户端部署了一个agent,通过agent跟注册中心通讯,对客户端调用透明,但是增加了部署复杂度。比如通过边车模式实现的 Netflix Prana。不过service mesh的到来反而做到解耦业务和网络配置,方便升级、支持多语言等,在k8s生态下部署也不是问题。

服务提供者设计

服务端:
1. 注册
2. 续租(心跳、定时上报)
3. 下线

启动完成后,调用注册中心注册服务信息,然后定时上报,除了告知自己还活着外,还可以上报健康状态。因为活着不代表健康,比如某个中间件连不上等,spring boot可以检查/health

注意,注册是服务正常启动完成后才开始,如果无法做到启动成功才上报,可以延时注册,否则客户端发起调用时,服务提供方其实还没准备好。

好的方式有个服务状态机:UP,DOWN,STRATING等,启动时注册服务时,status为STARTING,后续的心跳更新为UP。Eureka就是这样的方式。

容易出现的问题是服务下线时,注册中心没有及时下线,导致请求还是被路由到已经关闭(或者关闭中)的提供者,一般客户端会写一个shutdown hook通知注册中心下线。不过客户端可能直接被kill,或者消费者本地缓存没更新,仍然存在问题。以其纠结半天,不如弯道解决问题,通过应用启停脚本,先主动下线,隔几秒再stop提供者。

注册中心

市面上开源的可以用作注册中心的中间件主要是zk,console,etcd等。double用的zk、nacos,docker swarm使用consul,老版本的kubernate dns使用etcd。

这些中间件不是为服务发现而生,大部分保证了CAP定律的一致性、分区容错性,但不能保证每次请求都可用。对服务注册发现来说,我们更希望是AP,可以容忍短暂不一致,但必须可用。具体可参考:Why not use Curator/Zookeeper as a service registry?

cap理论

总结,目前实现服务发现的注册中心有三种方式:
1. 使用中心化一致性存储中间件,如zk(Paxos算法),etcd(Raft算法);
2. 使用传统DNS+新的一致性算法,如SkyDNS、Spotify;
3. 去中心化,弱一致性实现如Eureka。

上文提到的nacos采用的是raft算法保证集群数据一致。下文主要介绍Eureka,注册信息维护在内存中,不需选主,集群间同步注册信息,可能有短暂的数据不一致,但保证可用性。

Eureka

Eureka高可用架构图:

服务端注册什么?

eureka server主要通过一个嵌套ConcurrentHashMap维护注册信息:

 ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>  registry = new   ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>();
复制代码

外层map的key为服务名(要求服务名唯一、好像没有实现namespace隔离),内层map的key为实例ID,用以区分相同服务的不同实例。Lease对象持有InstanceInfo,还有一些属性如lastUpdatetime。InstanceInfo类就是服务信息,比如ip,port,host等。下面是数据结构示例:

{"name":"my-app-name","instance":[{"instanceId":"192.18.99.100:my-app-name:60000","hostName":"192.18.99.100","app":"my-app-name","ipAddr":"192.18.99.100","status":"UP","overriddenstatus":"UNKNOWN","port":{"$":8080,"@enabled":"true"},"securePort":{"$":443,"@enabled":"false"},"countryId":1,"dataCenterInfo":{"name":"MyOwn"},"leaseInfo":{"renewalIntervalInSecs":30,"durationInSecs":90,"registrationTimestamp":1525342799736,"lastRenewalTimestamp":1525364448834,"evictionTimestamp":0,"serviceUpTimestamp":1525242780899},"metadata":{},"homePageUrl":"http://192.18.99.100:8080/","statusPageUrl":"http://192.168.99.100:8080/info","isCoordinatingDiscoveryServer":"false","lastUpdatedTimestamp":"1525342799736","lastDirtyTimestamp":"1525342797179","actionType":"ADDED"}]
}
复制代码

服务如何下线?

不考虑数据同步间隔,一般移出不健康的服务用4种方式:
1. 停服务时,通过shutdown hook调用server,主动下线服务;
2. eureka server有一个定时任务检查一段时间没有心跳的服务,把它从列表剔除;
3. 服务健康检查不通过时,通过心跳上报给server,服务不被剔除,但被标记为down,客户端也不会访问。
4. 直接调用eureka api,把服务标为offline,同down类似。

eureka 集群如何同步?

eureka集群去中心化,客户端和不同的server通讯,eureka间同步有3个问题(eureka 特指eureka server):
1. 如图:如果某服务S,先向eurekaA注册,再向eurekaB注册,然后eurekaA向eurekaB同步。此时,同步信息会不会覆盖?
2. 服务S只向eurekaA注册,如果eurekaA向eurekaB同步失败,访问B的客户端是不是一直无法获取服务S的信息?
3. 服务多、eureka集群大时,eureka集群同步压力太大?

针对第一个问题,eureka通过时间戳和status判断新旧,始终以新版本为主;

第二个问题通过心跳解决,如果A->B失败,心跳再次达到A,还是会向B发送心跳,往B心跳结果404,则A重新把S注册到B。

第三个问题确实是个瓶颈,不过eureka通过“启动一次拉取,之后走批量、增量同步”的方式改善性能。

总结同步流程如下:
1. server启动时去其他server全量拉去
2. 启动后,其他客户端会自动增量同步(走批量接口)
3. 如果同步失败,通过心跳实现补偿逻辑。

eureka还做了哪些性能改善点?

eureka本身有点像一个缓存架构的设计,当然,其中为了改善性能也使用了缓存如guava cache,还有overriddenInstanceStatusMap,recentlyChangedQueue等队列。部分采用异步编程,gzip压缩等。

eureka的问题有哪些?

  1. 本身没有灰度功能(可以添加metadata信息自由扩展,然后在客户端扩展路由规则)
  2. 服务粒度太粗,客户端并不需要的信息也拉取,如果有上千个服务,难道客户端还维护那么多实例信息?
  3. 各个时间配置不好衡量(心跳时间,服务拉取时间等),需要改三四个配置以适应自己的服务。
  4. 没有权限控制;如果我起了一个服务,跟其他服务同名,且不是把浏览偷偷分过来了。
  5. 服务提供方也没有权限控制,可以任意调用server注册的服务。
  6. 控制台功能简陋,只有简单的查看注册列表的功能。

总之,如果要使用eureka,需要针对以上问题扩展很多细节。之所以单独研究它,也因为它问题多多,再看其他框架设计时更能理解相比Eureka的优缺点。

Gossip

如果想了解dns加持的方式,kubernate是一个很好的研究示例,通过一个dns服务还有iptables的方式实现服务发现和路由。这里再介绍一个新玩法Serf。

Eureka是去中心化,弱一致性,但还是有一个AP系统的注册中心集群。Serf的玩法是,完全去中心,不再需要一个注册中心,所有服务组成一个大集群,大概如下:

每个服务都是网络中的一个节点,每个节点都随机与其他节点通讯,最终达成一致,使得每一个节点都可能知道网络中的其他节点。Serf就是gossip算法的实现。看到这种分布式网络中通讯的容错问题,马上想到区块链,P2P。

Serf并不能解决服务发现所有需求,虽然解决了大集群网络中的容错性,但在几千个服务节点中,节点信息传播效率我没作测试。不过这是一个新的玩法,且gossip协议在集群信息同步上用得越来越多,比如consul不同数据中心的同步,Cassandra集群信息的同步都是通过gossip实现。

Apache还有一个正在孵化的项目incubator-gossip。

总结

本文通过研究一些开源服务注册发现框架,总结其设计要点。

[1]. Netflix github
[2]. serf
[3]. service-discovery-in-the-cloud
[4]. Gossip_protocol
[5]. nacos

服务发现-注册中心概述相关推荐

  1. 服务发现 注册中心 consul 的介绍、部署和使用

    什么是服务发现 微服务的框架体系中,服务发现是不能不提的一个模块.我相信了解或者熟悉微服务的童鞋应该都知道它的重要性.这里我只是简单的提一下,毕竟这不是我们的重点.我们看下面的一幅图片: 图中,客户端 ...

  2. java 服务注册中心_服务治理的含义和java最流行的微服务框架服务治理注册中心的搭建...

    原标题:服务治理的含义和java最流行的微服务框架服务治理注册中心的搭建 Spring Cloud Eureka基于Netflix Eureka做了二次封装,是Spring Cloud Netflix ...

  3. 为什么redis在陌陌上使用的这么广泛?当NoSQL数据库,微服务的注册中心,RPC的调用协议都能用

    前言 说到redis,可能大家的脑海中蹦出的关键词是:NoSQL.KV.高性能.缓存等.但今天的文章从另一个角度--微服务来展开. 这篇文章的起因也是源自一次面试经历,在面试一位来自陌陌的候选人(就是 ...

  4. 微服务:注册中心的作用

    1.为什么需要服务注册中心 微服务时代的服务管理 在微服务时代,我们所有的服务都被劲量拆分成最小的粒度,原先所有的服务都在混在1个server里,现在就被按照功能或者对象拆分成N个服务模块,这样做的好 ...

  5. .Net Core with 微服务 - Consul 注册中心

    上一次我们介绍了 Ocelot 网关的基本用法.这次我们开始介绍服务注册发现组件 Consul 的简单使用方法. 服务注册发现 首先先让我们回顾下服务注册发现的概念. 在实施微服务之后,我们的调用都变 ...

  6. 微服务:注册中心ZooKeeper、Eureka、Consul 、Nacos对比

    前言 服务注册中心本质上是为了解耦服务提供者和服务消费者.对于任何一个微服务,原则上都应存在或者支持多个提供者,这是由微服务的分布式属性决定的.更进一步,为了支持弹性扩缩容特性,一个微服务的提供者的数 ...

  7. 一文吃透何为微服务、网关、服务发现/注册?

    点击上方"Java基基",选择"设为星标" 做积极的人,而不是积极废人! 每天 14:00 更新文章,每天掉亿点点头发... 源码精品专栏 原创 | Java ...

  8. 一文吃透何为微服务、网关、服务发现/注册

    Part1序 随着互联网业务复杂性慢慢提高,单机服务的架构慢慢出现了生产效率问题 微服务架构带来的有优点也有缺点,使用前需要调研清楚 微服务架构的网关设计.服务注册/发现.配置管理都是关键点 Part ...

  9. 何为微服务、网关、服务发现/注册?

    点击上方"芋道源码",选择"设为星标" 管她前浪,还是后浪? 能浪的浪,才是好浪! 每天 10:33 更新文章,每天掉亿点点头发... 源码精品专栏 原创 | ...

最新文章

  1. java表或视图不存在_Error querying database. Cause: java.sql.SQLSyntaxErrorException: ORA-00942: 表或视图不存在...
  2. PHP ----MySQL 数据库
  3. echart(2),模拟数据导入篇
  4. APP应用下载站源码-带后台
  5. haproxy安装配置及haproxy+keepalived简单配置
  6. iOS开发 - 在状态栏显示FPS,CPU和内存信息
  7. 阶段5 3.微服务项目【学成在线】_day02 CMS前端开发_23-CMS前端页面查询开发-分页查询实现...
  8. 计算机科学与技术专业校友会排名,校友会2017中国民办大学计算机科学与技术专业排行榜...
  9. Android 实现电话拨号界面带按键声demo
  10. pycharm连接不上远程服务器
  11. 塔夫茨计算机科学,塔夫斯大学计算机科学博士专业详情及要求是怎样的?
  12. 微信属于计算机操作系统吗,一款国产操作系统的微信电脑版使用体验
  13. Wap与Net之争!!
  14. 一些常见的AAC编解码库
  15. 程序员与爱情不仅只是擦肩而过
  16. APP网络测试要点及弱网模拟
  17. 解锁三星bl锁有几种方法_三星手机通用解锁教程 官方解锁Bootloader教程
  18. 数字文明时代需要更好的GDP
  19. 【SQL进阶教程】第一章 case表达式
  20. 试试smartdraw

热门文章

  1. 小白终是踏上了这条不归路----小文的mysql学习笔记(4)----常见的基本函数
  2. 常见宽带路由器设置方法汇总
  3. 苹果会下架所有的基于H5的开发的APP?
  4. 快速批量修改word文档内容
  5. 个人云盘、企业云盘傻傻分不清楚?3分钟带你清晰两者差异!...
  6. 论文阅读 (58):Research and Implementation of Global Path Planning for Unmanned Surface Vehicle Based...
  7. 第一章 教育基础(03 教育目的与教育制度)
  8. 完美云主机购买小技巧分享
  9. linux文件中的^M
  10. i3-10110U和i5 10210u 哪个好