从零开始写一个微型dubbo框架 -- 进阶篇

3 篇文章 0 订阅
1 篇文章 0 订阅

前言:

在写RPC之前我们需要明白一点,什么是RPC?

为了回答好这个问题,我们换个思路,什么不是RPC?

这个应该比较好回答一些了,从java来看:同一个jvm进程中的方法调用就不属于RPC,所以换言之,跨jvm进程间的方法调用就属于RPC了,将这个概念突破一下语言的限制,那就是:跨进程间的方法调用就属于RPC。

本文带大家进一步熟悉一下mini-dubbo所提供的基础功能,让大家先有个直观的认识,下一篇 《从零开始写一个微型dubbo框架 – 实战篇》将侧重探讨具体的实现


一、mini-dubbo支持的功能列表
支持功能默认实现可选实现
注册中心zookeeper(ZookeeperRegistryService)redis(RedisRegistryService)
负载均衡随机(RandomLoadBalance)轮询(PollingLoadBalance)
动态代理jdk-
序列化google gson-
编解码LengthFieldBasedFrameDecoder、LengthFieldPrepender-
通信netty4-
通信协议自定义(RpcProtocol)-
ioc容器google guice-
心跳检测HeartbeatHandler-
失败重试ReconnectHandler-
重写默认配置RpcContext-

二、服务端启动过程解析
2.1 “零配置”

mini-dubbo的“零配置”是建立在启用默认配置的基础上来实现的,所以在启动服务之前需要初始化默认配置

2.1.1 默认配置

如下配置意味着完全使用了mini-dubbo设置的默认值,例如:注册中心使用的是zookeeper,连接的是本地127.0.0.1:2181

RpcContext.initContext();
2.1.2 指定配置

多数情况下,我们都希望可以灵活的切换自己喜欢的组件,例如:我们不想使用zookeeper,而是要用redis,则使用如下配置:

RpcContext.setConfiguration(new Configuration(RedisRegistryService.class));

当然,这里依然存在一个问题:虽然指定了使用的组件,但是并没有指定Redis服务器所在的地址,所以它依然默认连接的是本地的reids服务,我们还可以进一步设置:

RpcContext.setConfiguration(new Configuration(new RedisRegistryService("redis://127.0.0.1:6379")));

如果你的redis是一个集群服务,那么还可以进一步配置如下:

RpcContext.setConfiguration(new Configuration(new RedisRegistryService("redis://127.0.0.1:6379","redis://127.0.0.1:16379")));
2.2 暴露服务

RPC服务端需要在服务正常启动后主动将自己的服务地址(ip
:port)注册到注册中心,客户端发起调用时会在注册中心找可用服务

2.2.1 服务绑定

熟悉spring ioc的朋友们都知道,spring会在服务启动的时候扫描用户项目的类路径加载被@Service、@Component、@Controller等注解修饰过的类,主要就是要完成对象初始化及依赖注入。

mini-dubbo为了追求自身的“轻量级”并没有选择去依赖spring,而是使用了google的guice来替代spring ioc

所以,在项目启动的时候我们要将需要暴露的服务显式的进行注册

RpcContext.setConfiguration(new ServerConfig());  //ServerConfig 可参考demo中的源码

所以,完整的配置就是以下两行代码:

Configuration config = new RedisRegistryService("redis://127.0.0.1:6379","redis://127.0.0.1:16379"));
RpcContext.setConfiguration(config,new ServerConfig());

最精简的配置如下:

RpcContext.setConfiguration(new RedisRegistryService("redis://127.0.0.1:6379","redis://127.0.0.1:16379")),new ServerConfig());
2.2.2 启动服务

由于mini-dubbo通信层只使用了netty,所以启动mini-dubbo实际上就是启动了netty的服务端,在服务启动完成后再把服务端的地址(ip:port)注册到注册中心,如下代码则是完成了这个启动与注册的过程:

RpcContext.getBean(Protocol.class).export();

至此,服务端就启动完成了


三、客户端启动的过程

相对于服务端而言,客户端启动就简单一些了,说简单其实就是少了一个服务绑定、注册的环节,实际上启动的过程也不是在这个阶段完成的,真正的客户端启动发生在发起调用时(继续往下看“RPC调用过程解析”)

3.1 连接注册中心
RpcContext.setConfiguration(new Configuration(new RedisRegistryService("redis://127.0.0.1:6379")));
3.2 获取代理对象

在java中直接使用类名调用的方法只能是静态方法,而我们客户端只有一个接口名称,并且接口下面的方法也不一定是静态方法,所以我们需要先获取一个对象才能发起调用。mini-dubbo使用jdk的动态代理来获取一个对象,代码如下:

GreetingService greetingService = ProxyFactory.getProxyObject(GreetingService.class);

发起rpc调用

greetingService.sayHello("mini-dubbo");

四、RPC调用过程解析

4.1 封装rpc协议
Invocation invocation = new Invocation();
invocation.setInterfaceName(clz.getName()); invocation.setMethodName(method.getName());
invocation.setParameterType(Util.getParameterTypes(method.getParameterTypes()));
invocation.setArguments(args);
4.2 发送netty消息

封装RpcProtocol协议对象发起调用消息,然后使用Object的wait(), 让当前线程处于阻塞状态,等待处理结果的返回

# 代码片段
RpcProtocol protocol = new RpcProtocol();
protocol.setType(Constants.getType(Invocation.class));
protocol.setRequestId(requestId);
protocol.setBody(invocation);
channel.writeAndFlush(protocol);
4.3 服务端执行方法

使用反射机制执行方法

# 代码片段
method.invoke(RpcContext.getBean(clz),arguments))
4.4 返回netty消息

将返回结果封装为RpcProtocol协议对象,并发送结果消息

# 代码片段
Result result = new Result();
result.setReturnType(method.getReturnType().getName());
result.setData(method.invoke(RpcContext.getBean(clz),arguments));
protocol.setBody(result);
protocol.setType(Constants.getType(Result.class));
ctx.channel().writeAndFlush(protocol);
4.5 获取RPC调用结果

唤醒处于等待状态的线程获取返回结果

# 代码片段
Result result = AbstractInvoker.getResult(protocol.getRequestId());
result.setReturnType(((Result)protocol.getBody()).getReturnType());
result.setData(((Result)protocol.getBody()).getData());
result.notifyResult();

五、相关资源

示例代码

点击下载:mini-dubbo-demo

源码地址

https://github.com/applesline/mini-dubbo.git

姐妹篇

《从零开始写一个微型dubbo框架 – 体验篇》
《从零开始写一个微型dubbo框架 – 实战篇》
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值