motan源码分析四:客户端调用服务
在第一章中,我们分析了服务的发布与注册,本章中将简单的分析一下客户端调用服务的代码及流程,本文将以spring加载的方式进行分析。
1.在DemoRpcClient类的main()方法中加载类:
1 2 3 | 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的动态代理技术
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | 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对象进行信息的传递。
分类:
motan源码分析
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?