hello,小编这周比较忙,一直没有写相应的博客,今天抽空写一下dubbo应用的进阶第一篇,本文参考了dubbo官方文档服务化最佳实践。融入小编自己的理解与自己多年dubbo项目的经验,此篇前半部分多讲的是开发流程的方案,对于dubbo监控小编一笔带过了,后半部分为redis作为注册中心主要的机制,小伙伴或许会问不是都用zookeeper作为注册中心,是的官方也推荐zookeeper,后续小编会继续带来zookeeper作为注册中心的源码阅读以及注册机制讲解,希望不会让大家觉得多此一举。废话不多说咱们开始。
在rpc调用的服务中,调⽤⽅是通过接⼝来调⽤服务端,传⼊参数并获得返回结果。那这样服务端的接⼝和模型必须暴露给调⽤⽅项⽬。服务端如何暴露呢,客户端如何引⽤呢?咱们用一张图说明一下
暴露接⼝的通常做法是 接⼝与实现分离,服务端将 接⼝、模型、异常 等统⼀放置于⼀个模 块,实现置于另⼀个模块。调⽤⽅通过Maven进⾏引⽤。项目中往往也采用这种做法。
当项⽬越来越多,服务依懒关系越发复杂的时候,为了提⾼协作效率,必须采⽤⾃动化⼯具 完成 接⼝从编写到构建成JAR包,最后到引⽤的整个过程。
项目流程
前期的设计包括概设详设数据库设计咱们不表,直接说自动化构建协作是怎样的流程
这样服务端定义好接口模型数据后,两端分别开发速度快。
上面的话是最理想的情况下,只需要定义好接口就行了,那假如项目同时开发,然而调用了相同的接口那,此时有个项目组队这个接口的改变,就会影响到其他项目组的开发进程如下图所示:
项⽬B、C都依懒了项⽬A 当中的接⼝1,此时项⽬B业务需要,需要接⼝1多增加⼀个参数,升级完成后。项⽬B能正确 构建上线,项⽬C却不⾏。
此时的解决方案和原则如下:
对于rpc项目的联调有时候是非常痛苦的,假如公司机器很多,环境很多,甚至到了人手一个环境,那财大气粗,这个可以在自己环境上部署所有的项目然后想怎么玩就怎么玩,但对于大部分公司而言,环境总是有限的,假如我们有A,B,C,D服务,咱们开发新功能时需要修改D服务,然后需要本机联调怎么保证咱们用公共环境的参数却调到本机上来呢。那下面小编来讲几个联调方案
说说比较简单,那小编上代码演示
上次小编搭建了一个简单的dubbo和spring-boot 整合的项目,大家可以看下链接如下:
Dubbo快速上手与spring-boot整合以及配置体系
在那篇文章的技术上做修改
application.properties 修改加入server.{application}.group的配置,中间可以替换成你的项目名称
这边无论是服务端还是消费端都要引入
dubbo.application.name=boot-server
dubbo.protocol.name=dubbo
dubbo.protocol.port=-1
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.provider.timeout=6000
server.member.group = member
在实现的service层注解修改,引用端修改
@DubboService(group = "${server.member.group}")
public class UserServiceImpl implements UserService {
}
@DubboReference(group = "${server.member.group}")
private UserService userService;
让我们启动多台服务
我们copy一份如下图
可以在program arguments上加入也可以在vm options上加入 上面配置加-Dserver.member.group即可,效果一样。然后我们调整客户端的配置,修改server.member.group= temp-member再次调用效果
无论怎么刷新都是temp_member
在reference 中指定提供者的url即可做到直连
xml形式,客户端直接写死url本地的
<dubbo:reference url="dubbo://127.0.0.1:20880" id="demoService" timeout="2000" interface="com.tuling.teach.service.DemoService" check="false"/
代码写死配置上先写 application.url =dubbo://127.0.0.1:20880
@DubboReference(url = "${application.url}")
private UserService userService;
小编这边不做演示
⼀个项⽬有可能即是服务提供者⼜是消费者,在测试时需要调⽤某⼀服务同时⼜不希望正在开发的服务影响到其它订阅者如何实现? 通过修改 register=false 即可实现
dubbo有个管理后台和监控中心的搭建这边小编不做赘述,大家可以看这篇博客写得很详细
Dubbo管理控制台与监控中心的搭建
注:dubbo-admin 的版本并没有统⼀的管理,没有稳定分⽀,⽬前试了只有master-0.2.0 比较稳定实用。
为了到达服务集群动态扩容的⽬的,注册中⼼存储了服务的地址信息与可⽤状态信息,并实时推送给订阅了相关服务的客户端。
一个完整的注册中心需要实现以下功能:
关于Redis注册中心我们需要了解两点:
数据结构:
redis 注册中心存储了服务发布、订阅信息。他们以redis中的map字段进行存储,数据结构如下:
服务发布
/dubbo/{服务名}/providers (key)
{提供者url1}: {有效期} (value 仍然是map形式)
{提供者url2}: {有效期}
…
服务订阅
/dubbo/{服务名}/consumers
{订阅者url1}: {有效期}
{订阅者url2}: {有效期}
…
小编以redis客户端来看一下上面的数据结构
⼀个注册中⼼存储了多个服务、每个服务对应多个提供者和订阅者。每个服务的提供者的URL 作为接⼝map中的⼀个Key,与之对应的value 就是该提供者的有效期。有效期通常只有60 秒,dubbo通过⼀个维护线程,每隔30更新该时间。
这个源代码在org.apache.dubbo.registry.redis.RedisRegistry#deferExpired
大家有兴趣可以看一下啊,这里小编理解为心跳机制不断的报活。
发布与订阅流程
Dubbo使用Redis的发布订阅特性实现
提供者与消费者之前数据实时同步其原理如下:
Redis 发布订阅(pub/sub)是一种消息通信模式:
发送者(pub)发送消息,订阅者(sub)接收消息。
Redis 客户端可以订阅任意数量的频道。
下图展示了频道channel1,以及订阅这个频道的三个客户端 -client2 、client5 和client1之间的关系:
#进入redis 客户端
telnet 127.0.0.1 6379
#创建一个 channel
set name Bob
#订阅该channel,此时客户端处于阻塞状态,等待接收消息
subscribe name
#开启一个新的客户端
telnet 127.0.0.1 6379 9
#为该 channel 发布消息
publish name “luban uncle”
dubbo使用redis注册中心的流程
在提供者和消费者上线上时,分别会进行发布与订阅事件,
其主体流程如下:
消费端
a. 启动:注册消费者信息
b. 启动:启动一个阻塞线程,订阅(subscribe)该接口事件
c. 停止:删除接⼝消费信息
d. 停止:停止订阅线程
提供端
a. 启动:注册提供者信息
b. 启动:推送(publish) 接口注册事件
c. 停止:删除接口提供者信息
d. 停止:推送(publish) 接口注销事件
相关源代码如下
org.apache.dubbo.registry.redis.RedisRegistry#doSubscribe// 订阅 org.apache.dubbo.registry.redis.RedisRegistry.Notifier#run// 订阅线程,阻塞读取 org.apache.dubbo.registry.redis.RedisRegistry.NotifySub#onMessage// 变更通知 org.apache.dubbo.registry.redis.RedisRegistry#doRegister // 注册 org.apache.dubbo.registry.redis.RedisRegistry#destroy // 消费
今天小编主要讲了dubbo作为项目rpc调用框架的主要使用和一些方案,重点主要为redis作为注册中心的原理,后续小编会对其源码做解读,并且继续讲解当注册中心为zookeeper的时候是怎样的流程。谢谢
感谢源码阅读网鲁班大叔讲解
参考了dubbo官方文档以及监控时大佬的相关博客