精华内容
下载资源
问答
  • 一个好的Web应用程序,强大的异常处理是少不了的... Service代理时发生异常的处理办法 2,使用Asp.NetAjax内建的异常处理方法 3,Web.Config中customError节中设置错误跳转页   此篇博客总结的是:在客户端调用Web Servi

    一个好的Web应用程序,强大的异常处理是少不了的。最近在学习Ajax的过程中,我总结了几种不同情况下的处理异步调用中异常的不同办法;想分享给大家;

    1,在客户端调用Web Service代理时发生异常的处理办法

    2,使用Asp.NetAjax内建的异常处理方法

    3,Web.ConfigcustomError节中设置错误跳转页

     

    此篇博客总结的是:在客户端调用Web Service代理时发生异常的处理方法。

       由于Ajax的异步效果,再加上后台的运行,让我们很难判断某次对服务器的请求是否顺利完成,浏览器对Ajax程序运行时发生的异常无能为力;

       Ajax的使用,使Web应用程序从客户端想服务器端发起异步Http协议请求,在理想情况下是没有问题的,但是Web程序的运行并不是一直顺利的,它有着很多不确定性——网络故障、。。。开发者的粗心大意,一旦出现问题就会导致异步请求失败

       在开发中提高性能,尽量避免这些异常,但是有些异常是避免不了的,这样的话,我们就该处理这些异常。

       当然,Asp.Net Ajax也考虑到了这些,并提供了一整套针对异步回调过程中发生的异常处理机制;

       进入主题:在客户端调用Web Service代理时发生异常的处理办法;

     

    客户端调用Web Service代理的语法:

    [NameSpace].[ClassName].[MethodName](param1,param2,...,onSuceeded,onFailed)

    ——其中:onSuceeded:成功时调用的函数;

             onFailed:失败时调用的函数;(本博客介绍的重点之处)

    onFailed回调函数会接收一个类型为Sys.Net.WebServiceError的参数,表示异常对象。如下:

    Function onFailed(error){

         //显示异常信息

        //处理异常信息

    }

     

    注:

    ASP.NET AJAX的客户端Sys.Net.WebServiceError类型封装了异步请求服务器时可能发生的异常,它提供了若干个只读的属性,提供了对异常信息的详细描述。Sys.Net.WebServiceError类型的属性如下(在接下来的例子将会用到这些属性):

    属  性

         描  述

    exceptionType

    获取服务器端异常的具体类型

    message

    获取详细的异常描述信息

    statusCode

    获取造成异常的HTTP响应的状态码

    stackTrace

    获取服务器端异常的栈跟踪信息

    timedOut

    获取一个布尔值,表示异常是否是由于网络连接超时造成的

     

    例子:

    服务器端:ErroHandling.asmx

    定义了两个方法GetDivision(int a,int b):实现两个整数相除,并返回相除结果; int Timeout():

    <%@ WebService Language="C#" Class="ErrorHandling" %>
    
    using System;
    using System.Web;
    using System.Web.Services;
    using System.Web.Services.Protocols;
    using System.Web.Script.Services;
    using System.Threading;
    
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [ScriptService]
    public class ErrorHandling  : System.Web.Services.WebService
    {
    	[WebMethod]
    	public int GetDivision(int a, int b)
    	{           
           
           return a / b;
               
    	}
    
    	[WebMethod]
    	public int Timeout()
    	{
    		Thread.Sleep(5000);
    		return 0;
    	}
    }
    


    (注意:Web Service类要添加[ScriptService]标记;在方法上标记[WebMethod]表示:服务器端释放该方法,客户端可访问服务器端的该方法)

    客户端调用服务器端的GetDivision(int a,int b)函数,并传入两个参数:2,0;程序借助Asp.Net Ajax异步通信层将参数发送至服务器,服务器完成计算后将结果返回至客户端;

    显然,因为输入的被除数为0,服务器端执行时必定要抛出异常;但是

    并没有在服务器端做异常处理,此时客户端也许会显示异常错误,但是如果客户端并没有显示异常错误,那么客户端的用户就要无限期的等下去,可见是多么的不合理;那么如何在客户端处理此类异常?

     

    新建Asp.Net页面5_ClientProxy.aspx;前台添加ScriptManager控件(

    和UpdatePanel控件联合使用可以实现页面异步局部更新的效果),ErrorHandling.asmx的引用:

    <asp:ScriptManager ID="ScriptManager1" runat="server">
    		<Services>
    			<asp:ServiceReference Path="ErrorHandling.asmx" />
    		</Services>
    </asp:ScriptManager>
    

    添加两个按钮,并且分别在单击事件中调用getDivision(int a,int b)timeout()方法:(注:getDivision()函数传入的参数为5,0:即被除数为0)

    <input type="button" value="getDivision" οnclick="getDivision(5, 0)" />
    <input type="button" value="timeout" οnclick="timeout()" />



    <script language="javascript" type="text/javascript">
      ErrorHandling.set_timeout(2000);
    //getDivision(int a,int b)方法:
    function getDivision(a, b)
    {
    	function getDivision(a, b)
    	 {
    	    ErrorHandling.GetDivision(a, b, succeededCallback,failedCallback);
    		//调用服务器端GetDivision(int a,int b)函数,成功时的回调函数为succeededCallback(),失败时的回调函数为failedCallback()
             }
    }
    function timeout()
     {
        ErrorHandling.Timeout(succeededCallback, failedCallback);
     }
    
     function succeededCallback(result)
     {
        alert(result)
     }
    
     function failedCallback(error, userContext, methodName) 
    {
          // ASP.NET AJAX的客户端Sys.Net.WebServiceError类型封装了异步请求服务器时可能发生的异常,它提供了若干个只读的属性,提供了对异常信息的详细描述。Sys.Net.WebServ- iceError类型的属性,如下:
    		        //属  性	              描  述
    		        //exceptionType	 获取服务器端异常的具体类型
    		        //message	            获取详细的异常描述信息
    		        //statusCode	             取造成异常的HTTP响应的状态码
    		        //stackTrace	            获取服务器端异常的栈跟踪信息
    		        //timedOut	             获取一个布尔值,表示异常是否是由于网络连接超时造成的
    
        var message = String.format(
    	"Timeout: {0}\nMessage: {1}\nExceptionType: {2}\nStackTrace: {3}",
    	error.get_timedOut(),
    	error.get_message(),
    	error.get_exceptionType(),
    	error.get_stackTrace());
    
         alert("Error at " + methodName + "\n\n" + message);
     }
    
    </script>
    

    点击"getDivision"按钮:因为被除数为0,有异常,所以调用回调函数failedCallback(),运行结果如下图;假如传入的参数为5,1,则调用succeededCallback()函数:



    点击"timeout"按钮:因为睡眠时间为5秒,超过设定的2秒,所以所以调用回调函数failedCallback(),运行结果如下图;假如设定6秒,则调用succeededCallback()函数:



    总结:

    本篇博客主要讲:在客户端调用Web Service代理时发生异常的处理办法,以及Sys.Net.WebServiceError类型的属性的使用

     

    展开全文
  • 我们知道,Dubbo将服务调用封装成普通的Spring的Bean,于是我们可以像使用本地的Spring Bean一样,来调用远端的Dubbo服务,并有LoadBalance和Failover的功能。现在,我们从源码的角度来看看,Dubbo是如何做到这点的...

    疑惑一:为什么在Spring中我们能像注入普通本地服务JavaBean一样注入远程的Dubbo服务Bean

    我们知道,Dubbo将服务调用封装成普通的Spring的Bean,于是我们可以像使用本地的Spring Bean一样,来调用远端的Dubbo服务,并有LoadBalance和Failover的功能。现在,我们从源码的角度来看看,Dubbo是如何做到这点的。

    我们知道,要成为Dubbo服务的消费者,需要在Spring的xml文件中配置dubbo:reference节点,如下:

    <dubbo:reference id="configRemoteService"
    
    interface="com.manzhizhen.biz.ConfigRemoteService"
    
    version="1.0.0" filter="DataVerifyDubboConsumerFilter,LogDubboConsumerFilter" />

     

    在这里,我们已经发现了点特别,如果是普通的Spring Bean配置,会有个class属性,用来配置该Bean所属的类型,因为Spring最后需要创建一个该类型的对象,但Dubbo提供的自定义标签dubbo:reference却很奇怪,它提供的是interface,接口!怎么能从接口创建对象呢?别急,我们接着看。我们知道,在Spring容器中,由配置到Spring容器中的Bean,要经历配置->BeanDefinition->可用的Bean这几个阶段,Bean的所有创建信息,都放在了BeanDefinition对象中,因为Dubbo提供了自己的dubbo:reference标签,在上一篇文章(http://manzhizhen.iteye.com/admin/blogs/2276242)我们已经提到,Dubbo需要提供自己的BeanDefinition的解析类DubboBeanDefinitionParser,来自己将dubbo:reference元素转换成BeanDefinition对象:

    registerBeanDefinitionParser("reference"new DubboBeanDefinitionParser(ReferenceBean.classfalse)); 

    我们看看 DubboBeanDefinitionParser中是如何将dubbo:reference元素转换成BeanDefinition对象的,下面给出部分源码:

    public classDubboBeanDefinitionParserimplementsBeanDefinitionParser {
    
        private finalClass<?>beanClass;
    
        private final booleanrequired;
    
        publicDubboBeanDefinitionParser(Class<?> beanClass,booleanrequired) {
            this.beanClass= beanClass;
            this.required= required;
        }
    
    
        publicBeanDefinition parse(Element element, ParserContext parserContext) {
            return parse(element, parserContext,beanClass,required);
        }
    
    
        @SuppressWarnings("unchecked")
    
        private staticBeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass,booleanrequired) {
    
            RootBeanDefinition beanDefinition =new RootBeanDefinition();
    
            beanDefinition.setBeanClass(beanClass);
            beanDefinition.setLazyInit(false);
    
            String id = element.getAttribute("id");
    
       /**下面略*/

    我们直接看parse操作,beanDefinition.setBeanClass(beanClass);直接将传入的beanClass作为BeanDefinition要创建的Spring Bean的类型,于是乎,我们知道了,所有的Dubbo消费者Bean都是ReferenceBean类型的对象,interface属性中配置的接口只是让ReferenceBean对象知道Dubbo的服务提供方提供的方法签名而已。但问题立马来了,既然是ReferenceBean类型的对象,但我们确实使用的不应该是interface配置的类型的实例吗?,比如我们都是这样使用这个Bean的:

    @Autowired
    privateConfigRemoteService configRemoteService;

    它似乎就是ConfigRemoteService类型,而不是ReferenceBean类型,于是我们想到了,Dubbo肯定在某个地方创建了一个实现了该interface(比如上面的com.manzhizhen.biz.ConfigRemoteService)类型的对象,一般来说,Dubbo的消费者模块中只有服务提供者模块暴露的API,所以,需要能在Java体系中的动态创建对象,你一定立马想到了动态代理

    ReferenceConfig是ReferenceBean的父类,它存储了一些配置信息,我们看看ReferenceConfig中几个比较重要的成员变量:

    private static finalProxyFactoryproxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
    
    //接口类型
    privateString              interfaceName;
    
    privateClass<?>            interfaceClass;
    
    //接口代理类引用
    private transient volatileTref;
    
    private transient volatileInvoker<?>invoker;

    这里我们直接看到了代理工厂proxyFactory还有表示interface配置的类型的interfaceNameinterfaceClassref正是用来存储代理工厂proxyFactory创建出来的代理类!不信我们可以先看看ReferenceConfig中的几个方法:

    public synchronizedTget() {
    
        if(destroyed){
            throw new IllegalStateException("Already destroyed!");
        }
    
       if(ref==null) {
          init();
       }
    
       returnref;
    }
    
    private voidinit() {
    
    if(initialized) {
             return;
    }
    
    /** 此处有省略*/
         if(ProtocolUtils.isGeneric(getGeneric())) {
             interfaceClass = GenericService.class;
    
         } else {
             try {
    
                  interfaceClass= Class.forName(interfaceName,true, Thread.currentThread()
               .getContextClassLoader());
    
             } catch (ClassNotFoundException e) {
                  throw newIllegalStateException(e.getMessage(), e);
             }
    
             checkInterfaceAndMethods(interfaceClass, methods);
    
         }
    
    /** 此处有大量省略*/
    
        //attributes通过系统context进行存储.
       StaticContext.getSystemContext().putAll(attributes);
    
        ref= createProxy(map);
    
    }
    
    privateTcreateProxy(Map<String, String> map) {
    
           /**此处有大量省略*/
    
           Boolean c =check;
           if (c == null&&consumer!= null) {
               c =consumer.isCheck();
           }
    
           if (c == null) {
               c =true;// default true
          }
    
           if (c && ! invoker.isAvailable()) {
               throw new IllegalStateException("Failed to check the status of the service "+interfaceName+ ". No provider available for the service "+ (group==null ?"":group+"/") +interfaceName+ (version==null?"":":"+version) +" from the url "+invoker.getUrl() +" to the consumer "+ NetUtils.getLocalHost() +" use dubbo version "+ Version.getVersion());
    
           }
    
           if (logger.isInfoEnabled()) {
               logger.info("Refer dubbo service "+interfaceClass.getName() +" from url "+ invoker.getUrl());
           }
    
          //创建服务代理
          return(T)proxyFactory.getProxy(invoker);
       }

    这里先简单介绍下Dubbo中的Invoker和Invocation两个接口,Invoker定义了Dubbo服务调用者的基本操作行为,而Invocation描述调用过程中的方法信息,可理解成调用的上下文信息,我们直接看看这两个接口的定义:

    public interface Invoker<T> extends Node {
    
        /**
         * get service interface.
         *
         *@returnservice interface.
         */
       Class<T> getInterface();
    
        /**
         * invoke.
         *
         *@paraminvocation
        *@returnresult
         *@throwsRpcException
         */
       Result invoke(Invocation invocation)throwsRpcException;
    }
    
    public interface Invocation {
    
           /**为了缩短篇幅,常规的注释这里省略了*/
    
      String getMethodName();
    
      Class<?>[] getParameterTypes();
    
      Object[] getArguments();
    
      Map<String, String> getAttachments();
    
      String getAttachment(String key);
    
      String getAttachment(String key, String defaultValue);
    
      Invoker<?> getInvoker();
    }

    可以看到,这两个接口的定义有点类似于反射API中的相关定义,没错,调用就是调用,简单可扩展即可。我们知道可以在Dubbo中通过SPI的方式定义些拦截器(Filter),而拦截器中就可以使用Invoker#invoke来进行递归调用了。

    可见ReferenceConfig#get方法直接将代理工厂创建的代理对象返回,而该方法最终会被ReferenceConfig的子类ReferenceBean的getObject方法调用:

    public Object getObject() throws Exception {
        return get();
    }

    我们知道,在Spring容器中,会利用BeanDefinition对象信息来初始化创建之后的Bean对象,但如果你想插手“创建对象”这一步,有两种方法:1.使用工厂方法模式来指定某个工厂方法创建Bean。2. Bean的类实现FactoryBean接口,没错,Dubbo中就是用第二种方法来插手Bean对象创建这一步,所以显然ReferenceConfig实现了FactoryBean接口,而getObject()正是FactoryBean接口中定义的,这也就是为什么我们可以直接用interface属性中配置的类型来直接使用该Dubbo消费者对象的原因了。这样就解释了我们上面抛出的问题。

    那么,Dubbo也像Spring容器中那样默认对接口实现使用JDK代理,而对非接口实现使用CGLib方式来代理吗?我们上面又看到,代理对象是以Invoker对象为基础创建的(return(T)proxyFactory.getProxy(invoker);),在Dubbo中,定义了一个代理工厂接口ProxyFactory:

    @SPI("javassist")
    public interface ProxyFactory {
    
       @Adaptive({Constants.PROXY_KEY})
        <T> T getProxy(Invoker<T> invoker) throws RpcException;
    
       @Adaptive({Constants.PROXY_KEY})
        <T> Invoker<T> getInvoker(Tproxy, Class<T> type, URL url) throws RpcException;
    
    }

    抽象类AbstractProxyFactory实现了它的getProxy方法:

    public abstract class AbstractProxyFactory implements ProxyFactory {
    
        public<T>TgetProxy(Invoker<T> invoker)throwsRpcException {
    
            Class<?>[] interfaces =null;
    
            String config = invoker.getUrl().getParameter("interfaces");
    
            if (config != null&& config.length() >0) {
    
                String[] types = Constants.COMMA_SPLIT_PATTERN.split(config);
    
                if (types != null&& types.length>0) {
    
                    interfaces =newClass<?>[types.length+2];
    
                    interfaces[0] = invoker.getInterface();
    
                    interfaces[1] = EchoService.class;
    
                   for(inti =0; i < types.length; i ++) {
    
                        interfaces[i +1] = ReflectUtils.forName(types[i]);
    
                    }
                }
            }
    
            if (interfaces == null) {
                interfaces =newClass<?>[] {invoker.getInterface(), EchoService.class};
            }
    
            return getProxy(invoker, interfaces);
    
        }
    
        public abstract<T>TgetProxy(Invoker<T> invoker, Class<?>[] types);
    }

    可以看出,AbstractProxyFactory#getProxy的通用实现,其实主要是将该代理需要实现的接口给确定好,该接口存储在invoker对象的url的interfaces参数中,是一个字符串,用逗号分隔,我们不需要关心interfaces中存储的到底是哪些接口,只需要知道的是,invoker.getInterface()会加入到其中,而另一个重载的getProxy方法显然是丢给子类来实现了。AbstractProxyFactory有两个实现类:JdkProxyFactory和JavassistProxyFactory,顾名思义,一个是使用JDK的动态代理,一个是使用Javaassist来实现动态代理,我们直接看JdkProxyFactory的实现:

    public class JdkProxyFactory extends AbstractProxyFactory {
    
        @SuppressWarnings("unchecked")
        public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
            return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), interfaces, new InvokerInvocationHandler(invoker));
        }
    
        public<T> Invoker<T> getInvoker(Tproxy, Class<T> type, URL url) {
    
            return new AbstractProxyInvoker<T>(proxy, type, url) {
    
                @Override
               protectedObject doInvoke(Tproxy, String methodName,
                                          Class<?>[] parameterTypes,
                                          Object[] arguments)throwsThrowable {
    
                    Method method = proxy.getClass().getMethod(methodName, parameterTypes);
                   return method.invoke(proxy, arguments);
    
                }
            };
        }
    }

    在JdkProxyFactory的getProxy方法中我们看到了熟悉的类:InvokerInvocationHandler,很显然它实现了JDK中的InvocationHandler接口:

    public class InvokerInvocationHandler implements InvocationHandler {
    
        private finalInvoker<?>invoker;
    
        publicInvokerInvocationHandler(Invoker<?> handler){
            this.invoker= handler;
        }
    
    
    
        publicObject invoke(Object proxy, Method method, Object[] args)throwsThrowable {
    
            String methodName = method.getName();
    
            Class<?>[] parameterTypes = method.getParameterTypes();
    
            if (method.getDeclaringClass() == Object.class) {
                return method.invoke(invoker, args);
            }
    
            if ("toString".equals(methodName) && parameterTypes.length==0) {
                return invoker.toString();
            }
    
            if ("hashCode".equals(methodName) && parameterTypes.length==0) {
                return invoker.hashCode();
            }
    
            if ("equals".equals(methodName) && parameterTypes.length==1) {
                return invoker.equals(args[0]);
            }
    
            return invoker.invoke(newRpcInvocation(method, args)).recreate();
        }
    }

    也像我们预期那样,invoke操作是由invoker对象来完成的,这里的recreate是Result中的方法,只是对结果进行“再处理”,如果没异常就返回,有异常则再次抛出。

    所以,看到这我们就明白了,在Dubbo中,没有使用CGLib进行代理,而是使用JDK和Javassist来进行动态代理!我们知道,动态代理是无法用反射做的,只能靠动态生成字节码,这就需要使用字节码工具包,比如asm和Javassist等,在Spring3.2.2之前版本的源码中,我们可以看到是有单独spring-asm的模块的,但在Spring3.2.2版本开始,就没有spring-asm模块了,不是不使用了,而是spring-asm已经整合到spring-core中了,可见asm在Spring中的地位(CGLib使用的就是asm),至于Dubbo为什么不使用CGLib,当我们选择动态代理实现时,无非考虑的是如下因素:

    1. 使用的难易程度。
    2. 功能,比如JDK的代理只能通过接口实现,而CGLib则没这个限制。
    3. 生成的字节码的效率,包括创建的效率和运行的效率。
    4. 生成的字节码大小,为什么说这也比较重要?这和生成的Class和其实例的大小相关。

    至于具体的原因,可以参考Dubbo作者的博客:http://javatar.iteye.com/blog/814426

    那么,接下来的问题就是,什么时候会使用JDK的动态代理,而什么时候使用Javassist的动态代理呢?在dubbo-rpc-api的模块中,有个com.alibaba.dubbo.rpc.ProxyFactory文件,里面定义了代理的几种方式:

    stub=com.alibaba.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper

    jdk=com.alibaba.dubbo.rpc.proxy.jdk.JdkProxyFactory

    javassist=com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory

    StubProxyFactoryWrapper是存根(stub)的代理工厂,主要用于暴露服务提供者的本地服务给远端消费者来调用。可以直接通过xml的配置来手动选择代理,比如:

    <dubbo:provider proxy="jdk" />或<dubbo:consumer proxy="jdk" />

    如果不配置,由于ProxyFactory接口上有@SPI("javassist")注解,所以默认是使用Javassist来实现动态代理!

    具体细节可以深入源码去了解,这里不多做介绍。

    疑惑二:当有多个服务提供者存在时,Dubbo是怎么做负载均衡的?

    我们想知道消费者选择提供者时,是怎样选择远端的服务者的?轮询还是权重?

    我们先探讨负载均衡LoadBalance,在Dubbo的源码中,它是在dubbo-cluster模块中来实现的,我们看看dubbo-cluster模块中com.alibaba.dubbo.rpc.cluster.LoadBalance文件中的内容:

    random=com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance

    roundrobin=com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance

    leastactive=com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance

    consistenthash=com.alibaba.dubbo.rpc.cluster.loadbalance.ConsistentHashLoadBalance

    这下我们一看就知道Dubbo中有四种LB的方式:随机、轮询、最少活跃和一致哈希。在这之前,我们先来看看负载均衡的接口LoadBalance:

    @SPI(RandomLoadBalance.NAME)
    public interface LoadBalance {
    
       /**
        * select one invoker in list.
        *
        *@paraminvokersinvokers.
        *@paramurlrefer url
        *@paraminvocationinvocation.
        *@returnselected invoker.
        */
       @Adaptive("loadbalance")
    
       <T> Invoker <T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
    
    }

    接口LoadBalance的定义说明,LoadBalance的实现只是在一个服务提供的调用者列表(invokers)中选出一个调用者即可,默认的负载方式是随机负载均衡(@SPI(RandomLoadBalance.NAME)),我们也可以指定使用哪种负载均衡:

    <dubbo:reference interface="xxx" loadbalance="roundrobin"/> 或 <dubbo:service interface="xxx" loadbalance="roundrobin" />

    肯定有人会问,普通的随机负载均衡和轮询的负载均衡方式效果不是一样的吗?确实,如果是普通的随机,理论上来说当流量积累的足够多时随机和轮询的方式效果相当,如果采取的随机数的随机程度随机数生成开销不当,随机效果可能会低于轮询(注意,在Dubbo中随机负载均衡性能远高于轮询,所以Dubbo默认采用的是随机LB),但如果在随机过程中加入权重这一属性的话,随机的优势不言而喻了,比如可以做到“预热”功能,给刚上的服务器在某段时间内分配比其他服务器更少的请求,让刚上的服务器能先“热热身”,这一点,普通轮询方式是很难做到的。没错,Dubbo默认的随机负载方式就加入了权重这一因数,权重精确到某个接口的某个方法,这也是我们所期望的,其默认“预热”时间是10分钟。随机LB还有一个好处是轮询很难做到的,就是当某个实例故障时,如果用户配置了重试,那么轮询LB会将所有重试流量打到下一个轮询的实例,可能会把它打挂,但随机LB不一样,如果某个实例故障,重试时随机LB会从剩下的可用实例中随机选一个调用,更均匀。

    Dubbo中有个抽象类AbstractLoadBalance,它实现了获取某个接口某个方法的权重值,预热部分的计算也包含在其中,这里代码就不贴出来了,之所以Dubbo这样做,是因为其他负载形式都用到权重因子。

    我们这里简单描述下各种负载均衡的实现方式:

    随机负载均衡(RandomLoadBalance:先统计所有服务器上该接口方法的权重总和,然后对这个总和随机nextInt一下,看生成的随机数落到哪个段内,就调哪个服务器上的该服务。

    轮询负载均衡(RoundRobinLoadBalance):如果所有服务器的该接口方法的权重一样,则直接内部的序列计数器(sequences)+1然后对服务器的数量进行取模来决定调用哪个服务器上的服务;如果服务器的该接口方法的权重不一样(就是说存在预热中的服务器),则找到其中最大的权重,然后将内部的权重计数器(weightSequences)+1并对该最大权重数取模,然后再找出权重比该取模后的值大服务器列表,最后通过内部的序列计数器(sequences)+1然后对服务器列表数量进行取模来决定调用哪个服务器上的服务。

    最少活跃负载均衡(LeastActiveLoadBalance:每个接口和接口方法都对应一个RpcStatus对象,记录了他们的活跃数、失败数等等相关统计信息,此种负载均衡方式是在活跃数最低的服务器中对其权重的总和取模来看结果是在哪个权重段中,则选择该服务器来调用,活跃数就像并发量降级中的计数器一样,开始调用时活跃数+1,调用结束时活跃数-1,所以活跃值越大,表明该提供者服务器的该接口方法耗时越长,而消费能力强的提供者接口往往活跃值很低。最少活跃负载均衡保证了“慢”提供者能接收到更少的服务器调用。

    一致哈希负载均衡(ConsistentHashLoadBalance):一致性哈希算法的负载均衡保证了同样的请求(参数)将会落到同一台服务器上,这在某些场景是非常有用的,Dubbo中默认采用了160个虚拟节点,因为Dubbo的请求URL中除了我们使用的参数,还有些额外的系统调用参数,比如timestamp、loadbalance、pid和application等,有人可定会问,Dubbo会对URL中哪些参数进行hash,Dubbo默认除了对我们接口所有参数进行hash外,还会加上这些额外参数,因为有timestamp,这是不是也意味着在Dubbo的重试调用时timestamp不变?

    上述的四种负载均衡,除了一致性哈希,其他三种都依赖了接口方法的权重统计,借助权重的不同,随机负载均衡就能做到动态调整的效果,Dubbo中的轮询负载方式,也利用了权重,那么有人会问,Dubbo中的随机和轮询是不是差别不大?是的,笔者也这样认为,相似的效果,也有着相似的缺点,Dubbo中的随机和轮询负载都没有考虑到提供者服务器消费服务的能力,如果相差很大,“慢”提供者有可能被“快”提供给者给拖垮,其根本原因也是这两种负载均衡的加权因子考虑的不是服务耗时。最少活跃的负载均衡就很巧妙的解决了此问题,而且它不是直接通过统计服务调用的耗时,而是采用统计调用差(活跃数)。一致性哈希特别适用于有缓存的系统,这样缓存命中率会比较高。

    一句话总结本文,虽然动态代理和负载均衡没有直接的关系,但因为看源码看到这里了,所以写篇文章“纪念”一下,带着问题看源码往往就是这种效果。可以看出,Dubbo的设计者,在很多细节上都下足了功夫,从动态代理的选型和负载的多种实现方式就可以看出来,好的软件框架,不仅需要有好的架构设计,技术细节实现起来也不能马虎。本来想写更多,但有人和我说过,技术博文不要太长… …

    展开全文
  • 调用时报错:java.lang.NoSuchMethodException调用查看如下:下面是消费端报错时的断点截图:这下面是正常demo的断电截图:最后断点调试发现spring自动代理了dubbo 导致了这个问题, 因为公司框架无法关闭AOP自动代理( ...

    调用时报错:java.lang.NoSuchMethodException

    调用查看如下:

    下面是消费端报错时的断点截图:


    这下面是正常demo的断电截图:



    最后断点调试发现spring自动代理了dubbo 导致了这个问题, 因为公司框架无法关闭AOP自动代理(

      aop:aspectj-autoproxy

    自动代理扫描指定包下面的类),最后 通过将 接口类 变更包名/结构 

    之前的service接口包名为: com.xxx.xxx.service下面, spring自动代理配置会扫描这个包下面所有的类,

    修改service接口包名为:com.xxx.xxx.dubboservice,解决问题, 因为spring不会扫描这个包下面的;

    最后成功解决这个问题, 困扰了我三天, 找公司的"技术大牛"也没能搞定, 还是要自己多琢磨啊, 泪~~~~

    展开全文
  • 更多干货spring-boot系列一 之restfull api与多环境配置springboot系列二之 日志SpringBoot系列三之 MVC 模版引擎SpringBoot 2.0.0.M7 系列四 异常处理springboot 2.0.0.M7之 注解 与 配置springboot 2.0.0.M7 配置...

    更多干货


    调用REST服务

    • 调用REST服务
    • 使用代理调用rest服务

    添加依赖

    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
    </dependency>
    @Autowired
        private RestTemplateBuilder restTemplateBuilder;
    
        /**
         * get请求
         */
        @Test
        public void getForObject() {
    
            ViewerResult bean = restTemplateBuilder.build().getForObject("http://localhost:8081/api/blog/category/findAll", ViewerResult.class);
            System.out.println(bean);
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("id", 2);
            ViewerResult bean = restTemplateBuilder.build().postForObject("http://localhost:8081/api/blog/category/findAll", map, ViewerResult.class);
            System.out.println(bean);
    
            String result = restTemplateBuilder.additionalCustomizers(new ProxyCustomizer()).build().getForObject("http://111.231.142.109", String.class);
            System.out.println(result);
    
        }
    
        static class ProxyCustomizer implements RestTemplateCustomizer {
            @Override
            public void customize(RestTemplate restTemplate) {
                String proxyHost = "43.255.104.179";
                int proxyPort = 8080;
    
                HttpHost proxy = new HttpHost(proxyHost, proxyPort);
                HttpClient httpClient = HttpClientBuilder.create().setRoutePlanner(new DefaultProxyRoutePlanner(proxy) {
                    @Override
                    public HttpHost determineProxy(HttpHost target, HttpRequest request, HttpContext context)
                            throws HttpException {
                        System.out.println(target.getHostName());
                        return super.determineProxy(target, request, context);
                    }
                }).build();
                HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(
                        httpClient);
                httpComponentsClientHttpRequestFactory.setConnectTimeout(10000);
                httpComponentsClientHttpRequestFactory.setReadTimeout(60000);
                restTemplate.setRequestFactory(httpComponentsClientHttpRequestFactory);
            }
        }

    在线代理:

    展开全文
  • 玩过 springcloud 的微服务架构的小伙伴们都知道,在我们通过 eureka 远程调用其他的微服务的时候,防止网络抖动或者程序异常等情况,往往会加上 hystrix 断路器对异常进行处理,但是往往在处理的时候一般都是记录...
  • 深入浅出 gRPC 04:gRPC 服务调用原理

    千次阅读 2020-02-03 19:34:59
    1. 常用的服务调用方式 1.1 同步服务调用 1.2 并行服务调用 1.3 异步服务调用 2. 服务调用的一些误区和典型问题 2.1 理解误区 2.1.1 I/O 异步服务就是异步 2.1.2 服务调用天生就是同步的 2.1.3 异步服务调用...
  • 【远程调用框架】如何实现一个简单的RPC框架(三)优化一:利用动态代理改变用户服务调用方式 【远程调用框架】如何实现一个简单的RPC框架(四)优化二:改变底层通信框架这篇博客,在(一)(二)的基础上,对第一...
  •  一个应用大多数都要调用其它服务接口获取相关资源,而第三方服务会有很多错误码,有相关系统异常,用户操作异常,授权异常等等。其实对于自己的应用而讲,可以两种:系统异常和用户非法操作异常,对应的http状态为...
  • Scrapy 调用代理动态转发BUG 解决

    千次阅读 2018-09-07 16:59:04
    问题:最近找一些代理来使用 , 用到了讯代理的动态转发,使用requests能正常使用,但是使用Scrapy框架调用的时候,怎么都调用不了。异常是没有特定的头部信息,但是请求是 解决:先使用抓包工具,查看程序发出去的...
  • 接上一篇,本人是java,但是为了项目 研究了一段时间的nodejs和puppeteer,就是用http服务提供爬虫服务,这个爬虫服务调用的是puppeteer,有验证码的可以调用验证码服务然后继续执行! 话不多说 直接上代码更加清晰(无数...
  • 在controller中调用Service时, 报空指针异常,后来发现是因为当前方法用private来修饰的, 更改为public后完美解决 查了一下, 可能是 容器扫描bean生成代理类的时候,public和protected方法可以被正常代理,而private...
  • 当我们在调用某个服务提供者暴露出来的接口的时候,实际上调用的是dubbo框架帮我们生成的代理类,例如我们现在有个服务提供者暴露的接口 public interface IHelloProviderService { String getName(Integer id); } ...
  • gateway自己服务的全局异常处理,参考这篇https://segmentfault.com/a/1190000016854364?utm_source=tag-newest 有两个类 /** * @author wuweifeng wrote on 2018/11/2. */ @Configuration public class ...
  • java调用webservice服务

    万次阅读 2020-07-07 23:50:11
    /** * 客户端调用webservice服务的示例代码 * @author ourlang */ public class ClientServiceUtil { /** * 动态调用第三方webservice通用方法 * * @param wsdlUrl 请求的wsdl的路径 * @param operationMethod 调用...
  • 最近项目上出现了JDK动态代理UndeclaredThrowableException异常,此异常之前没有接触过,那么该异常将会导致什么呢? UndeclaredThrowableException后果:导致该抛出的直接异常信息被包装了好几层,异常看起来很...
  • dubbo源码浅析-远程服务调用流程

    千次阅读 2018-05-29 17:22:49
    转载自:dubbo源码浅析(五)-远程服务调用流程非商业转载,如造成侵权,请联系本人删除消费端调用远程服务接口时,使用上和调用普通的java接口是没有任何区别,但是服务消费者和提供者是跨JVM和主机的,客户端如何...
  • javascript调用WCF服务

    千次阅读 2011-01-13 17:04:00
    在 ASP.NET AJAX 框架中,如果需要用 javascript 调用 WebService 或者调用 PageMethod 都是很容易实现的,主要是通过ScriptManager服务器控件协调脚本资源,生成客户端代理,实现对服务器端的访问,当然不...
  • 使用动态代理处理异常

    千次阅读 2017-03-15 19:46:20
    catch,所以想到了说可以用动态代理,通过切面代码来写,这样可以不写try-catch,为此总结了如下的知识点: 大概是说把异常处理放在切面里面统一进行 不直接写在业务代码里面 就像下面这样 原来是这样的 ...
  • RPC服务远程调用

    万次阅读 2018-09-03 22:43:42
    它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。即程序员无论是调用本地的还是远程的,本质上编写的调用代码基本相同。 RPC 起源 RPC 这个...
  • 但是第一种写法最正确的 因为FeignClient是在我们本地直接调用的,根本不需要这个注解,Controller调用方法的时候就是直接将对象传给FeignClient,而FeignClient通过http调用服务时则需要将对象转换成json传递。...
  • splice系统调用实现的TCP代理

    千次阅读 2013-12-01 22:46:51
    下面是代码,没有异常处理,没有错误判断,直奔主题(splice代理即无极变速代理): #define _GNU_SOURCE #include #include #include #include #include #include int main(int argc,char *argv[]) { int fd, ...
  • dubbo 远程服务调用流程

    千次阅读 2017-07-03 17:35:53
    在消费者初始化的时候,会生成一个消费者代理注册到容器中,该代理回调中持有一个MockClusterInvoker实例,消费调用服务接口时它的invoke会被调用,此时会构建一个RpcInvocation对象,把服务接口的method对象和参数...
  • 关于JDK动态代理方法的调用

    千次阅读 2018-01-18 21:27:23
    这篇文章关于JDK动态代理的演示让我有点迷惑,又重新看了下动态代理 1.首先JDK动态代理是针对接口的, 用提供的被代理对象获得该对象所有实现了的接口,重新生成的一个类 针对这个,像spring注解事务应该放在接口...
  • dubbo 服务消费方调用过程

    千次阅读 2019-02-08 17:44:13
    消费端调用 1、在业务层代码中调用远程接口的方法时,实际上是...即在《4.4.3 创建服务类的本地代理》中创建的代理对象。 2、在该代理对象中,调用了InvokerInvocationHander.invoke方法。远程调用以Invcati...
  • ssm如何调用其他服务器地址接口

    千次阅读 2017-11-16 17:45:39
    //设置代理服务器 //httpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, new HttpHost("10.0.0.4", 8080)); httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT,...
  • 异步服务调用的工作原理

    千次阅读 2018-02-08 12:15:23
    1、消费者调用服务端发布的接口,接口调用由分布式服务框架包装成动态代理,发起远程服务调用; 2、通信框架异步发送请求消息,如果没有发生I/O异常,返回; 3、请求消息发送成功后,I/O线程构造Future对象,设置...
  • dubbo消费方和服务调用流程

    千次阅读 2019-09-06 10:31:59
    即在《4.4.3 创建服务类的本地代理》中创建的代理对象。 2、在该代理对象中,调用了InvokerInvocationHander.invoke方法。远程调用以Invcation、Result为中心,在这个方法中根据调用的远程方法和传入的参数构建...
  • 解决springboot服务间Feign调用超时问题概述 起因 在完成项目功能需求的开发,经过自己测试以及通过测试组测试通过后,昨晚正式部署到线上环境进行正式运行前的最后一次的测试。但是在测试中,由A服务调用B服务接口...
  • Java远程服务调用协议对比

    千次阅读 2017-08-18 09:16:18
    Java中比较流行的远程服务调用协议有:RMI,Hessian,Burlap,Httpinvoker,web service等5种通讯协议,当然RPC也是一种,不过和RMI有些类似,不同之处在于rmi只是适用于java语言,rpc跨语言的,另外就是在于方法是...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 187,162
精华内容 74,864
关键字:

代理调用服务异常