这篇文章的起因也是源自一次面试经历,在面试一位来自陌陌的候选人(就是那个交友的陌陌)时,他提到一点让我觉得很有意思,他说redis在陌陌被使用的非常广泛,除了常规的缓存外,某些场景下也当NoSQL数据库来使用,还用redis作为微服务的注册中心,甚至连RPC的调用协议都用了redis协议。
注册中心
最早了解到redis可以作为注册中心是从dubbo的源码中看到,但一直也没有过多的了解,因为从没听说哪家公司使用redis来做服务发现。
在dubbo中使用redis来做服务发现还是挺简单的,引入jedis依赖,将注册中心地址改为redis地址即可:
redis.clients
jedis
2.9.0
dubbo.registry.address=redis://127.0.0.1:6379
注册上来的数据是这样,类型是hash
/dubbo/ s e r v i c e / {service}/ service/{category}
如
/dubbo/com.newboo.sample.api.DemoService/consumers
/dubbo/com.newboo.sample.api.DemoService/providers
hash数据结构下保存的key是注册上来的url,value是过期时间
127.0.0.1:6379> hgetall /dubbo/com.newboo.sample.api.DemoService/providers
《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》
【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享
172.23.233.142:20881/com.newboo.sample.api.DemoService?anyhost=true&application=boot-samples-dubbo&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=com.newboo.sample.api.DemoService&metadata-type=remote&methods=sayHello&pid=19807&release=2.7.8&side=provider×tamp=1621857955355"
从理论上来说,注册中心只要符合数据存储、监听推送变更、心跳检测这几个基本的功能即可。
以dubbo为例看下redis是如何利用自身特性来完成注册中心的功能( 以dubbo 2.7.8版本为例):
服务注册
provider在服务注册时,将服务提供方的url写入/dubbo/${service}/providers
下,数据类型为hash,key为提供方url,value为key的过期时间,默认为60s,可配置
写入完成后以/dubbo/${service}/providers
为key调用publish
命令发布一个register事件
provider在初始化时起一个单独的线程每隔1/2过期时间
(默认30s)时对provider进行重新重新注册并发布register事件
服务发现
获取匹配/dubbo/${service}/*
的key(此处用到了keys
命令),拿到的有这几种:/dubbo/${service}/providers
、/dubbo/${service}/routers
、/dubbo/${service}/configuators
对/dubbo/${service}/*
拿到的key进行hgetall
,拿到真实的provider列表以及配置等数据,进行组装、匹配
同时对每个subscribe的服务单独开一个线程,对/dubbo/${service}
执行psubscribe
命令阻塞等待有事件发生
从源码和测试来看,dubbo的redis注册中心不能直接用于生产环境,原因有如下两点:
使用了keys
命令,会阻塞单线程的redis,keys执行期间,其他命令都得排队
没有心跳检测这个功能,我测试了provider被kill -9
杀死后,consumer是无法感知的。但从实现上来看是想通过存储的过期时间来判断服务是否可用,即需要对比url对应的value与当前的时间,如果过期应被剔除,但这部分貌似没有实现完整
虽然dubbo的redis注册中心生产不可用,但这并不影响他可以构建一个生产可用的注册中心,陌陌就是个很好的例子。