消息框架代理 与 RPC框架 有什么区别和联系

RPC框架基本原理(一):服务注册 - 草千里 - 博客园
什么是RPC框架
RPC整个过程涉及四类对象:客户端、客户端代理、服务端和服务端代理。RPC要求客户端和服务端之间约定好调用接口和传输格式(如JSON,Xml等),客户端在调用该接口时,由客户端的代理对象负责对调用的参数(包括调用的函数名和参数等信息)进行格式转换,使之符合约定的传输格式,并通过网络传送至服务端。数据传输至服务端后,交由服务端代理对象进行格式解码,获取调用的接口和参数,最后调用服务端对象相应的方法获取结果并返回客户端。服务端的服务地址发布到ConfigServer,并推送给客户端,各种配置和规则信息通过Diamond发布订阅,服务的基本信息采集到Redis中。
基本的调用关系图如下
这张图是解释RPC注册中心经典的图,图中的ConfigServer可以看做是一个内存数据库,它主要有两个功能:
服务提供者的地址和服务信息
向服务订阅者推送所订阅的服务提供者的地址信息
这里需要注意的是:真正发起远程调用是在消费者端和服务端生产者端和ConfigServer没有直接关系,实际的RPC是一个点对点的过程,通过TCP进行通信。
架构图如下:
服务注册的流程
服务与spring容器绑定,监听容器的fresh,close等事件,进行服务的注册与关闭,
第一个服务注册时,首先校验该服务是否已经注册,如果hsf依赖的netty服务未启动,启动netty服务,这里涉及nio的轮询线程池(hsfwork),以及后端具体hsf处理线程池(bizProcess),这些线程池是同步的,可以采用listenerFuture进行异步解构。
启动好服务后,注册服务的基本信息到configserver中(注册流程分为注册前,注册,注册后)。
关键的技术
常见I/O模型
阻塞I/O(blocking I/O)
非阻塞I/O (nonblocking I/O)
I/O复用(select,poll,epoll) (I/O multiplexing)
信号驱动I/O (signal driven I/O (SIGIO))
异步I/O (asynchronous I/O (the POSIX aio_functions))
阻塞I/O(blocking I/O)
非阻塞I/O (nonblocking I/O)
用户进程其实是需要不断的主动询问kernel数据好了没有。因为需要不断地轮询,这消耗了大量的CPU的资源。
I/O复用(select,poll,epoll)
select本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理。这样所带来的缺点是:
单个进程可监视的fd数量被限制,即能监听端口的大小有限。
一般来说这个数目和系统内存关系很大,具体数目可以cat /proc/sys/fs/file-max察看。32位机默认是1024个。64位机默认是2048.
对socket进行扫描时是线性扫描,即采用轮询的方法,效率较低:
当套接字比较多的时候,每次select()都要通过遍历FD_SETSIZE个Socket来完成调度,不管哪个Socket是活跃的,都遍历一遍。这会浪费很多CPU时间。如果能给套接字注册某个回调函数,当他们活跃时,自动完成相关操作,那就避免了轮询,这正是epoll与kqueue做的。
需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大
poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。
它没有最大连接数的限制,原因是它是基于链表来存储的,但是同样有一个缺点:
大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。 2. poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。
epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就需态,并且只会通知一次。还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知。
Java从1.4开始提供了NIO工具包,支持用 I/O复用模型来进行网络编程。其模式图:
信号驱动I/O (signal driven I/O (SIGIO))
异步I/O (asynchronous I/O (the POSIX aio_functions))
这种模型与信号驱动模型的主要区别是:信号驱动I/O由内核通知我们何时可以开始一个I/O操作,而异步I/O模型由内核通知我们I/O操作何时已经完成.
JDK1.7 升级了NIO 类库,升级后的NIO类库被称为NIO2.0。java也正是提供了异步文件I/O操作,同时提供了与UNIX网络编程事件驱动I/O对应的AIO。
Netty4的beta3加了AIO了,但是到beta9又被去了,作者的意思是测试下来AIO性能不如NIO,所以没必要用,在Linux上NIO的实现本身就是epoll,使用jdk的AIO没有意义,在windows上jdk的AIO实现是IOCP,这种情况下使用AIO是比poll的性能高的,但是netty的服务器一般是在linux上,所以抛弃windows没啥大不了,windows最多做个客户端,用nio也就够了。微服务Microservice(18)
微服务框架(10)
RPC 框架的原理及实践 ,为什么说要搞定微服务架构,先搞定RPC框架呢
转自:http://www./lib/view/open2.html
今天开始聊一些 微服务的实践 ,第一块, RPC 框架的原理及实践 ,为什么说要搞定微服务架构,先搞定RPC框架呢?
服务化的一个好处就是,不限定服务的提供方使用什么技术选型,能够实现大公司跨团队的技术解耦 ,如下图:
服务 A 是欧洲团队提供服务,欧洲团队的技术背景是 Java ,可以用 Java 实现服务;
服务 B 是美洲团队提供服务,可以用 C++ 实现服务;
服务 C 是中国团队提供服务,可以用 Go 实现服务;
服务的上游调用方,按照接口、协议即可完成对远端服务的调用。
但实际上, 99.9% 的公司的团队规模有限,技术团队人数也有限,基本是使用同一套技术体系来调用和提供服务 的:
这样的话,&如果没有统一的服务框架, RPC 框架&, 各个团队的服务提供方就需要各自实现一套序列化、反序列化、网络框架、连接池、收发线程、超时处理、状态机等“业务之外”的重复技术劳动 ,造成整体的低效。所以,&统一 RPC 框架&把上述“业务之外”的技术劳动统一处理, 是服务化首要解决的问题 。
在达成【“使用统一的 RPC 框架”是正确的道路】这个一致的前提下,本文期望用简单通俗的言语简述一下一个通用 RPC 框架的技术点与实现。
什么是 RPC ( Remote Procedure Call Protocol ),远程过程调用?
先来看下什么是本地函数调用,当我们写下:
int result = Add(1, 2);
这段代码的时候,我们知道,我们传入了 1 , 2 两个入参数,调用了本地代码段中的一个 Add 函数,得到了 result 出参。此时, 传入数据,传出数据,代码段在同一个进程空间里,这是本地函数调用 。
那有没有办法,我们能够调用一个跨进程(所以叫 “ 远程 ” ,典型的,这个进程部署在另一台服务器上)的函数呢 ?
最容易想到的, 两个进程约定一个协议格式,使用 Socket 通信,来传输【入参】【调用哪个函数】【出参】 。
假设请求报文协议是一个 11 字节的字节流:
( 1 )前 3 个字节填入函数名
( 2 )中间 4 个字节填入第一个参数
( 3 )末尾 4 个字节填入第二个参数
同时可以设计响应报文协议是一个 4 字节的字节流:
即处理结果。
调用方的代码可能变为:
request = MakePacket(“add”, 1, 2);
SendRequest_ToService_B(request);
response = RecieveRespnse_FromService_B();
int result = unMakePacket(respnse);
简单解释一下:
( 1 )讲传入参数变为字节流
( 2 )将字节流发给服务 B
( 3 )从服务 B 接受返回字节流
( 4 )将返回字节流变为传出参数
服务方的代码可能变为:
request = RecieveRequest();
args/function = unMakePacket(request);
result = Add(1, 2);
response = MakePacket(result);
SendResponse(response);
这个过程也很好理解:
( 1 )服务端收到字节流
( 2 )将字节流转为函数名与参数
( 3 )本地调用函数得到结果
( 4 )将结果转变为字节流
( 5 )将字节流发送给调用方
这个过程用一张图描述如上,调用方与服务方的处理步骤都是非常清晰的。&这个过程存在最大的问题是什么呢?
回答:调用方太麻烦了,每次都要关注很多底层细节
( 1 )入参到字节流的转化,即序列化应用层协议细节
( 2 ) socket 发送,即网络传输协议细节
( 3 ) socket 接受
( 4 )字节流到出参的转化,即反序列化应用层协议细节
能不能调用层不关注这个细节呢?
回答:可以, RPC 框架就是解决这个问题的,它能够让调用方“像调用本地函数一样调用远端的函数(服务)” 。
通过上面的讨论, RPC 框架要向调用方屏蔽各种复杂性,要向服务提供方也屏蔽各类复杂性 :
( 1 )调用方感觉就像调用本地函数一样
( 2 )服务提供方感觉就像实现一个本地函数一样来实现服务
所以整个 RPC 框架又分为 client 部分与 server 部分,负责把整个非( 1 )( 2 )的各类复杂性屏蔽,这些复杂性就是 RPC 框架的职责。
&再细化一些, client 端又包含:序列化、反序列化、连接池管理、负载均衡、故障转移、队列管理,超时管理、异步管理等等等等职责。
server 端包含:服务端组件、服务端收发包队列、 io 线程、工作线程、序列化反序列化、上下文管理器、超时管理、异步回调等等等等职责。
however ,因为篇幅有限,这些细节不做深入展开。
( 1 ) RPC 框架是架构微服务化的首要基础组件 ,它能大大降低架构微服务化的成本,提高调用方与服务提供方的研发效率,屏蔽跨进程调用函数(服务)的各类复杂细节
( 2 ) RPC 框架的&职责&是: 让调用方感觉就像调用本地函数一样调用远端函数、让服务提供方感觉就像实现一个本地函数一样来实现服务
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:702443次
积分:9460
积分:9460
排名:第2046名
原创:181篇
转载:515篇
评论:101条
(12)(21)(25)(20)(15)(5)(5)(85)(47)(10)(4)(5)(36)(4)(31)(16)(29)(56)(43)(42)(11)(5)(8)(9)(3)(3)(2)(4)(9)(1)(5)(8)(7)(7)(1)(9)(17)(14)(39)(23)(1)(1)
(window.slotbydup = window.slotbydup || []).push({
id: '4740881',
container: s,
size: '200,200',
display: 'inlay-fix'这几天打算自己写一个RPC框架,参考了阿里的dubbo,微博的motan等框架的设计。在选择代理技术时,dubbo有两种方案,一个是jdk的动态代理,一个JAVAASSIST的字节码生成技术,在dubbo作者梁飞的博客《动态代理方案性能对比》/blog/814426中,因为作者在编写服务框架需要用动态代理生成客户端接口的stub,进行了几个动态代理方案性能的测试,测试表明了JAVAASSIST的字节码生成技术的性能应该是最好的。而微博的motan框架里,只使用了jdk代理技术。我脑海里就有疑问,为什么motan框架不用JAVAASSIST的字节码生成技术呢。所以我干脆把几种动态代理技术都用上,测试下在RPC框架中,几种动态代理的性能如何。
下面是测试用例:
maven依赖的包如下:
&dependency&
&groupId&org.javassist&/groupId&
&artifactId&javassist&/artifactId&
&version&3.20.0-GA&/version&
&/dependency&
&dependency&
&groupId&net.bytebuddy&/groupId&
&artifactId&byte-buddy&/artifactId&
&version&1.6.3&/version&
&/dependency&
&dependency&
&groupId&cglib&/groupId&
&artifactId&cglib-nodep&/artifactId&
&version&2.2&/version&
&/dependency&
&dependency&
ProxyEnum:其中javassist字节码生成技术由于需要拼接字符串,比较复杂,所以我把dubbo中这部分的源码扒出来直接拿来用了。
package com.hcd.melody.
import com.mon.util.ReflectU
import javassist.util.proxy.MethodH
import javassist.util.proxy.ProxyO
import net.bytebuddy.ByteB
import net.bytebuddy.dynamic.loading.ClassLoadingS
import net.bytebuddy.implementation.MethodD
import net.bytebuddy.matcher.ElementM
import net.sf.cglib.proxy.E
import net.sf.cglib.proxy.MethodI
import java.lang.reflect.InvocationH
import java.lang.reflect.P
* 代理技术枚举类
* Created by cd_huang on .
public enum ProxyEnum {
JDK_PROXY(new ProxyFactory() {
public &T& T newProxyInstance(Class&T& inferfaceClass, Object handler) {
return inferfaceClass.cast(Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[] {inferfaceClass}, (InvocationHandler)handler));
BYTE_BUDDY_PROXY(new ProxyFactory() {
public &T& T newProxyInstance(Class&T& inferfaceClass, Object handler) {
Class&? extends T& cls = new ByteBuddy()
.subclass(inferfaceClass)
.method(ElementMatchers.isDeclaredBy(inferfaceClass))
.intercept(MethodDelegation.to(handler, "handler"))
.load(inferfaceClass.getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
.getLoaded();
return ReflectUtil.newInstance(cls);
CGLIB_PROXY(new ProxyFactory() {
public &T& T newProxyInstance(Class&T& inferfaceClass, Object handler) {
Enhancer enhancer = new Enhancer();
enhancer.setCallback((MethodInterceptor)handler);
enhancer.setInterfaces(new Class[] {inferfaceClass});
return (T) enhancer.create();
JAVASSIST_BYTECODE_PROXY(new ProxyFactory() {
public &T& T newProxyInstance(Class&T& inferfaceClass, Object handler) {
return (T) com.hcd.melody.proxy.bytecode.Proxy.getProxy(inferfaceClass).newInstance((InvocationHandler)handler);
JAVASSIST_DYNAMIC_PROXY(new ProxyFactory() {
public &T& T newProxyInstance(Class&T& inferfaceClass, Object handler) {
javassist.util.proxy.ProxyFactory proxyFactory = new javassist.util.proxy.ProxyFactory();
proxyFactory.setInterfaces(new Class[] { inferfaceClass });
Class&?& proxyClass = proxyFactory.createClass();
T javassistProxy = (T)ReflectUtil.newInstance(proxyClass);
((ProxyObject) javassistProxy).setHandler((MethodHandler)handler);
return javassistP
private ProxyF
ProxyEnum(ProxyFactory factory){
this.factory =
public &T& T newProxyInstance(Class&T& interfaceType, Object handler) {
return factory.newProxyInstance(interfaceType, handler);
interface ProxyFactory{
&T& T newProxyInstance(Class&T& inferfaceClass, Object handler);
InvokeHandler:
package com.hcd.melody.
import javassist.util.proxy.MethodH
import net.bytebuddy.implementation.bind.annotation.AllA
import net.bytebuddy.implementation.bind.annotation.O
import net.bytebuddy.implementation.bind.annotation.RuntimeT
import net.bytebuddy.implementation.bind.annotation.T
import net.sf.cglib.proxy.MethodI
import net.sf.cglib.proxy.MethodP
import java.lang.reflect.InvocationH
import java.lang.reflect.M
* 客户端代理类的处理
* Created by cd_huang on .
public class InvokeHandler implements InvocationHandler, MethodHandler, MethodInterceptor {
private Object doInvoke(Object proxy, Method method, Object[] args) {
if (Object.class == method.getDeclaringClass()) {
String name = method.getName();
if ("equals".equals(name)) {
return proxy == args[0];
} else if ("hashCode".equals(name)) {
return System.identityHashCode(proxy);
} else if ("toString".equals(name)) {
return proxy.getClass().getName() + "@" +
Integer.toHexString(System.identityHashCode(proxy)) +
", with InvokeHandler " +
throw new IllegalStateException(String.valueOf(method));
//TODO RPC调用
* jdk invoke
* @param proxy
* @param method
* @param args
* @throws Throwable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return doInvoke(proxy, method, args);
* byteBuddy invoke
* @param proxy
* @param method
* @param args
* @throws Throwable
@RuntimeType
public Object byteBuddyInvoke(@This Object proxy, @Origin Method method, @AllArguments @RuntimeType Object[] args) throws Throwable {
return doInvoke(proxy, method, args);
* javassist invoke
* @param proxy
* @param method
* @param proceed
* @param args
* @throws Throwable
public Object invoke(Object proxy, Method method, Method proceed, Object[] args) throws Throwable {
return doInvoke(proxy, method, args);
* cglib invoke
* @param proxy
* @param method
* @param args
* @param methodProxy
* @throws Throwable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
return doInvoke(proxy, method, args);
TestService:
package com.hcd.melody.
* test接口类
* Created by cd_huang on .
public interface TestService {
String test(String s);
TestProxy:
package com.hcd.melody.
import com.hcd.melody.proxy.*;
* 测试动态代理技术区别
* Created by cd_huang on .
public class TestProxy {
public static void main(String args[]) {
InvokeHandler handler =new InvokeHandler();
long time = System.currentTimeMillis();
TestService test1 =ProxyEnum.JDK_PROXY.newProxyInstance(TestService.class,handler);
time = System.currentTimeMillis() -
System.out.println("Create JDK Proxy: " + time + " ms");
time = System.currentTimeMillis();
TestService test2 =ProxyEnum.BYTE_BUDDY_PROXY.newProxyInstance(TestService.class,handler);
time = System.currentTimeMillis() -
System.out.println("Create byteBuddy Proxy: " + time + " ms");
time = System.currentTimeMillis();
TestService test3 =ProxyEnum.CGLIB_PROXY.newProxyInstance(TestService.class,handler);
time = System.currentTimeMillis() -
System.out.println("Create CGLIB Proxy: " + time + " ms");
time = System.currentTimeMillis();
TestService test4 =ProxyEnum.JAVASSIST_BYTECODE_PROXY.newProxyInstance(TestService.class,handler);
time = System.currentTimeMillis() -
System.out.println("Create JAVASSIST Bytecode Proxy: " + time + " ms");
time = System.currentTimeMillis();
TestService test5 =ProxyEnum.JAVASSIST_DYNAMIC_PROXY.newProxyInstance(TestService.class,handler);
time = System.currentTimeMillis() -
System.out.println("Create JAVASSIST Proxy: " + time + " ms");
String s ="proxy";
System.out.println("----------------");
for (int i = 0; i &10; i++) {
test(test1, "Run JDK Proxy: ",s);
test(test2, "Run byteBuddy Proxy: ",s);
test(test3, "Run CGLIB Proxy: ",s);
test(test4, "Run JAVASSIST Bytecode Proxy: ",s);
test(test5, "Run JAVASSIST Proxy: ",s);
System.out.println("----------------");
private static void test(TestService service, String label,String s) {
service.test(s); // warm up
int count = ;
long time = System.currentTimeMillis();
for (int i = 0; i & i++) {
service.test(s);
time = System.currentTimeMillis() -
System.out.println(label + time + " ms, ");
测试结果如下:
Create JDK Proxy: 26 ms
Create byteBuddy Proxy: 639 ms
Create CGLIB Proxy: 103 ms
Create JAVASSIST Bytecode Proxy: 164 ms
Create JAVASSIST Proxy: 17 ms
----------------
Run JDK Proxy: 21 ms,
Run byteBuddy Proxy: 241 ms,
Run CGLIB Proxy: 951 ms,
Run JAVASSIST Bytecode Proxy: 918 ms,
Run JAVASSIST Proxy: 827 ms,
----------------
Run JDK Proxy: 499 ms,
Run byteBuddy Proxy: 632 ms,
Run CGLIB Proxy: 547 ms,
Run JAVASSIST Bytecode Proxy: 602 ms,
Run JAVASSIST Proxy: 718 ms,
----------------
Run JDK Proxy: 544 ms,
Run byteBuddy Proxy: 567 ms,
Run CGLIB Proxy: 563 ms,
Run JAVASSIST Bytecode Proxy: 586 ms,
Run JAVASSIST Proxy: 724 ms,
----------------
Run JDK Proxy: 550 ms,
Run byteBuddy Proxy: 553 ms,
Run CGLIB Proxy: 555 ms,
Run JAVASSIST Bytecode Proxy: 585 ms,
Run JAVASSIST Proxy: 761 ms,
----------------
Run JDK Proxy: 532 ms,
Run byteBuddy Proxy: 552 ms,
Run CGLIB Proxy: 528 ms,
Run JAVASSIST Bytecode Proxy: 601 ms,
Run JAVASSIST Proxy: 724 ms,
----------------
Run JDK Proxy: 545 ms,
Run byteBuddy Proxy: 518 ms,
Run CGLIB Proxy: 518 ms,
Run JAVASSIST Bytecode Proxy: 603 ms,
Run JAVASSIST Proxy: 715 ms,
----------------
Run JDK Proxy: 544 ms,
Run byteBuddy Proxy: 544 ms,
Run CGLIB Proxy: 544 ms,
Run JAVASSIST Bytecode Proxy: 571 ms,
Run JAVASSIST Proxy: 727 ms,
----------------
Run JDK Proxy: 546 ms,
Run byteBuddy Proxy: 589 ms,
Run CGLIB Proxy: 536 ms,
Run JAVASSIST Bytecode Proxy: 577 ms,
Run JAVASSIST Proxy: 729 ms,
----------------
Run JDK Proxy: 667 ms,
Run byteBuddy Proxy: 632 ms,
Run CGLIB Proxy: 508 ms,
Run JAVASSIST Bytecode Proxy: 596 ms,
Run JAVASSIST Proxy: 708 ms,
----------------
Run JDK Proxy: 541 ms,
Run byteBuddy Proxy: 532 ms,
Run CGLIB Proxy: 533 ms,
Run JAVASSIST Bytecode Proxy: 613 ms,
Run JAVASSIST Proxy: 761 ms,
----------------
从上面的测试,我得出来的结论和梁飞完全不同,几种动态代理技术的性能差异不大,JDK Proxy、byteBuddy Proxy、CGLIB Proxy的性能会比JAVAASSIST Bytecode Proxy、JAVAASSIST Proxy稍微快一些,而梁飞的结论是javaassist字节码生成技术的性能是最好的。但是上面看来,javaassist字节码生成技术的性能是比较差的。为什么会有这个区别呢。是因为在梁飞的例子里,会对目标对象delegate进行调用。而jdk动态代理,cglib动态代理,javaassist动态代理,都会使用反射调用目标对象,而javaassist字节码生成技术,asm字节码生成技术,则没有使用反射调用目标对象,而是类似装饰器的设计模式,直接调用delegate.method(args)。所以,性能的差异在于用反射调用方法和直接调用。而在RPC框架或分布式服务框架中,并不需要用反射去调用某个类,而是拿到接口名,方法,以及参数,把这些信息发送给服务端让服务端去反射调用类,再把结果返回到客户端。所以,在我看来,dubbo框架的客户端中使用javaassist字节码生成技术完全没有必要,字节码生成技术的优势在于避免反射调用方法,而RPC框架的客户端则没有这样的场景。像微博的motan框架那样直接jdk动态代理,简单省事,性能又好,又非常的可靠稳定。
xiangshouxiyang
浏览: 3814 次
来自: 厦门
达神,强无敌
学习了,原理分析的很透彻,博主出品的都是精品啊
&div class=&quote_title ...
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'这并不是原理性的解释文章。而是快速入门,还有一个完整的Java例子。
一篇我觉得不错的文章推荐:
什么是RPC?
RPC(Remote Procedure Call)远程过程调用。见名知意 - 从远程主机调用一个过程/函数。
RPC的目标是:使得本程序调用其它远程主机上的函数,好像调用本程序内的函数一样简单,并且屏蔽编程语言的差异性。
要实现上述目标首先是设计一种通讯协议,称之为:RPC协议(Protocol)
RPC协议不是一个具体的协议,而是一个类型名,代表一类协议,这类协议叫做RPC协议;
RPC协议在TCP/UDP之上,广义上可以跨越平台、语言进行应用间通讯(说广义是因为可以开发一个协议且不支持跨语言);
为什么要用RPC?
其实这是应用开发到一定的阶段的强烈需求驱动的。
如果我们开发简单的单一应用,逻辑简单、用户不多、流量不大,那我们用不着;
当我们的系统访问量增大、业务增多时,我们会发现一台单机运行此系统已经无法承受。此时,我们可以将业务拆分成几个互不关联的应用,分别部署在各自机器上,以划清逻辑并减小压力。此时,我们也可以不需要RPC,因为应用之间是互不关联的。
当我们的业务越来越多、应用也越来越多时,自然的,我们会发现有些功能已经不能简单划分开来或者划分不出来。此时,可以将公共业务逻辑抽离出来,将之组成独立的服务Service应用 。而原有的、新增的应用都可以与那些独立的Service应用 交互,以此来完成完整的业务功能。所以此时,我们急需一种高效的应用程序之间的通讯手段来完成这种需求,所以你看,RPC大显身手的时候来了!
其实#3描述的场景也是服务化 、微服务 和分布式系统架构 的基础场景。即RPC框架就是实现以上结构的有力方式。
有哪些RPC?
有很多RPC框架:CORBAR、Thrift、Dubbo等等。基本上他们分为两种类别:
一种是跨语言的;
一种是同语言的,如果你的分布式应用架构主体都是Java应用,显然我们不应该使用跨语言的RPC来多一层中转浪费效率。
就Java来说,我认为其本身API提供的RMI就是一种RPC协议,但是其毕竟太原始,需要自己去添加很多机制才能上生产环境。所以,今天介绍下我最近使用的Dubbo框架。
二、Dubbo使用实例
什么是Dubbo?
Dubbo是阿里巴巴为Java开发的RPC,据网上评价来看,非常不错。在Dubbo开源后很多公司都使用其来构建自己的分布式架构。今天我来做一个使用实例。
由于Dubbo官方文档已经把一切都说的很详细了,我就不没意思的摘抄了,放上地址:
官网上也有详细使用方法,那我为什么还要写个使用实例呢?
是因为,似乎到2012年,Dubbo已经比较稳定的完成了所有开发,所以现在Dubbo其实是在一个松散的维护状态下的。有些文档内容已经失效:地址无法访问,一些错误没有明确说明。
所以参照官网会有一些问题,我这里就是解决了某些问题而完成的一个实例,放在这里供大家参考。
建议大家先去看文档,形成一个整体概念再做实例。
所以,我的整体架构是这样的:
使用Dubbo来服务化(基于Spring);
使用Zookeeper集群作为Dubbo服务的注册中心();
使用Dubbo提供的一个JavaWeb项目作为Dubbo服务管理控制台;
创建了一个服务提供者项目(pom父项目) - 叫;
创建了一个服务消费者项目(pom父项目) - 叫;
另外说一点,这两个只是练习项目,实际上一个项目可以既是提供者也是消费者。
首先,要确保Zookeeper的安装和配置正确,可以直接按照我另一篇文章来做。
我们需要一个Dubbo服务管理控制台:
本身这个项目的war包,Dubbo在其文档中是给出了下载地址的,但是地址已经失效了。所以,现在只能去其GitHub地址上自己下载源码下来打包了。
使用Eclipse,用其自带的git插件下载dubbo项目整体,地址是:;
先将dubbo项目转为Maven项目,然后单独将其中的子Maven项目dubbo-admin导出来,这就是一个JavaWeb项目。
但是这个项目,别直接Maven打包,因为它有一些依赖问题需要修改,打开其pom.xml文件:
&com.alibaba&
&org.springframework&
如果使用Spring的2.5.6.SEC03版本直接打包,就会有如下错误:
ERROR context.ContextLoader - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'uriBrokerService': Cannot create inner bean '(inner bean)' of type [com.alibaba.citrus.service.uribroker.impl.URIBrokerServiceImpl$URIBrokerInfo] while setting bean property 'brokers' with key [0]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#25': Cannot create inner bean 'server' of type [com.alibaba.citrus.service.uribroker.uri.GenericURIBroker] while setting
nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'server': Error set nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'URIType' of bean class [com.alibaba.citrus.service.uribroker.uri.GenericURIBroker]: Bean property 'URIType' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveInnerBean(BeanDefinitionValueResolver.java:230)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:122)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveManagedList(BeanDefinitionValueResolver.java:287)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:126)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1245)
接着,如果此项目你不打算部署到与任意一个Zookeeper一个机器的话,就需要修改/dubbo-admin/src/main/webapp/WEB-INF/dubbo.properties文件,在其中指定你的Zookeeper地址(要保证这些机器能互相通讯)。
没有要修改的了。可以使用Maven的package命令打包了。之后部署war项目,可以按照官网的部署方式,也就是将之部署为ROOT应用,。
服务提供者项目
源码地址:
其中有两个子项目:
dubbo-practice-provider-api:暴漏接口和JavaBeans,让实现项目引用然后去实现、让消费者引用然后调用。这个比较简单,一看就懂就不展开讲了。
dubbo-practice-provider-impl:上面api项目的实现项目,是一个普通Java项目;
实现者项目dubbo-practice-provider-impl的主要内容就是Dubbo如何暴漏一个服务的使用了,说一下:
首先是依赖关系,pom.xml中
&dubbo-practice-provider-api&
&dubbo-practice-provider-api&
&0.0.1-SNAPSHOT&
&org.springframework&
&spring-context&
&${spring.version}&
&com.alibaba&
&${dubbo.version}&
&org.apache.zookeeper&
&zookeeper&
&${zookeeper.version}&
&com.github.sgroschupf&
&zkclient&
&${zkclient.version}&
其次是dubbo配置,配置在Spring配置文件中,我放在/META-INF/spring/spring-dubbo.xml
&?xml version="1.0" encoding="UTF-8"?&
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="/schema/dubbo"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
/schema/dubbo /schema/dubbo/dubbo.xsd"&
name="dubbo-practice-provider" /&
protocol="zookeeper" address="192.168.157.130:.157.129:2181" /&
name="dubbo" port="20880" /&
interface="com.ddup.dubbo.service.api.PersonService" ref="personService" /&
最后,做好之后就可以运行了,我写了一个简单的启动服务类/dubbo-practice-provider-impl/src/main/java/com/ddup/dubbo/service/impl/DubboContainerStart.java,运行这个就行了,不要关。
服务消费者项目
源码地址:;
主要就是Dubbo服务的调用了,见/META-INF/spring/spring-dubbo.xml:
name="dubbo-practice-consumer" /&
protocol="zookeeper" address="192.168.157.130:.157.129:2181" /&
id="personService" interface="com.ddup.dubbo.service.api.PersonService" init="true" /&
然后,运行/dubbo-practice-consumer/src/main/java/com/ddup/dubbo/consumer/SpringStart.java,鹅妹子嘤!!!可以看到,调用成功啦!
在上面#2说的管理控制台上,现在你就可以看到服务了,能看到服务者以及每个服务的消费者等等信息;
查看Dubbo服务
dubbo-admin项目的tomcat启动后无法访问:
打开catalina.out查看错误
[root@localhost logs]
如果发现如下错误,是因为Zookeeper没有成功启动,可以去检查Zookeeper的状态。
INFO velocity.VelocityEngine - SpringResourceLoaderAdapter : initialization starting.
INFO velocity.VelocityEngine - SpringResourceLoaderAdapter : set path '/templates/common/'
INFO velocity.VelocityEngine - SpringResourceLoaderAdapter : initialization complete.
INFO rule.ExtensionMappingRule - Initialized extension.input:ExtensionMappingRule with cache disabled
INFO rule.ExtensionMappingRule - Initialized extension.output:ExtensionMappingRule with cache disabled
INFO rule.DirectModuleMappingRule - Initialized action:DirectModuleMappingRule with cache disabled
INFO rule.DirectModuleMappingRule - Initialized screen.notemplate:DirectModuleMappingRule with cache disabled
INFO rule.FallbackModuleMappingRule - Initialized screen:FallbackModuleMappingRule with cache enabled
INFO rule.DirectTemplateMappingRule - Initialized screen.template:DirectTemplateMappingRule with cache disabled
INFO rule.FallbackTemplateMappingRule - Initialized layout.template:FallbackTemplateMappingRule with cache enabled
INFO rule.DirectModuleMappingRule - Initialized control.notemplate:DirectModuleMappingRule with cache disabled
INFO rule.FallbackModuleMappingRule - Initialized control:FallbackModuleMappingRule with cache enabled
INFO rule.DirectTemplateMappingRule - Initialized control.template:DirectTemplateMappingRule with cache disabled
INFO zkclient.ZkEventThread - Starting ZkClient event thread.
Provider的服务与Consumer引用时的一些问题:
关闭启动时检查
Dubbo默认会在启动时检查依赖的服务是否可用,不可用会抛出异常,阻止Spring初始化完成。如果对有些服务不关心,或者出现了循环依赖,必须有一方先启动时,可以关闭启动时检查。方式如下:
interface="com.xxx.XxxService" check="false" /&
引用默认还是延迟初始化的,只有引用被注入到其它Bean,或者被getBean()获取时,才会初始化。如果需要饥饿加载,即Dubbo启动时就立即生成动态代理实例,则可以配置:
interface="com.xx.XxxService" init="true" /&
更新:Dubbo官网声明官方要重新开始重点维护此项目了!
转载注明出处:
本文已收录于以下专栏:
相关文章推荐
Dubbo背景和简介Dubbo开始于电商系统,因此在这里先从电商系统的演变讲起。
单一应用框架(ORM)
当网站流量很小时,只需一个应用,将所有功能如下单支付等都部署在一起,以减少部署节点和成本。 ...
dubbo springmvc
一、前言dubbo的使用,其实只需要有注册中心,消费者,提供者这三个就可以使用了,但是并不能看到有哪些消费者和提供者,为了更好的调试,发现问题,解决问题,因此引入dubbo-admin。通过dubbo...
MySQL中的锁概念
Mysql中不同的存储引擎支持不同的锁机制。比如MyISAM和MEMORY存储引擎采用的表级锁,BDB采用的是页面锁,也支持表级锁,InnoDB存储引擎既支持行级锁,也支持表级锁...
关注公众号
在公众号里回复“”秘密“”两个字
http://task.csdn.net/m/task/home?task_id=398 领取奖励
提示:根据公众号里的自动回复,完成...
他的最新文章
讲师:刘文志
讲师:陈伟
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)}

我要回帖

更多关于 java web消息推送框架 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信