dubbo框架揭秘之服务引用
ApplicationConfig config = new ApplicationConfig("hello-worldp"); RegistryConfig reg = new RegistryConfig("127.0.0.1:2181");
reg.setProtocol("zookeeper"); ReferenceConfig<DemoService> refrence=new ReferenceConfig<DemoService>();
refrence.setApplication(config);
refrence.setRegistry(reg);
refrence.setInterface(DemoService.class);
refrence.setVersion("1.0"); DemoService demo=refrence.get();
System.out.println(demo.sayHello("selrain")); Thread.sleep(Integer.MAX_VALUE);
和服务发布一样,不采用Spring配置的方式,手动编写消费者的代码。其中ReferenceConfig的get()方法获取代理类。
public synchronized T get() {
if (ref == null) {
init();
}
return ref;
} private void init() {
...
if (isJvmRefer) {
URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);
invoker = refprotocol.refer(interfaceClass, url);
if (logger.isInfoEnabled()) {
logger.info("Using injvm service " + interfaceClass.getName());
}
} else {
if (url != null && url.length() > 0) { // 用户指定URL,指定的URL可能是对点对直连地址,也可能是注册中心URL
String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(url);
if (us != null && us.length > 0) {
for (String u : us) {
URL url = URL.valueOf(u);
if (url.getPath() == null || url.getPath().length() == 0) {
url = url.setPath(interfaceName);
}
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
urls.add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
} else {
urls.add(ClusterUtils.mergeUrl(url, map));
}
}
}
} else { // 通过注册中心配置拼装URL
List<URL> us = loadRegistries(false);
if (us != null && us.size() > 0) {
for (URL u : us) {
URL monitorUrl = loadMonitor(u);
if (monitorUrl != null) {
map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
}
urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
}
}
if (urls == null || urls.size() == 0) {
throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");
}
} if (urls.size() == 1) {
//注册中心注册消费者地址
invoker = refprotocol.refer(interfaceClass, urls.get(0));
} else {
List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
URL registryURL = null;
for (URL url : urls) {
invokers.add(refprotocol.refer(interfaceClass, url));
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
registryURL = url; // 用了最后一个registry url
}
}
if (registryURL != null) { // 有 注册中心协议的URL
// 对有注册中心的Cluster 只用 AvailableCluster
URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
invoker = cluster.join(new StaticDirectory(u, invokers));
} else { // 不是 注册中心的URL
invoker = cluster.join(new StaticDirectory(invokers));
}
}
}
ref = createProxy(map);
}
private T createProxy(Map<String, String> map) {
...
// 创建服务代理
return (T) proxyFactory.getProxy(invoker);
}
proxyFactory通过ExtensionLoader的getAdaptiveExtension方法初始化,为StubProxyFactoryWrapper
private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
看看StubProxyFactoryWrapper的getProxy方法,其调用了内部的proxyFactory的getProxy方法,而StubProxyFactoryWrapper在初始化的时候proxyFactory也一并初始化了,其值为JavassistProxyFactory。
public <T> T getProxy(Invoker<T> invoker) throws RpcException {
T proxy = proxyFactory.getProxy(invoker);
...
return proxy;
}
看看JavassistProxyFactory的getProxy方法:
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
Proxy: 创建代理类
public static Proxy getProxy(ClassLoader cl, Class<?>... ics)
{
if( ics.length > 65535 )
throw new IllegalArgumentException("interface limit exceeded"); StringBuilder sb = new StringBuilder();
for(int i=0;i<ics.length;i++)
{
String itf = ics[i].getName();
if( !ics[i].isInterface() )
throw new RuntimeException(itf + " is not a interface."); Class<?> tmp = null;
try
{
tmp = Class.forName(itf, false, cl);
}
catch(ClassNotFoundException e)
{} if( tmp != ics[i] )
throw new IllegalArgumentException(ics[i] + " is not visible from class loader"); sb.append(itf).append(';');
} // use interface class name list as key.
String key = sb.toString(); // get cache by class loader.
Map<String, Object> cache;
synchronized( ProxyCacheMap )
{
cache = ProxyCacheMap.get(cl);
if( cache == null )
{
cache = new HashMap<String, Object>();
ProxyCacheMap.put(cl, cache);
}
} Proxy proxy = null;
synchronized( cache )
{
do
{
Object value = cache.get(key);
if( value instanceof Reference<?> )
{
proxy = (Proxy)((Reference<?>)value).get();
if( proxy != null )
return proxy;
} if( value == PendingGenerationMarker )
{
try{ cache.wait(); }catch(InterruptedException e){}
}
else
{
cache.put(key, PendingGenerationMarker);
break;
}
}
while( true );
} long id = PROXY_CLASS_COUNTER.getAndIncrement();
String pkg = null;
ClassGenerator ccp = null, ccm = null;
try
{
ccp = ClassGenerator.newInstance(cl); Set<String> worked = new HashSet<String>();
List<Method> methods = new ArrayList<Method>(); for(int i=0;i<ics.length;i++)
{
if( !Modifier.isPublic(ics[i].getModifiers()) )
{
String npkg = ics[i].getPackage().getName();
if( pkg == null )
{
pkg = npkg;
}
else
{
if( !pkg.equals(npkg) )
throw new IllegalArgumentException("non-public interfaces from different packages");
}
}
ccp.addInterface(ics[i]); for( Method method : ics[i].getMethods() )
{
String desc = ReflectUtils.getDesc(method);
if( worked.contains(desc) )
continue;
worked.add(desc); int ix = methods.size();
Class<?> rt = method.getReturnType();
Class<?>[] pts = method.getParameterTypes();
//为代理类实例添加方法,当我们调用sayHello()方法,执行这里为sayHello方法生成的代码,即:handler.invoke()
StringBuilder code = new StringBuilder("Object[] args = new Object[").append(pts.length).append("];");
for(int j=0;j<pts.length;j++)
code.append(" args[").append(j).append("] = ($w)$").append(j+1).append(";");
code.append(" Object ret = handler.invoke(this, methods[" + ix + "], args);");
if( !Void.TYPE.equals(rt) )
code.append(" return ").append(asArgument(rt, "ret")).append(";"); methods.add(method);
ccp.addMethod(method.getName(), method.getModifiers(), rt, pts, method.getExceptionTypes(), code.toString());
}
} if( pkg == null )
pkg = PACKAGE_NAME; // create ProxyInstance class.
String pcn = pkg + ".proxy" + id;
ccp.setClassName(pcn);
ccp.addField("public static java.lang.reflect.Method[] methods;");
ccp.addField("private " + InvocationHandler.class.getName() + " handler;");
ccp.addConstructor(Modifier.PUBLIC, new Class<?>[]{ InvocationHandler.class }, new Class<?>[0], "handler=$1;");
ccp.addDefaultConstructor();
Class<?> clazz = ccp.toClass();
clazz.getField("methods").set(null, methods.toArray(new Method[0])); // create Proxy class.
String fcn = Proxy.class.getName() + id;
ccm = ClassGenerator.newInstance(cl);
ccm.setClassName(fcn);
ccm.addDefaultConstructor();
ccm.setSuperClass(Proxy.class);
ccm.addMethod("public Object newInstance(" + InvocationHandler.class.getName() + " h){ return new " + pcn + "($1); }");
Class<?> pc = ccm.toClass();
proxy = (Proxy)pc.newInstance();
}
catch(RuntimeException e)
{
throw e;
}
catch(Exception e)
{
throw new RuntimeException(e.getMessage(), e);
}
...
return proxy;
}
最终调用DubboInvoker 的doInvoke(Invocation)方法进行与服务器间通信
protected Result doInvoke(final Invocation invocation) throws Throwable {
RpcInvocation inv = (RpcInvocation) invocation;
final String methodName = RpcUtils.getMethodName(invocation);
inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());
inv.setAttachment(Constants.VERSION_KEY, version); ExchangeClient currentClient;
if (clients.length == 1) {
currentClient = clients[0];
} else {
currentClient = clients[index.getAndIncrement() % clients.length];
}
try {
boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY,Constants.DEFAULT_TIMEOUT);
if (isOneway) {
boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
currentClient.send(inv, isSent);
RpcContext.getContext().setFuture(null);
return new RpcResult();
} else if (isAsync) {
ResponseFuture future = currentClient.request(inv, timeout) ;
RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
return new RpcResult();
} else {
RpcContext.getContext().setFuture(null);
return (Result) currentClient.request(inv, timeout).get();
}
} catch (TimeoutException e) {
throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "Invoke remote method timeout. method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
} catch (RemotingException e) {
throw new RpcException(RpcException.NETWORK_EXCEPTION, "Failed to invoke remote method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
参考:
dubbo框架揭秘之服务引用的更多相关文章
- dubbo框架揭秘之服务发布
通常情况下是通过Spring配置的方式去实现服务的发布,为了方便调试,我就不采用Spring配置的方式. DemoService demo = new DemoServiceImpl(); Appli ...
- 基于Dubbo框架构建分布式服务(一)
Dubbo是Alibaba开源的分布式服务框架,我们可以非常容易地通过Dubbo来构建分布式服务,并根据自己实际业务应用场景来选择合适的集群容错模式,这个对于很多应用都是迫切希望的,只需要通过简单的配 ...
- 基于Dubbo框架构建分布式服务
Dubbo是Alibaba开源的分布式服务框架,我们可以非常容易地通过Dubbo来构建分布式服务,并根据自己实际业务应用场景来选择合适的集群容错模式,这个对于很多应用都是迫切希望的,只需要通过简单的配 ...
- [转载] 基于Dubbo框架构建分布式服务
转载自http://shiyanjun.cn/archives/1075.html Dubbo是Alibaba开源的分布式服务框架,我们可以非常容易地通过Dubbo来构建分布式服务,并根据自己实际业务 ...
- (八)整合 Dubbo框架 ,实现RPC服务远程调用
整合 Dubbo框架 ,实现RPC服务远程调用 1.Dubbo框架简介 1.1 框架依赖 1.2 核心角色说明 2.SpringBoot整合Dubbo 2.1 核心依赖 2.2 项目结构说明 2.3 ...
- Dubbo 源码分析 - 服务引用
1. 简介 在上一篇文章中,我详细的分析了服务导出的原理.本篇文章我们趁热打铁,继续分析服务引用的原理.在 Dubbo 中,我们可以通过两种方式引用远程服务.第一种是使用服务直联的方式引用服务,第二种 ...
- dubbo refrence bean(服务引用)
在xml上写一个dubbo标签就可以把远程的服务引用到本地使用: <dubbo:reference id="buyFoodService" interface="c ...
- 基于Dubbo框架构建分布式服务(集群容错&负载均衡)
Dubbo是Alibaba开源的分布式服务框架,我们可以非常容易地通过Dubbo来构建分布式服务,并根据自己实际业务应用场景来选择合适的集群容错模式,这个对于很多应用都是迫切希望的,只需要通过简单的配 ...
- SpringBoot2.0 整合 Dubbo框架 ,实现RPC服务远程调用
一.Dubbo框架简介 1.框架依赖 图例说明: 1)图中小方块 Protocol, Cluster, Proxy, Service, Container, Registry, Monitor 代表层 ...
随机推荐
- 不停止MySQL服务增加从库的两种方式【转载】
现在生产环境MySQL数据库是一主一从,由于业务量访问不断增大,故再增加一台从库.前提是不能影响线上业务使用,也就是说不能重启MySQL服务,为了避免出现其他情况,选择在网站访问量低峰期时间段操作. ...
- 打开一个activity,让edittext不获得焦点
在实际开发中,有的页面用到Edittext控件,这时候进入该页面可能会自动弹出输入法,这么显示不太友好,所以需要设置一下让Edittext默认不自动获取焦点.在网上查资料解决办法如下: 在Edit ...
- 使用FusionCharts出柱状图和饼状图
在最近的项目中,需要使用出图,能够查看柱状图,饼状图等效果,刚开始我们用JS写的效果,发现效果不理想,找了一个JS插件发现效果还是不理想,客户也不满意,客户希望要很炫的效果,最后我们使用了Fusion ...
- 总结自己的Git常用命令
总结自己的Git常用命令 使用git也有一段时间了,把自己常用的命令用自己的描述记录起来,方便自己备忘也方便其他人参考. 目录: 最基本的命令: git clone 拷贝并跟踪远程的master分支. ...
- ZOJ 3702 Gibonacci number(数学推导)
公式推导题,G(0) = 1,G(1) = t,给出一个 i 和 G(i),要求求出G(j)的值: G(0) = 0*t + 1 G(1) = 1*t + 0; 观察t的系数和常数值可以知道二者都遵循 ...
- fragment 数据传递,通信
Fragment之间的通信 在本节中,你会学到 1.定义接口 2.实现接口 3.将消息传递给fragment 为了重用Fragment UI 组件,在设计中你应该通过定义每一个fragemnt自己 ...
- zepto学习之路--数组去重和原生reduce
好吧开始读zepto的源代码,最前面给处理trim和reduce的原生实现,感觉写的很紧凑,其中reduce写的有点晦涩,个人感觉还不错.主要zepto的作者是无分号党,看起了有点不习惯. 3 if ...
- angular中控制器之间的通讯方式
1, 利用作用域的继承方式 由于作用域的继承是基于js的原型继承方式,所以这里分为两种情况,当作用域上面的值为基本类型的时候,修改父作用域上面的值会 影响到子作用域,反之,修改子作用域只会影响子作用域 ...
- PHP Markdown 解析器Parsedown
PHP Markdown 解析器Parsedown http://parsedown.org/demo
- css3常用样式集锦
控制线显示0.5px .line:after{ content:""; display:block; position:absolute; width:200%; left:0; ...