zoukankan      html  css  js  c++  java
  • motan源码分析四:客户端调用服务

    在第一章中,我们分析了服务的发布与注册,本章中将简单的分析一下客户端调用服务的代码及流程,本文将以spring加载的方式进行分析。

    1.在DemoRpcClient类的main()方法中加载类:

            ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"classpath:motan_demo_client.xml"});
    
            MotanDemoService service = (MotanDemoService) ctx.getBean("motanDemoReferer");
    

    2.上面加载了spring的配置文件motan_demo_client.xml

        <motan:registry regProtocol="zookeeper" name="registry" address="127.0.0.1:2181" connectTimeout="2000"/>
    
        <!-- motan协议配置 -->
        <motan:protocol default="true" name="motan" haStrategy="failover"
                        loadbalance="roundrobin" maxClientConnection="10" minClientConnection="2"/>
    
        <!-- 通用referer基础配置 -->
        <motan:basicReferer requestTimeout="200" accessLog="false"
                            retries="2" group="motan-demo-rpc" module="motan-demo-rpc"
                            application="myMotanDemo" protocol="motan" registry="registry"
                            id="motantestClientBasicConfig" throwException="false" check="true"/>
    
        <!-- 具体referer配置。使用方通过beanid使用服务接口类 -->
        <motan:referer id="motanDemoReferer"
                       interface="com.weibo.motan.demo.service.MotanDemoService"
                       connectTimeout="300" requestTimeout="300" basicReferer="motantestClientBasicConfig"/>

    经过spring装载RefererConfig后,每次向spring框架getBean时会调用RefererConfig的getRef()方法

    3.获取接口MotanDemoService的实现类代码如下:

        public Object getRef()
        {
            if(ref == null)
                initRef();//初始化
            return ref;
        }
    
        public synchronized void initRef()
        {
            if(initialized.get())
                return;
            try
            {
                interfaceClass = Class.forName(interfaceClass.getName(), true, Thread.currentThread().getContextClassLoader());
            }
            catch(ClassNotFoundException e)
            {
                throw new MotanFrameworkException((new StringBuilder("ReferereConfig initRef Error: Class not found ")).append(interfaceClass.getName()).toString(), e, MotanErrorMsgConstant.FRAMEWORK_INIT_ERROR);
            }
            if(CollectionUtil.isEmpty(protocols))//protocol配置是否为空
                throw new MotanFrameworkException(String.format("%s RefererConfig is malformed, for protocol not set correctly!", new Object[] {
                    interfaceClass.getName()
                }));
            checkInterfaceAndMethods(interfaceClass, methods);
            clusterSupports = new ArrayList(protocols.size());//初始化集群支持类列表,可以支持多个
            List clusters = new ArrayList(protocols.size());//初始化集群类列表,可以支持多个
            String proxy = null;
            ConfigHandler configHandler = (ConfigHandler)ExtensionLoader.getExtensionLoader(com/weibo/api/motan/config/handler/ConfigHandler).getExtension("default");//加载SimpleConfigHandler
            List registryUrls = loadRegistryUrls();//加载注册中心url列表,可以支持多个注册中心
            String localIp = getLocalHostAddress(registryUrls);
            for(Iterator iterator = protocols.iterator(); iterator.hasNext();)
            {
                ProtocolConfig protocol = (ProtocolConfig)iterator.next();
                LoggerUtil.info((new StringBuilder("ProtocolConfig's")).append(protocol.getName()).toString());
                Map params = new HashMap();
                params.put(URLParamType.nodeType.getName(), "referer");
                params.put(URLParamType.version.getName(), URLParamType.version.getValue());
                params.put(URLParamType.refreshTimestamp.getName(), String.valueOf(System.currentTimeMillis()));
                collectConfigParams(params, new AbstractConfig[] {
                    protocol, basicReferer, extConfig, this
                });
                collectMethodConfigParams(params, getMethods());
                URL refUrl = new URL(protocol.getName(), localIp, 0, interfaceClass.getName(), params);
                ClusterSupport clusterSupport = createClusterSupport(refUrl, configHandler, registryUrls);
                clusterSupports.add(clusterSupport);
                clusters.add(clusterSupport.getCluster());
                proxy = proxy != null ? proxy : refUrl.getParameter(URLParamType.proxy.getName(), URLParamType.proxy.getValue());
            }
    
            ref = configHandler.refer(interfaceClass, clusters, proxy);//调用SimpleConfigHandler的refer方法获取接口实现类
            initialized.set(true);
        }

    4.下面我们来看一下SimpleConfigHandler的refer方法的代理实现,使用了jdk的动态代理技术

        public <T> T refer(Class<T> interfaceClass, List<Cluster<T>> clusters, String proxyType) {
            ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension(proxyType);//创建代理工厂
            return proxyFactory.getProxy(interfaceClass, new RefererInvocationHandler<T>(interfaceClass, clusters));//获取代理类
        }
    
    public class JdkProxyFactory implements ProxyFactory {
    
        @SuppressWarnings("unchecked")
        public <T> T getProxy(Class<T> clz, InvocationHandler invocationHandler) {
            return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[] {clz}, invocationHandler);//使用jdk的动态代理,实际调用的代码是下面的的invoke方法
        }
    
    }
    
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            DefaultRequest request = new DefaultRequest();//封装通信用的request,request和response是在客户端和服务端通信的两个对象
    
            request.setRequestId(RequestIdGenerator.getRequestId());
            request.setArguments(args);
            request.setMethodName(method.getName());
            request.setParamtersDesc(ReflectUtil.getMethodParamDesc(method));
            request.setInterfaceName(clz.getName());
            request.setAttachment(URLParamType.requestIdFromClient.getName(), String.valueOf(RequestIdGenerator.getRequestIdFromClient()));
    
            // 当 referer配置多个protocol的时候,比如A,B,C,
            // 那么正常情况下只会使用A,如果A被开关降级,那么就会使用B,B也被降级,那么会使用C
            for (Cluster<T> cluster : clusters) {//motan支持多个protocol的配置,也就是支持多个cluster,但是默认情况下只取第一个,如果前面的被降级,则取下一个
                String protocolSwitcher = MotanConstants.PROTOCOL_SWITCHER_PREFIX + cluster.getUrl().getProtocol();
    
                Switcher switcher = switcherService.getSwitcher(protocolSwitcher);
    
                if (switcher != null && !switcher.isOn()) {
                    continue;
                }
                List<Referer<T>> referL = cluster.getReferers();//此段代码为我单独添加的,目的是证明客户端在配置多个注册中心的情况下,cluster可以支持跨注册中心的调用
                for(Referer<T> refer : referL){
                    LoggerUtil.info(refer.getServiceUrl().getUri()+refer.getServiceUrl().getPath());
                }
                request.setAttachment(URLParamType.version.getName(), cluster.getUrl().getVersion());
                request.setAttachment(URLParamType.clientGroup.getName(), cluster.getUrl().getGroup());
                // 带上client的application和module
                request.setAttachment(URLParamType.application.getName(), ApplicationInfo.getApplication(cluster.getUrl()).getApplication());
                request.setAttachment(URLParamType.module.getName(), ApplicationInfo.getApplication(cluster.getUrl()).getModule());
                Response response = null;
                boolean throwException =
                        Boolean.parseBoolean(cluster.getUrl().getParameter(URLParamType.throwException.getName(),
                                URLParamType.throwException.getValue()));
                try {
                    response = cluster.call(request);//调用cluster的call方法
                    return response.getValue();//获取返回信息
                } catch (RuntimeException e) {
                    if (ExceptionUtil.isBizException(e)) {
                        Throwable t = e.getCause();
                        // 只抛出Exception,防止抛出远程的Error
                        if (t != null && t instanceof Exception) {
                            throw t;
                        } else {
                            String msg =
                                    t == null ? "biz exception cause is null" : ("biz exception cause is throwable error:" + t.getClass()
                                            + ", errmsg:" + t.getMessage());
                            throw new MotanServiceException(msg, MotanErrorMsgConstant.SERVICE_DEFAULT_ERROR);
                        }
                    } else if (!throwException) {
                        LoggerUtil.warn("RefererInvocationHandler invoke false, so return default value: uri=" + cluster.getUrl().getUri()
                                + " " + MotanFrameworkUtil.toString(request), e);
                        return getDefaultReturnValue(method.getReturnType());
                    } else {
                        LoggerUtil.error(
                                "RefererInvocationHandler invoke Error: uri=" + cluster.getUrl().getUri() + " "
                                        + MotanFrameworkUtil.toString(request), e);
                        throw e;
                    }
                }
            }
    
            throw new MotanServiceException("Referer call Error: cluster not exist, interface=" + clz.getName() + " "
                    + MotanFrameworkUtil.toString(request), MotanErrorMsgConstant.SERVICE_UNFOUND);
    
        }
    

    本章知识点总结:

    1.客户端在获取业务接口的实现类时,使用了jdk的动态代理技术;

    2.客户端可以支持多个注册中心;

    3.客户端可以支持多个cluster,但是只取最前面有效那个;

    4.使用request和response对象进行信息的传递。

  • 相关阅读:
    python线程与进程手记
    3.08课·········switch case及if else嵌套(日期格式)
    3.07课·········if分支语句
    3.06课·········C#语言基础
    3.05课·········进制转换
    Oracle profile 使用技巧
    sys用户密码丢失找回密码的步骤和命令
    oracle帐号scott被锁定如何解锁
    SQL中哪些情况会引起全表扫描
    Oracle创建用户、角色、授权、建表
  • 原文地址:https://www.cnblogs.com/mantu/p/5882439.html
Copyright © 2011-2022 走看看