dubbo和netty相关分析和积累

dubbo知识累计

 

官网文档:http://dubbo.apache.org/zh-cn/docs/user/configuration/properties.html

1 dubbo接口测试

1)使用telnet连接对应的dubbo服务,telnet xxip port(服务端dubbo端口);

2 )invoke com.xx.method({jsonstr})

2.查看zookeeper服务中注册的dubbo服务

1.进入到dubbo服务的物理机,然后ps -ef | grep zookeeper 查看到zookeeper的路径

2.进入路径后,bash zkCli.sh,这样就能进入zk服务l

3.查看providers ls /dubbo/xx类/providers  查看comsumer ls /dubbo/xx类/consumers

4.dubbo默认重试两次,超时是1000ms

3 dubbo配置

注意:如果在classpath下有超过一个dubbo.properties文件,比如,两个jar包都各自包含了dubbo.properties,dubbo将随机选择一个加载,并且打印错误日志。

dubbo.application.name=xx

dubbo.registry.protocol=zookeeper

dubbo.registry.address=zookeeper://xxip:port

dubbo.protocol.port=3222 //dubbo服务调用接口端口号,可以根据这个端口号去invoke

dubbo.protocol.name=dubbo //不可随意更改

dubbo.scan.base-packages =//服务接口扫描基础路径

dubbo.application.qos.accept.foreign.ip=false//默认情况下,dubbo 接收任何主机发起的命令,这样配置,拒绝远端主机发出的命令,只允许服务本机执行

4.dubbo序列化方式

在dubbo RPC中,同时支持多种序列化方式,例如:

  1. dubbo序列化:阿里尚未开发成熟的高效java序列化实现,阿里不建议在生产环境使用它
  2. hessian2序列化:hessian是一种跨语言的高效二进制序列化方式。但这里实际不是原生的hessian2序列化,而是阿里修改过的hessian lite,它是dubbo RPC默认启用的序列化方式
  3. json序列化:目前有两种实现,一种是采用的阿里的fastjson库,另一种是采用dubbo中自己实现的简单json库,但其实现都不是特别成熟,而且json这种文本序列化性能一般不如上面两种二进制序列化。
  4. java序列化:主要是采用JDK自带的Java序列化实现,性能很不理想。

在通常情况下,这四种主要序列化方式的性能从上到下依次递减。对于dubbo RPC这种追求高性能的远程调用方式来说,实际上只有1、2两种高效序列化方式比较般配,而第1个dubbo序列化由于还不成熟,所以实际只剩下2可用,所以dubbo RPC默认采用hessian2序列化。

dubbo支持拓展序列化方式,序列化需要实现org.apache.dubbo.common.serialize.Serialization接口

目前所有的序列化方式为

dubbo更改序列化框架

更改序列化方式有2个步骤

1.在META-INF/dubbo下建立纯文本org.apache.dubbo.common.serialize.Serialization,然后在该文件下编写xxx=com.XxxSerialization,例如 fastJson=org.apache.dubbo.common.serialize.fastjson.FastJsonSerialization

2.在application.yml配置文件中编写dubbo.protocol.Serialization=fastJson,这样既可更改序列化方式

dubbo  负载策略

  • Random LoadBalance 基于权重的随机负载均衡机制(默认)
  • RoundRobin LoadBalance 基于权重的轮询负载均衡机制
  • LeastActive LoadBalance 最少活跃数负载均衡机制
  • ConsistentHash LoadBalance 一致性哈希负载均衡机制

dubbo问题分析

1 forbid consumer xx_ip access xxservice  from registry

这两天经常被这个dubbo的错误所困扰,其实解决完之后才发现其实并没有那么难,最核心的就是要保证服务器的provider和consumer之间的版本要对应上,如果没有对应上,就会报这个错误,其实这个错误挺误导人的,因为错误上说的是拒绝这个consumer调用dubbo的服务,给出的提示是查看访问列表,黑名单白名单,但是读过源码才知道,其实就是consumer要调用provider没有找到,可以简单的理解成NullPointerException

2.Caused by: java.lang.IllegalStateException: No registry config found or it's not a valid config! The registry config is: <dubbo:registry valid="false" zookeeperProtocol="false" prefix="dubbo.registry" />

原因是application的配置文件是不存在的或者配置文件有误,尤其是yml文件需要注意空格,否则很容易出错

3.dubbo  invoke  invalid json argument ,cause:com/alibaba/fastjson

原因之一是因为缺少fastjson的jar包引用

4.instance must be started before calling this method

由于ZooKeeper原生的客户端比较低层,而且使用起来不方便,Netflix公司对ZooKeeper客户端API进行了封闭,提供一可以替代原生ZooKeeper客户端的框架——Curator,换成兼容的jar包即可

5 dubbo接口注册不上zookeeper,原因可能有多种

     1)看下zookeeper服务是不是ping的通

     2)看下是不是其他接口有注册到zookeeper上,看下接口provider的相关dubbo的注册url信息,从注册信息中看下是否有问题,

     3)看下换一个zookeeper服务重试,看下dubbo接口是不是能够注册到新的zookeeper上

  (1)遇到一个dubbo接口注册不上zookeeper的原因是因为接口名称换了,然后zookeeper一直缓存原来的接口名称,并且由于maven deploy到仓库的接口jar包是没有clean过的,导致原来改名之前的接口类依然存在,其他服务依赖该接口出现了连不上该dubbo接口问题。同时原来的容器实例被瞎掉了,可以看下是不是实例依旧存在。

springboot下dubbo启动过程

对于dubbo而言,有两个很重要的类ServiceBean和ReferenceBean,这两个类分别调用ServiceBean.export和ReferenceBean.get分别暴露服务以及获取远程服务。Dubbo程序是以Spring Beans的形式存在于Spring容器中,随着Spring容器启动而启动。在使,用这两个bean的时候,dubbo的设计者希望可以让用户通过以Annotation的方式使用dubbo,所以他们使用了spring的很多拓展点,在spring容器启动过程中分别自动发现,注册,实例化ServiceBean和ReferenceBean。

dubbo服务源码分析

在dubbo中,服务接口+服务分组+服务版本能够顾唯一确定一个服务,同一个服务接口可以有不同的版本以便服务升级

dubbo中有两个比较重要的类ServiceConfig和ReferenceConfig,真正的dubbo暴露服务和调用服务如下:

/**
**暴露服务过程
*/
//创建serviceConfig实例
ServiceConfig<xxInterface> serviceConfig=new ServiceConfig(xxInterface);
//设置应用程序配置
serviceConfig.setApplication(new ApplicationConfig("first-dubbo-provider"));

//设置服务注册中心信息
RegistryConfig registryConfig=new RegistryConfig("zookeeper://127.0.0.1:3444");
serviceConfig.setRegistry(registryConfig)
//设置接口和实现类
serviceConfig.setInterface(xxInterface.class);
serviceConfig.setRef(new xxInterfaceImpl());

serviceConfig.setVersion("1.0.0");
serviceConfig.setGroup("dubbo");

//设置线程池策略
/HashMap<String,String> params=new HashMap();
//params.put("threadpool","mythreadPool");
//serviceConfig.setParameters(params);

//导出服务
serviceConfig.export();

//dubbo服务同时支持本地导出和远程导出协议,其中none表示不到处服务,remote表示之淡出远程服务,local只导出本地服务
serviceConfig.setScope("remote")

//挂起服务,避免服务停止
System.in.read();


//创建服务引用对象实例
RefereceConfig<xxInterface> referenceConfig=new ReferenceConfig<xxInterface>();
//设置应用程序信息
referenceConfig.setApplication(new ApplicationConfig("first-dubbo-consumer"));
//设置服务注册中心
referenceConfig.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:3344"));

//设置服务接口和超时时间
referenceCOnfig.setInterface(xxInterface.class);
referenceConfig.setTimeout(5000);

//设置自定义负载策略和集群容错策略
//referenceConfig.setLoadBalance("myLoadBalance");
//referenceConfig.setCluster("myBroadCast");

//设置服务分组和版本
referenceConfig.setVersion("1.0.0");
referenceConfig.setGroup("dubbo");

//可以设置为异步的
//refereceConfig.setAysc(true);

//引用服务
xxInterface  xxinter=referenceConfig.get();

//设置隐式参数
RpcCOntext.getContext().setAttachment("param1","parm");

//调用服务,如果服务设置为异步的,此时返回的是null
xxInter.xxmethod();

//如果是异步调用的,这样才会获取得到异步调用的值,这里借助了netty底层IO线程,在接收到响应后自动毁掉注册的回调函数,dubbo2.7版本以后支持CompletableFuture
Future<xx> future=RpcContext.getContext().getFuture();
future.setCallback(new ResponseCallback(){
    @Override
    public void done(Object response){
        //TODO
    }
});

dubbo的分层架构

dubbo的分层架构一共有10层,就像是网络的7层协议一样,每一个上层都会依赖下层的功能,下层的改变对上层来说是不可见的,并且每一层都是可以替换的组件。

10层从上到下分别是service,config,proxy,registry,cluster,monitor,protocol,exchange,transport,serialize,除了serivce和config层,其他组件都是SPI(Service Provider Interface)层,并且这些层都有一个扩展点接口,每个拓展点接口都有@SPI标注,表明默认使用某个实现类(可以通过配置文件更改)

proxy层:SPI扩展接口类是ProxyFactory,其默认的实现类是JavassistProxyFactory(JdkProxyFactory),如下图,其中JavassistProxyFactory为每个服务提供者实现类生成了Wrapper类

Registry层:SPI扩展接口是RegistryFactory,其默认的实现类是DubboRegistryFactory

dubbo在springboot的fresh context阶段第一步如下:

可以看到获取的ExensionLoader内部有type属性(如Filter.class)以及ExtensionFactory实例(实际上就是AdaptiveExtensionFactory对象)

AdaptiveExtensionFactory内部有两个ExtensionFactory,一个是SpiExtensionFactory,一个是DubboExtensionFactory

获取扩展器实例如下

需要经过createAdaptiveExtension()->getAdaptiveExensionClass-》createAdaptiveExendsionClass

实际上内部最终调用了loadExtensionClasses方法进行加载实现类,loadExtensionClass实际上是从META-INF/dubbo/internal,MEAT-INF/services以及META-INF/dubbo资源路径下获取classes

除此之外,dubbo在getExtensionClasses里面的loadDirectory()->loadResource->loadClass方法除了加载加载拓展接口的所有实现类的class独享,还对包装类Wrapper类进行了收集

wrapper类的判断时wrapper类必须含有参数为实现接口的实现类

如下图所示ProtocolListenerWrapper含有参数为Protocol的实现类,同时它也继承了Protocol接口

dubbo会给每个服务提供者的服务接口实现类生产一个Wrapper类,这个Wrapper类里面最终调用的服务提供者的服务接口实现类,Wrapper类的存在意义是为了减少反射的调用,当服务提供方收到来回消费方的请求,需要根据消费者传递过来的方法名和参数反射调用服务提供者的实现类,而反射本身是具有性能开销的,dubbo把每个服务提供者的实现类通过JavaAssist包装成一个Wrapper类以减少反射的调用开销。

public class GreetingServiceImpl implemets GreetingService{
@Override
public String sayHello(String name){
  return "hello "+name;
}

//对应的wrapper类的源码就是
public class Wrapper1 extends Wrapper implements ClassGenerator.DC{
    public Object invokeMethod(Object object,String string,Class[] arrclass,Object[] arrobject) throw InvocationTargetException{
    GreetingServiceImpl greetintgServiceimpl;
    try{
        greetingServiceImpl=(GreetingServiceImpl) object;
    }catch(Throwable throwable){
        throw new IllegalArgumentsException(throwable)
    }
}
    
}
//dubbo接口实现类的wrapper类是在ProxyFactory中产生的,Dubbo提供的实现主要是JavassistProxyFactory(默认的)和JdkProxyFactory

dubbo里面的DubboProtol经过了两层包装,第一次使用ProtocolListenerWrapper类对dubboProtocol进行了包装,这个时候ProtocolListenerWrapper里面的impl就是DubboProtocol,然后第二次适宜用了ProtocolFilterWrapper对ProtocolListenerWrapper进行了包装,也就是说ProtocolFilterWrapper里面的impl就是ProtocolListenerWrapper,这个时候适配器Protocol$Adaptive的export方法,如果URL对象里面的protocol就是dubbo的话,那么在扩展点自动包装室,protocol。export返回的就是ProtocolFitlerWrapper.

实际上REGISTERY_PROTOCOL可以分为几种类型,如果为远程服务暴露,则内部的protocol类型是retistry,会选择protocol的实现类RegistryProtocol,如果为本地服务暴露,则根据url的protocol类型为injvm,会选择InjvmProtocol

//ProtocolListenerWrapper的输出方法 
@Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        //REGISTRY_PROTOCOL="registry"
        if (REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
            return protocol.export(invoker);
        }
        return new ListenerExporterWrapper<T>(protocol.export(invoker),
                
//EXPORTER_LISTENER_KEY="exporter.listener"
Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
                        .getActivateExtension(invoker.getUrl(), EXPORTER_LISTENER_KEY)));
    }

//实际上ServiceConfig里面exporters.add(exporter),这个exporter最终获取的就是ListenerExporterWrapper
new ListenerExporterWrapper<T>(protocol.export(invoker),
                Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
                        .getActivateExtension(invoker.getUrl(), EXPORTER_LISTENER_KEY)));
    @Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        if (REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
            return protocol.export(invoker);
        }
        //SERVICE_FILTER_KEY="service.filter"
        //buildInvokeChain得到的是new CallbackRegistrationInvoker<>
        return protocol.export(buildInvokerChain(invoker, SERVICE_FILTER_KEY, CommonConstants.PROVIDER));
    }

可以看到ExtendsionLoader的类型参数都是ExensionFactory,然后ExtensionFactory有两种,一种SpiExtensionFactory,另一种是SpringExtensionFactory

每一个扩展接口都有扩展实现类,选择某个实现类实际上是通过适配器来选择的,适配器是通过dubbo的Compiler接口生成接口的适配器,如下卖弄生成Protocol接口的适配器,核心方法使用过ExtensionLoader.createAdaptiveExtensionClass,内部通过AdaptiveCompiler.compile(code,ClassLoader),编译生成class对象,所以适配器类是dubbo动态生成的。代码通过AdaptiveClassCodeGenerator.generate方法生成。可以看到不同的适配器会生成不同的代码。

getExtension获取wrapper包裹的核心实现类

getAdaptiveExtension获取的是xx$Adaptive适配类对象

org.apache.dubbo.common.extension.ExtensionLoader#createAdaptiveExtensionClass 可以在这里打断点看动态代码。在调试的时候因为动态生成的code编译后的类没有源码,所以一般会调到对应的接口代码上。实际上wrapper包裹的是spring aop增强过的bean

适配器代码如下:

package org.apache.dubbo.rpc;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
    public void destroy()  {
        throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }
    public int getDefaultPort()  {
        throw new UnsupportedOperationException("The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }
    public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {
        if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
        if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
        org.apache.dubbo.common.URL url = arg0.getUrl();
        String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
        if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
        org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
        return extension.export(arg0);
    }
    public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException {
        if (arg1 == null) throw new IllegalArgumentException("url == null");
        org.apache.dubbo.common.URL url = arg1;
        String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
        if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
        org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
        return extension.refer(arg0, arg1);
    }
}
package org.apache.dubbo.rpc;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class ProxyFactory$Adaptive implements org.apache.dubbo.rpc.ProxyFactory {
    public java.lang.Object getProxy(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {
        if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
        if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
        org.apache.dubbo.common.URL url = arg0.getUrl();
        String extName = url.getParameter("proxy", "javassist");
        if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
        org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
        return extension.getProxy(arg0);
    }
    public java.lang.Object getProxy(org.apache.dubbo.rpc.Invoker arg0, boolean arg1) throws org.apache.dubbo.rpc.RpcException {
        if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
        if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
        org.apache.dubbo.common.URL url = arg0.getUrl();
        String extName = url.getParameter("proxy", "javassist");
        if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
        org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
        return extension.getProxy(arg0, arg1);
    }
    public org.apache.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, org.apache.dubbo.common.URL arg2) throws org.apache.dubbo.rpc.RpcException {
        if (arg2 == null) throw new IllegalArgumentException("url == null");
        org.apache.dubbo.common.URL url = arg2;
        String extName = url.getParameter("proxy", "javassist");
        if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
        org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
        return extension.getInvoker(arg0, arg1, arg2);
    }
}

每一个扩展接口都有一个ExtendsionLoader对应,ExtensionLoader.getExtensionLoader中获取对应的ExtensionLoader,如果没有,则创建一个,在ServiceConfig中代码如下

ExtensionLoader内部有个EXTENSION_LOADERS,类型为ConcurrentMap<Class<?>, ExtensionLoader<?>>,key是Filter这些需要扩展的类class,但是真正的以拓展接口的类为key,以ExntensionLoader为value

ExtendsionLoader内部方法getExtensionClasses(xxx)通过META-INF/dubbo/internal,以及META-INF/dubbo以及META-INF/services这三个路径下面获取所有的Protocol实现类

package org.apache.dubbo.rpc;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
	public void destroy()  {
		throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
	}
	public int getDefaultPort()  {
		throw new UnsupportedOperationException("The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
	}
	public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {
		if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
		if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
		org.apache.dubbo.common.URL url = arg0.getUrl();
		String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
		if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
		org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
		return extension.export(arg0);
	}
	public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException {
		if (arg1 == null) throw new IllegalArgumentException("url == null");
		org.apache.dubbo.common.URL url = arg1;
		String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
		if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");

//☆getExtension方法就已经实现了wrapper包裹核心实现类
		org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
		return extension.refer(arg0, arg1);
	}
}

有的时候我们的扩展接口只需要所有实现类中的其中一个,但是现在有些扩展接口需要使用到全部扩展接口,例如Filter

比如ProtocolFilterWrapper类中的buildInvokerChain方法在建立filter责任链时,需要把属于某一个group中的所有Filter放入到责任链中,那是通过获取属于某个组的Filter来扩展实现类,当服务提供端启动时只会加载group名为provider的filter扩展实现类,而消费启动时只会加载group名为consumer的filter的扩展类

@Activate(group=Constants.PROVIDER,value=Constants.EXECUTES_KEY)
public class ExecuteLimitFilter implements Filter
@Activate(group=Constants.CONSUMER,value=Constants.ACTIVES_KEY)
public class ActiveLimitFilter implements Filter

dubbo 服务导出过程解析

dubbo服务发布导出过程是在Context fresh阶段通过ServiceBean(该服务通过实现ApplicationListener去部署dubbo服务)

在dubbo里会将所有参数都封装到一个url中然后具体执行服务导出,其核心方法是ServiceConfig中断doExportUrlsFor1Protocol,如果是延迟发布,则会使用scheduledExecutorService来实现,可以在serviceConfig中setDelay(int)来设置发布时间,dubbo在服务导出过程中会将所有参数拼接成url,然后执行具体的导出过程.

Javaassist 就是一个用来处理 Java 字节码的类库。它可以在一个已经编译好的类中添加新的方法,或者是修改已有的方法,并且不需要对字节码方面有深入的了解。不需要了解字节码指令(同类型的ASM需要),性能比ASM稍差些,但是简单,同时也可以去生成一个新的类对象,通过完全手动的方式。

首先ExtensionLoader会根据在getExtension中从cachedInstance中获取holder,发现holder为null的时候,会调用createExtension(可能会有wrapper进行增强),wraper类也是实现接口(protocol)的类,不同的是,extensionloader在寻找接口实现类的时候回根据isWrapperclass函数来判断(内部判断逻辑就是这个类有没有一个参数为接口对象的拷贝构造函数)。

Wrapper

dubbo在导出服务的时候,会给每个发布的接口实现上套上一层wrapper

在服务(本地和远程)暴露的时候会调用proxyFactory.getInvoker方法

具体位置:

  • 本地暴露:ServiceConfig#exportLocal line:538
  • 远程暴露: ServiceConfig#doExportUrlsFor1Protocol line:512

会先调用AOP织入的类StubProxyFactoryWrapper#getInvoker

然后执行JavassistProxyFactory#getInvoker

JavassistProxyFactory#getInvoker如下: 得到的AbstractProxyInvoke后面在调用invoke方法的时候实际上里面调用的是对应wrapper的invokeMethod方法。

public class JavassistProxyFactory extends AbstractProxyFactory {

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }

    @Override
    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        // TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
        return new AbstractProxyInvoker<T>(proxy, type, url) {
            @Override
            protected Object doInvoke(T proxy, String methodName,
                                      Class<?>[] parameterTypes,
                                      Object[] arguments) throws Throwable {
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };
    }
}

//待发布的接口
public interface TestService {

    String getData(String var1);

    List<String> getList();
}

//首先通过makWrapper得到Wrapper对象,wrapper对象如下:
public class Wrapper0 extends com.alibaba.dubbo.common.bytecode.Wrapper {

    /**
     * 属性名, 属性类型
     */
    public static java.util.Map pts = new HashMap<String, Class<?>>();
    public static String[] pns = new String[0];

    /**
     * 所有的方法名
     */
    public static String[] mns = {"getData"};
    /**
     * 本类中的所有方法名
     */
    public static String[] dmns = {"getData"};

    /**
     * 一个方法中所有的参数类型  mts[n]属性的个数和方法的个数形同
     */
    public static Class[] mts0 = {String.class};

    public static Class[] mts1 = {List.class};

    @Override
    public String[] getPropertyNames() {
        return pns;
    }

    @Override
    public boolean hasProperty(String n) {
        return pts.containsKey(n);
    }

    @Override
    public Class getPropertyType(String n) {
        return (Class) pts.get(n);
    }

    @Override
    public String[] getMethodNames() {
        return mns;
    }

    @Override
    public String[] getDeclaredMethodNames() {
        return dmns;
    }

    @Override
    public void setPropertyValue(Object o, String n, Object v) {
        per.qiao.service.TestService w;
        try {
            w = ((per.qiao.service.TestService) o);
        } catch (Throwable e) {
            throw new IllegalArgumentException(e);
        }
        throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \"" + n + "\" filed or setter method in class per.qiao.service.TestService.");
    }

    @Override
    public Object getPropertyValue(Object o, String n) {
        per.qiao.service.TestService w;
        try {
            w = ((per.qiao.service.TestService) o);
        } catch (Throwable e) {
            throw new IllegalArgumentException(e);
        }
        if (n.equals("list")) {
            return w.getList();
        }
        throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \"" + n + "\" filed or setter method in class per.qiao.service.TestService.");
    }
	
  	/**
  	 *  在调用接口时,就时调用的这个方法
  	    @param o 接口实例
  	    @param n 方法名
  	    @param p 参数类型
  	    @param v 参数
  	 */
    @Override
    public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException {
        per.qiao.service.TestService w;
        try {
            w = ((per.qiao.service.TestService) o);
        } catch (Throwable e) {
            throw new IllegalArgumentException(e);
        }
        try {
            //这个try范围内就是你所需要暴露的所有方法
            //可以看到生成的wrapper是直接调用的对象的方法,而不是通过反射来调用的,因此dubbo接口在调用的时候可以减少传统反射带来的开销
            if ("getData".equals(n) && p.length == 1) {
                return w.getData((java.lang.String) v[0]);
            }
            if ("getList".equals(n) && p.length == 0) {
                return w.getList();
            }
        } catch (Throwable e) {
            throw new java.lang.reflect.InvocationTargetException(e);
        }
        throw new com.alibaba.dubbo.common.bytecode.NoSuchMethodException("Not found method \"" + n + "\" in class per.qiao.service.TestService.");
    }
}

生成wrapper以后,这些wrapper会保存在Wrapper类的WRAPPER_MAP中

public static Wrapper getWrapper(Class<?> c) {
        while (ClassGenerator.isDynamicClass(c)) // can not wrapper on dynamic class.
        {
            c = c.getSuperclass();
        }

        if (c == Object.class) {
            return OBJECT_WRAPPER;
        }

        Wrapper ret = WRAPPER_MAP.get(c);
        if (ret == null) {
            ret = makeWrapper(c);
            WRAPPER_MAP.put(c, ret);
        }
        return ret;
    }

得到wrapper以后使用DelegateProviderMetaDataInvoker包装AbstractProxyInvoker和metaData(实际上就是ServiceConfig)

public class DelegateProviderMetaDataInvoker<T> implements Invoker {
    protected final Invoker<T> invoker;
    private ServiceConfig metadata;

    public DelegateProviderMetaDataInvoker(Invoker<T> invoker, ServiceConfig metadata) {
        this.invoker = invoker;
        this.metadata = metadata;
    }
}

在dubboProtocol中有个Map<String, Exporter<?>> exporterMap = new ConcurrentHashMap<String, Exporter<?>>();

实际上存储的key是serviceKey,这个key实际上是[serviceGroup/]serviceName[:serviceVersion]:port,例如:com.oppoer.spring.facade.UserFacade:7006

存储的value是 DubboExporter

在发布的时候有个url参数optimizer是可以

//dubboProtocol方法
private void optimizeSerialization(URL url) throws RpcException {
        String className = url.getParameter(OPTIMIZER_KEY, "");
        if (StringUtils.isEmpty(className) || optimizers.contains(className)) {
            return;
        }

        logger.info("Optimizing the serialization process for Kryo, FST, etc...");

        try {
            Class clazz = Thread.currentThread().getContextClassLoader().loadClass(className);
            if (!SerializationOptimizer.class.isAssignableFrom(clazz)) {
                throw new RpcException("The serialization optimizer " + className + " isn't an instance of " + SerializationOptimizer.class.getName());
            }

            SerializationOptimizer optimizer = (SerializationOptimizer) clazz.newInstance();

            if (optimizer.getSerializableClasses() == null) {
                return;
            }

            for (Class c : optimizer.getSerializableClasses()) {
                SerializableClassRegistry.registerClass(c);
            }

            optimizers.add(className);

。。。。。。。

export发布服务过程

发布服务以后,dubbo的ServiceBean会调用ApplicationEventPublisher来发布事件

//1.ServiceBean通过继实现ApplicationListener方法来监听ContextRefreshedEvent事件
@Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if (!isExported() && !isUnexported()) {
            if (logger.isInfoEnabled()) {
                logger.info("The service ready on spring started. service: " + getInterface());
            }
            export();
        }
    }
//2.export服务

//ServiceBean通过继承ApplicationEventPublisherAware接口来获取ApplicationEventPublisher
private void publishExportEvent() {
        ServiceBeanExportedEvent exportEvent = new ServiceBeanExportedEvent(this);
        applicationEventPublisher.publishEvent(exportEvent);
    }

注意:dubbo的javassist动态代理是覆盖在springframework的cglib动态代理之上的

在DubboProtocol服务中会openServer,开启nettyserver服务器,并且服务consumer端在启动后会连接每一个provider服务,对于同一个provider服务器的不同服务会共享同一个连接

dubbo consume 服务消费启动流程分析

consume服务在启动过程中会将待引用的dubbo接口和ReferenceBean(实现了FactoryBean接口)进行关联起来,然后在bean实例化的时候通过ReferenceBean.getObject获取远程调用代理类

dubbo在服务引用(refer)以及销毁引用(destroy)服务的过程中提供了回调窗口,供用户做业务处理。ProtocolListenerWrapper在暴露过程中构建了监听器链

Dubbo的开源版本中没有监听的实现,但是开放了口子,业务方如有需要可以利用这个功能实现特定的业务,具体开发路径如下和Protocol等方式是一样的,都是通过扩展META-INF/dubbo这种类似文件

在Protocol$Adaptive中真正的register是带有"registey"的RegistryProtocol,而不是dubboProtocol

最后在获取得到Invoker以后,ReferenceConfig还进行了进一步动态代理Proxy.getProxy,无论是JdkProxyFactory还是JavassistProxyFactory最后都使用InvokeInovcationHandler,也就是说都是用java原生的动态代理进行生成远程调用接口

的代理类。

//ReferenceConfig.init方法里面
return (T) PROXY_FACTORY.getProxy(invoker);

dubbo  consume服务远程调用流程

都是从InvokeInovationHandler.invoke开始的

dubbo provider服务路程

解码之后到HeaderExchangeHandler的received方法:

public void received(Channel channel, Object message) throws RemotingException {
    channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
    ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel);
    try {
    	//request类型的消息
        if (message instanceof Request) {
            Request request = (Request) message;
            if (request.isEvent()) {//判断心跳还是正常请求
            	//	处理心跳
                handlerEvent(channel, request);
            } else {//正常的请求
            	//需要返回
                if (request.isTwoWay()) {
                	//处理请求,并构造响应信息
                    Response response = handleRequest(exchangeChannel, request);
                    //NettyChannel,发送响应信息
                    channel.send(response);
                } else {//不需要返回的处理
                    handler.received(exchangeChannel, request.getData());
                }
            }
        } else if (message instanceof Response) {//response类型的消息
            handleResponse(channel, (Response) message);
        } else if (message instanceof String) {
            if (isClientSide(channel)) {
                Exception e = new Exception("Dubbo client can not supported string message: " + message + " in channel: " + channel + ", url: " + channel.getUrl());
            } else {
                //telnet类型,即invoke的字段,此时messsage就是string类型,内容即“invoke xx.xx(xx)”
                String echo = handler.telnet(channel, (String) message);
                if (echo != null && echo.length() > 0) {
                    channel.send(echo);
                }
            }
        } else {
            handler.received(exchangeChannel, message);
        }
    } finally {
        HeaderExchangeChannel.removeChannelIfDisconnected(channel);
    }
}

Decodeable
RpcInvocation
DecodeableRpcInvocation

DubboProtocol#getInvoker方法中,从exporterMap中获取DubboExporter  serviceKey一般是完整的类名:端口号

HeaderExchangeChannel 里面的channel是NettyChannel
NettyChannel下面的channel是NioSocketChannel

从exporterMap中获取到的是ProtocolFilterWrapper#CallbackRegisterInvoker
而callbackRegisterInvoker内部的filterInvoker是RegisterProtocl#InvokerDelegate
RegisterProtocol#InvokerDelegate内部的invoker是DelegateProviderMetaDataInvoker

而DelegateProviderMetaDataInvoker的invoker是JavassistProxyFactory#getInvoker中产生的AbstractProxyInvoker
AbstractProxyInvoker#invoke方法首先是调用doinvoke方法得到返回值
doInvoke方法中的proxy就是实现类的对象,没有经过任何增强处理
其方法中的methodName,parameterTypes以及agruments都是DecodeableRpcInvocation中得到的

JavassistProxyFactory产生的代理类Wrapper,其内部方法invokeMethod是通过编写string产生的,是直接产生的
有三个方法是通过这个方式产生的
c1  setPropertyValue
c2  getPropertyValue
c3  invokeMethod

然后通过ClassGenerator.addMethod方式添加这三个方法

RpcContext.getContext方法是从其内部静态变量LOCAL(ThreadLocal)中获取的线程独立变量,
在ContextFilter执行invoker之前,都会创建新的RpcContext,给RpcContext设置invoker,Invocation以及localADDress和remoteApplicationName
且当invoker执行完后都会移除RpcContext内容,ServerRpcContext也会被remove掉

this.storedContext = RpcContext.getContext();
this.storedServerContext = RpcContext.getServerContext();

dubbo线程模型和线程池策略

dubbo线程模型的解析:dubbo底层的网络通信框架使用的是netty ,netty服务使用两级线程池,其中EventLoopGroup(boss)主要用来接收客户端的连接请求,并将完成三次握手的连接分发给EventLoopGroup(worker)来处理

我们将boss和worker线程组称为IO线程,,如果服务提供方的逻辑处理能迅速完成,并且不会发起新的io请求,那么直接在IO线程上处理更快,可以减少线程池调度和上下文切换的开销

如果处理逻辑较为复杂,或者需要发起新的IO情趣,比如查询数九,那么IO线程必须派发新的线程池进行处理,否则IO线程会被阻塞,不能接受其他请求

根据请求的消息是否被派发到io线程组还是业务线程组,dubbo提供了五种线程模型

all(AllDispatcher,默认的):将所有的消息都派发到业务线程池中,这些消息包括请求、响应,连接时间,断开时间,心跳事件

direct(DirectDispatcher):所有消息都不派发到业务线程池中的,全部都在IO线程池中

mssage(MessageOnlyDispatcher):只有请求响应的消息事件会被派发到业务线程池总,其他消息被派发到IO线程中执行

execution(ExecutionDispatcher):只把请求类消息派发到业务线程池中执行,其他消息事件直接在io线程中执行

connection(ConnectionOrderedDispatcher):在IO线程中将连接事件,断开事件放入到队列中有序逐个执行,其他消息派发到业务线程池中处理。

public class AllDispatcher implements Dispatcher {

    public static final String NAME = "all";

    @Override
    public ChannelHandler dispatch(ChannelHandler handler, URL url) {
        
        return new AllChannelHandler(handler, url);
    }

}

AllChannelHandle继承自WrappedChannelHandler,WrapperChannelHandler有各种默认的处理方法,其内部都是通过直接调用DecodeHandler的各种处理方法,而DecodeHandler和AllChannelHandler都实现了ChannelHandler接口的方法,而AllDispatcher中new AllchannelHandler构造参数的ChannelHandler就是DecodeHandler,而NettyChannel实现了Channle接口

AllChannelHandler将nettyChannel和msg(Request内部的mData即是DecodeableRpcInvocation)都放入到ChannelEventRunnable,其内部的run方法和WrapChannelHandler中的处理一样都是通过DecodeHandler.received等各种方法处理,只不过AllChannelHandler使用了线程池ThreadPoolExecutor

dubbo mock相关

在开发自测,联调过程中,经常碰到一些下游服务调用不通的场景,这个时候我们如何不依赖于下游系统,就业务系统独立完成自测?
dubbo自身是支持mock服务的,在reference标签里,有一个参数mock,该参数有四个值,false,default,true,或者Mock类的类名。分别代表如下含义:

  • false,不调用mock服务。
  • true,当服务调用失败时,使用mock服务。
  • default,当服务调用失败时,使用mock服务。
  • force,强制使用Mock服务(不管服务能否调用成功)。(使用xml配置不生效,使用ReferenceConfigAPI可以生效

dubbo Router路由

Dubbo路由机制概述   https://blog.csdn.net/prestigeding/article/details/80848594

路由机制,Dubbo的路由机制主要解决的目的就是服务调用时,从已知的所有服务提供者中根据路由规则刷选服务提供者。,路由机制可以筛选出合适的invoker,白名单和黑名单等

DUBBO的注册中心

Zookeeper 注册中心: 基于分布式协调系统 Zookeeper 实现,采用 Zookeeper 的 watch 机制实现数据变更(官方推荐)
Multicast 注册中心: 基于网络中组播传输实现,不需要任何中心节点,只要广播地址,就能进行服务注册和发现
Redis 注册中心: 基于 Redis 实现,采用 key/Map 数据结构存储,主 key 存储服务名和类型,Map 中 key 存储服务 URL,Map 中 value 存储服务过期时间,基于 Redis 的发布/订阅模式通知数据变更
Simple 注册中心:一个普通的 Dubbo 服务,可以减少第三方依赖,使整体通讯方式一致,不支持集群

面试题:

Dubbo中zookeeper做注册中心,如果注册中心集群全都挂掉,发布者和订阅者之间还能通信么?

1、【提供者】在【启动】时,向注册中心zk 【注册】自己提供的服务。

 2、【消费者】在【启动】时,向注册中心zk 【订阅】自己所需的服务。

可以的,消费者在启动时,消费者会从zk拉取注册的生产者的地址接口等数据,缓存在本地。

每次调用时,按照本地存储的地址进行调用

可以的,消费者本地有一个生产者的列表,他会按照列表继续工作,倒是无法从注册中心去同步最新的服务列表,短期的注册中心挂掉是不要紧的,但一定要尽快修复

挂掉是不要紧的,但前提是你没有增加新的服务,如果你要调用新的服务,则是不能办到的

Dubbo的通信框架

dubbo通信框架 netty reactor模式   https://www.baidu.com/link?url=3eNxZeXsoHMXoj-0jaynEfRt2JpqaGErYJ9C9aR2VT_T-JBB_jUAlyd88csu9XyE_RUrapNFe6VEXWGyPK5TfT54LLwTrZdFm8hs4F926sy&wd=&eqid=99ce5f7800021fae0000000660a27a75

Dubbo服务注册与动态发现机制的原理与实现细节   https://www.jianshu.com/p/9271068e0caf

服务上线怎么兼容旧版本?

可以用版本号(version)过渡,多个不同版本的服务注册到注册中心,版本号不同的服务相互间不引用。这个和服务分组的概念有一点类似。

Dubbo SPI 和 Java SPI 区别?

JDK SPI

JDK 标准的 SPI 会一次性加载所有的扩展实现,如果有的扩展吃实话很耗时,但

也没用上,很浪费资源。

所以只希望加载某个的实现,就不现实了

DUBBO SPI

1,对 Dubbo 进行扩展,不需要改动 Dubbo 的源码

2,延迟加载,可以一次只加载自己想要加载的扩展实现。

3,增加了对扩展点 IOC 和 AOP 的支持,一个扩展点可以直接 setter 注入其

它扩展点。

3,Dubbo 的扩展机制能很好的支持第三方 IoC 容器,默认支持 Spring Bean。

Dubbo和缓存

可以,Dubbo 提供了声明式缓存,用于加速热门数据的访问速度,以减少用户加缓存的工作量。

<dubbo:reference cache="true" />

其实比普通的配置文件就多了一个标签 cache="true"

dubbo和分布式事务

Dubbo 如何实现分布式事务  https://blog.csdn.net/weixin_34148456/article/details/92387930

dubbo和springcloud服务的区别

Java 微服务框架选型(Dubbo 和 Spring Cloud?)  https://www.cnblogs.com/xishuai/p/dubbo-and-spring-cloud.html

比较spring cloud和dubbo,各自的优缺点是什么   https://blog.csdn.net/djrm11/article/details/106431170

dubbo服务之间的调用是否是异步的

默认是同步等待结果阻塞的,支持异步调用。

Dubbo 是基于 NIO 的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小,异步调用会返回一个 Future 对象。

异步调用流程图如下。

img

Dubbo服务之间的调用是同步的吗?https://blog.csdn.net/u010627840/article/details/101275533

Dubbo 在安全方面有哪些措施?

  • Dubbo 通过 Token 令牌防止用户绕过注册中心直连,然后在注册中心上管理授权。
  • Dubbo 还提供服务黑白名单,来控制服务所允许的调用方。

dubbo 令牌验证 源码分析

在ServiceConfig.java中在doExportUrlsFor1Protocol中:

       

if (! ConfigUtils.isEmpty(token)) {//如果配置令牌功能
            if (ConfigUtils.isDefault(token)) {
                map.put("token", UUID.randomUUID().toString());
            } else {
                map.put("token", token);
            }
        }

如果服务提供者只是设置token=true,那使用系统使用UUID生成token令牌,否则直接使用token做令牌

当消费者发起调用时,会运行到TokenFilter调用链进行处理:

@Activate(group = Constants.PROVIDER, value = Constants.TOKEN_KEY)
public class TokenFilter implements Filter {
 
    public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
        String token = invoker.getUrl().getParameter(Constants.TOKEN_KEY);
        if (ConfigUtils.isNotEmpty(token)) {
            Class<?> serviceType = invoker.getInterface();
            Map<String, String> attachments = inv.getAttachments();
            String remoteToken = attachments == null ? null : attachments.get(Constants.TOKEN_KEY);
            if (! token.equals(remoteToken)) {
                throw new RpcException("Invalid token! Forbid invoke remote service " + serviceType + " method " + inv.getMethodName() + "() from consumer " + RpcContext.getContext().getRemoteHost() + " to provider "  + RpcContext.getContext().getLocalHost());
            }
        }
        return invoker.invoke(inv);
    }
 
}

group=Constants.PROVIDER表示当rpc请求到达服务端时才会运行这个调用链,value=Constants.TOKEN_KEY表示只有开启token令牌功能时,才运行这段代码逻辑。
 

dubbo的线上问题

阿里面试题:使用dubbo过程中遇到过哪些坑? https://blog.csdn.net/u013256816/article/details/102674846

dubbox和dubbo的区别

Dubbo 使用rest协议发布http服务  https://www.cnblogs.com/loveyou/p/9551468.html

服务提供者能实现失效踢出是什么原理?

服务失效踢出基于 zookeeper 的临时节点原理。

基于zookeeper的临时节点原理

持久节点 
所谓持久节点,是指在节点创建后,就一直存在,直到有删除操作来主动清除这个节点,也就是说不会因为创建该节点的客户端会话失效而消失

临时节点 
临时节点的生命周期和客户端会话绑定,也就是说,如果客户端会话失效,那么这个节点就会自动被清除掉

4、创建的临时节点什么时候会被删除,是连接一断就删除吗?延时是多少? 
连接断了之后,ZK不会马上移除临时数据,只有当SESSIONEXPIRED之后,才会把这个会话建立的临时数据移除。因此,用户需要谨慎设置Session_TimeOut

说说 Dubbo 服务暴露的过程。

Dubbo 会在 Spring 实例化完 bean 之后,在刷新容器最后一步发布 ContextRefreshEvent 事件的时候,通知实现了 ApplicationListener 的 ServiceBean 类进行回调 onApplicationEvent 事件方法,Dubbo 会在这个方法中调用 ServiceBean 父类 ServiceConfig 的 export 方法,而该方法真正实现了服务的(异步或者非异步)发布。

问题集锦

https://blog.csdn.net/weixin_43647393/article/details/103466758

dubbo 面试18问(含答案):https://mp.weixin.qq.com/s?__biz=MzAxMjEwMzQ5MA==&mid=2448887352&idx=2&sn=4f91cb206362006dc3226fadd1b4b75e&chksm=8fb55415b8c2dd0326b6362e2cc42841d0f7f8e0738e384a7f251aade4b4a210acc70d73c66a&mpshare=1&scene=23&srcid=121014JQxHAK2YYc2LbUUsGT&sharer_sharetime=1575909073768&sharer_shareid=93b93f64d6be12a070aa3eafe50296b4#rd

1问题:假如有多个接口实现的远程服务,dubbo是如何进行区分的

在Dubbo中接口类并不能唯一确定一个服务,在Dubbo中接口+服务分组+版本号才能唯一确定一个服务所以,当一个接口有多种实现时,可以使用group分组区分

如果版本号和group在providers服务启动的时候没有配置,注册到zookeeper上面的url没有group和version字段,常见的注册字段包括

[dubbo://10.0.75.1:7006/com.oppoer.spring.facade.UserFacade?anyhost=true&application=dubboProvider&bean.name=ServiceBean:com.oppoer.spring.facade.UserFacade:version-1.0:group--1&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&group=group--1&interface=com.oppoer.spring.facade.UserFacade&methods=getUserName&pid=15672&register=true&release=2.7.3&revision=version-1.0&serialization=fastJson&side=provider&timestamp=1616225938146&version=version-1.0]

其中dubbo 字段表示dubbo的版本,release表示dubbo的jar包版本,side表示是提供方还是消费方,serialization表示使用的的是哪个序列化方式

如果请求参数或响应结果,数据容量过大(超过8M)

Dubbo传输数据大小默认为8M,超限则会抛出异常,如:Data length too large: 11557050, max payload: 8388608(8*1024*1024)
大数据的传输并不符合Dubbo设计的初衷,Dubbo的长连接主要用于小数据包频繁调用,对于大数据的传输可以考虑通过其他方式实现,如设置dubbo.protocol.payload=11557050

绕过注册中心直接连接提供服务

<dubbo:reference id="serviceInfoService" check="false"
                 interface="com.beibei.customer.service.ServiceInfoService" protocol="dubbo" timeout="6000"
                 retries="0" url="dubbo://172.17.33.108:20882"/>


版权声明:本文为qq_36236038原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。