远程方法调用(RPC)

远程方法调用RPC使用广泛,虽然跟MQ本质无必然联系,但是在分布式应用中经常会互相融合。MQ重点解决系统耦合问题,提供丰富的消息模式支持,也因此MQ的消息更偏向于单向与责任链设计。RPC则依然耦合了客户端与服务端时间序列,消息是双向的。

RPC的实现可以非常多选择,也无标准可言,底层通道可以是TCP,也可以基于HTTP,甚至也可以是更底层协议。目标可以简化为:

封装远程方法[名字+参数] -->--+                 +--->解析出方法+参数调用本地方法
                           |<==> 网络传输<==> |
调用结果 <------------------+                 +<---封装调用结果

网络传输协议有各种选择(TCP/HTTP...),如何封装方法+参数也可以各种选择,比如JSON,XML甚至二进制底层的协议(ProtoBuffer,Thrift等等)。

ZBUS的选择,

1) 底层通信协议: 基于MQ消息传输(TCP),之上支持HTTP直接访问
2)RPC请求应答协议: 默认JSON封装, 可替换

ZBUS的RPC的优势在于,继承了MQ消息特性,直接支持了HA高可用特性,减少NameServer等重复开发,天生融合MQ与RPC。 选择JSON是一个折中: 动态能力、通信效率和协议标准化三项指标的平衡。当然也支持个性化选择,需要扩展zbus的RpcCodec来完成。

RPC可讨论的主题众多,诸如高可用,超时,重试,熔断,限流,版本控制等等,zbus并没有全部解决,比如熔断。因为这些主题有不少仍然存在争议,在不同应用场景下会有完全不同的结论,比如自动重试在交易系统中是一个非常危险的选择。

ZBUS的RPC在应用设计上追求了极少依赖的目标,给应用程序提供更换的更大可能,也给zbus本身升级带来平滑。

仍然以Java语言示例,其他语言请参考各自示例。

RPC服务端

手动加载服务

public static void main(String[] args) throws Exception {   
    ServiceBootstrap b = new ServiceBootstrap();

    //手动增加模块,使用默认构造,也可以指定模块细节
    b.addModule(InterfaceExampleImpl.class); 
    b.addModule(GenericMethodImpl.class);
    b.addModule(SubService1.class);
    b.addModule(SubService2.class);  


    b.serviceName("MyRpc") 
     .port(15555)  //内部启动了zbus服务器,zbus与rpc服务之间不通过网络协议栈
     //.serviceAddress("localhost:15555")   //也可以通过网络链接到远程服务器上
     //.ssl("ssl/zbus.crt", "ssl/zbus.key") //启用SSL
     //.serviceToken("myrpc_service")       //启用Token权限验证
     .start();
} 

自动加载服务

public static void main(String[] args) throws Exception { 
    ServiceBootstrap b = new ServiceBootstrap();  
    b.serviceName("MyRpc")
     .port(15555) 
     .autoDiscover(true)
     .start();
}

是的,没有任何业务相关的,你已经启动了zbus的RPC服务,而业务相关的代码自动加载进来了。 侵入点:服务需要注释@io.zbus.rpc.Remote

对Spring的支持,zbus可以让应用简单几行配置享用zbus的RPC特性

<bean class="io.zbus.examples.rpc.biz.InterfaceExampleImpl"></bean>
<bean class="io.zbus.examples.rpc.biz.generic.GenericMethodImpl"></bean> 
<bean class="io.zbus.examples.rpc.biz.inheritance.SubService1"></bean> 
<bean class="io.zbus.examples.rpc.biz.inheritance.SubService2"></bean>  

<bean class="io.zbus.rpc.bootstrap.SpringServiceBootstrap" init-method="start">
    <property name="serviceName" value="MyRpc"/> 
    <property name="serviceToken" value="myrpc_service"/> 
    <property name="autoDiscover" value="true"/>  
    <property name="port" value="15555"/>
</bean>

除此之外你需要关心的可能就是监控与问题排查的时候了解zbus。

RPC客户端

ClientBootstrap b = new ClientBootstrap(); 
b.serviceAddress("localhost:15555")
    .serviceName("MyRpc")
    .serviceToken("myrpc_service"); 

RpcInvoker rpc = b.invoker();   //可以通过该RpcInvoker调用底层同步、异步各种API能力
InterfaceExample api = rpc.createProxy(InterfaceExample.class); //最常用是动态代理出实现类,隔离掉zbus细节

通过Spring进一步完全隔离

<bean id="bootstrap" class="io.zbus.rpc.bootstrap.SpringClientBootstrap">
    <property name="serviceAddress" value="localhost:15555"/>
    <property name="serviceName" value="MyRpc"/> 
    <property name="serviceToken" value="myrpc_service"/> 
</bean>

<bean factory-bean="bootstrap" factory-method="createProxy">
    <constructor-arg type="java.lang.Class" value="io.zbus.examples.rpc.biz.InterfaceExample"/> 
</bean> 

另外,高可用HA的配置并没有发生本质变化,serviceAddress地址填写多个完毕,细节参考详细示例。