困惑的袋鼠,对框架的把握有些茫然,但是仍然一步步向前,行动总比一直迷茫停止不前要好,您说呢,各位客官?

 

这篇开始客户端的分析。有些地方的代码,就不每段都标出了,中间有跳跃的地方,请自己对照代码来看。鄙人认为,光看武功秘籍没用,得动手debug,您说是不?

文章发了之前,斟酌了一会,因为自己认为写的不理想,有很多东西分析的不透彻,但是,决定还是发布出来,作为自己学习的历程,也希望大家多多给出建议。不断努力。

 

Demo

当然还是看源码中的tutorial demo了。


1 public class MotanApiClientDemo {
 2 
 3     public static void main(String[] args) {
 4         RefererConfig<MotanDemoService> motanDemoServiceReferer = new RefererConfig<MotanDemoService>();
 5 
 6         // 设置接口及实现类
 7         motanDemoServiceReferer.setInterface(MotanDemoService.class);
 8 
 9         // 配置服务的group以及版本号
10         motanDemoServiceReferer.setGroup("motan-demo-rpc");
11         motanDemoServiceReferer.setVersion("1.0");
12         motanDemoServiceReferer.setRequestTimeout(1000);
13 
14         // 配置注册中心直连调用
15         RegistryConfig registry = new RegistryConfig();
16 
17         //use direct registry
18         //registry.setRegProtocol("direct");
19         //registry.setAddress("127.0.0.1:8002");
20 
21         // use ZooKeeper registry
22         registry.setRegProtocol("zookeeper");
23         registry.setAddress("127.0.0.1:2181");
24         motanDemoServiceReferer.setRegistry(registry);
25 
26         // 配置RPC协议
27         ProtocolConfig protocol = new ProtocolConfig();
28         protocol.setId("motan");
29         protocol.setName("motan");
30         motanDemoServiceReferer.setProtocol(protocol);
31         // motanDemoServiceReferer.setDirectUrl("localhost:8002");  // 注册中心直连调用需添加此配置
32 
33         // 使用服务
34         MotanDemoService service = motanDemoServiceReferer.getRef();
35         System.out.println(service.hello("motan"));
36 
37         System.exit(0);
38     }
39 }



前边的部分跟服务端的差不多,都是配置信息的设置,重点在34行和35行。注意,这边用的是RefererConfig这个类,它和ServiceConfig的关系如下:

xmodem 服务端_xmodem 服务端

(手残,打lol总输5555555555555,图形大家勉强看着,用StarUML画的)

motanDemoServiceReferer.getRef()这行代码中就是Referer的初始化方法



1 public T getRef() {
 2         if (ref == null) {
 3             initRef();
 4         }
 5         return ref;
 6     }
 7 
 8     @SuppressWarnings({"unchecked", "rawtypes"})
 9     public synchronized void initRef() {


。。。省略。。。25 
26         checkInterfaceAndMethods(interfaceClass, methods);
27 
28         clusterSupports = new ArrayList<>(protocols.size());
29         List<Cluster<T>> clusters = new ArrayList<>(protocols.size());
30         String proxy = null;
31 
32         ConfigHandler configHandler = ExtensionLoader.getExtensionLoader(ConfigHandler.class).getExtension(MotanConstants.DEFAULT_VALUE);
33 
34         List<URL> registryUrls = loadRegistryUrls();
35         String localIp = getLocalHostAddress(registryUrls);
36         for (ProtocolConfig protocol : protocols) {
37             Map<String, String> params = new HashMap<>();
。。。省略。。。44 
45             String path = StringUtils.isBlank(serviceInterface) ? interfaceClass.getName() : serviceInterface;
46             URL refUrl = new URL(protocol.getName(), localIp, MotanConstants.DEFAULT_INT_VALUE, path, params);
47             ClusterSupport<T> clusterSupport = createClusterSupport(refUrl, configHandler, registryUrls);
48 
49             clusterSupports.add(clusterSupport);
50             clusters.add(clusterSupport.getCluster());
51 
52             if (proxy == null) {
53                 String defaultValue = StringUtils.isBlank(serviceInterface) ? URLParamType.proxy.getValue() : MotanConstants.PROXY_COMMON;
54                 proxy = refUrl.getParameter(URLParamType.proxy.getName(), defaultValue);
55             }
56         }
57 
58         ref = configHandler.refer(interfaceClass, clusters, proxy);
59 
60         initialized.set(true);
61     }



上面的的代码重要的功能,我用图形表示一下:

xmodem 服务端_List_02

上文的initRef()中最重要的当属Cluster的初始化。什么是Cluster?他其实代表provider服务集群,每一个具体的provider服务都被抽象成一个Referer接口,这个接口与客户端的Exporter相对应。因此,Cluster的本质是Referer的容器,并且提供了负载均衡和HA服务。每一个Cluster都被ClusterSupport封装起来,以提供对Cluster的刷新机制和生命周期管理。一个RefererConfig只会对应一个Cluster和一个ClusterSupport。(这段话,摘自http://kriszhang.com/motan-rpc-impl/)

 

createClusterSupport()



1 private ClusterSupport<T> createClusterSupport(URL refUrl, ConfigHandler configHandler, List<URL> registryUrls) {
 2         List<URL> regUrls = new ArrayList<>();
 3 
 4         // 如果用户指定directUrls 或者 injvm协议访问,则使用local registry
 5         if (StringUtils.isNotBlank(directUrl) || MotanConstants.PROTOCOL_INJVM.equals(refUrl.getProtocol())) {

。。。省略。。。
28         } else { // 通过注册中心配置拼装URL,注册中心可能在本地,也可能在远端
29             if (registryUrls == null || registryUrls.isEmpty()) {

。。。
34             }
35             for (URL url : registryUrls) {
36                 regUrls.add(url.createCopy());
37             }
38         }
39 
40         for (URL url : regUrls) {
41             url.addParameter(URLParamType.embed.getName(), StringTools.urlEncode(refUrl.toFullStr()));
42         }
           // 这里,委托给SimpleConfigHandler进行ClusterSupport的创建
43         return configHandler.buildClusterSupport(interfaceClass, regUrls);
44     }



class ClusterSupport<T> implements NotifyListener

buildClusterSupport方法中主要是new ClusterSupport,然后进行初始化,我们看看他初始化里的内容。



1 public void init() {
 2         long start = System.currentTimeMillis();
       // 方法里,通过SPI机制,生成ClusterSpi、ConfigurableWeightLoadBalance、FailoverHaStrategy三个对象,然后设置到ClusterSpi的实例中。
 3         prepareCluster();
 4 
 5         URL subUrl = toSubscribeUrl(url);
 6         for (URL ru : registryUrls) {
 7 
 8             String directUrlStr = ru.getParameter(URLParamType.directUrl.getName());
 9             // 如果有directUrl,直接使用这些directUrls进行初始化,不用到注册中心discover
10             if (StringUtils.isNotBlank(directUrlStr)) {
11                 List<URL> directUrls = parseDirectUrls(directUrlStr);
12                 if (!directUrls.isEmpty()) {
13                     notify(ru, directUrls);
14                     LoggerUtil.info("Use direct urls, refUrl={}, directUrls={}", url, directUrls);
15                     continue;
16                 }
17             }
18 
19             // client 注册自己,同时订阅service列表         重点在这两行
               // getRegistry方法中首先是通过SPI机制生成ZookeeperRegistryFactory实例,利用这个工厂模式,创建ZookeeperRegistry
20             Registry registry = getRegistry(ru);
               // 这里做的是服务的订阅。方法中最终调用的是CommandFailbackRegistry的doSubscribe方法,相关类图的关系图请往下看
21             registry.subscribe(subUrl, this);
22         }
23 
。。。 省略 。。。
38     }



服务发现与刷新

RegistryFactory相关类图关系如下:

xmodem 服务端_netty_03

Registry相关类图关系如下:

xmodem 服务端_大数据_04

CommandFailbackRegistry   doSubscribe()的内容如下:



1 protected void doSubscribe(URL url, final NotifyListener listener) {
 2         LoggerUtil.info("CommandFailbackRegistry subscribe. url: " + url.toSimpleString());
 3         URL urlCopy = url.createCopy();
 4         CommandServiceManager manager = getCommandServiceManager(urlCopy);
 5         manager.addNotifyListener(listener);
 6 
 7         subscribeService(urlCopy, manager);
 8         subscribeCommand(urlCopy, manager);
 9 
10         List<URL> urls = doDiscover(urlCopy);
11         if (urls != null && urls.size() > 0) {
12             this.notify(urlCopy, listener, urls);
13         }
14     }



 AbstractRegistry



1 protected void notify(URL refUrl, NotifyListener listener, List<URL> urls) {
 2         if (listener == null || urls == null) {
 3             return;
 4         }
 。。。 省略。。。 25 
26         for (List<URL> us : nodeTypeUrlsInRs.values()) {
 // 重点在这里,调用了listener的notify方法。那么这个listerner是谁,就是ClusterSupport,它是在上面方法中的这两行代码里注册的
               // Registry registry = getRegistry(ru);
               // registry.subscribe(subUrl, this); this 指定的是ClusterSupport
         // 说了一大堆就是,概括说明就是,通过retistry进行回调notify方法  ※
27             listener.notify(getUrl(), us);
28         }
29     }



我们继续探索ClusterSupport中的notfiy方法:

下面的代码,它主要就是根据新的URL(从zk端获得)创建Referer对象,并且刷新整个集群。刷新操作主要将新的Referer加入集群,并将旧的Referer对象释放掉。需要注意,这里并没有直接释放Referer资源,而是采用了延迟机制,主要考虑到Referer可能正在执行中,马上销毁会影响正常请求。我们会一层层进入代码内部去分析。



1 /**
 2      * <pre>
 3      * 1 notify的执行需要串行
 4      * 2 notify通知都是全量通知,在设入新的referer后,cluster需要把不再使用的referer进行回收,避免资源泄漏;
 5      * 3 如果该registry对应的referer数量为0,而没有其他可用的referers,那就忽略该次通知;
 6      * 4 此处对protoco进行decorator处理,当前为增加filters
 7      * </pre>
 8      */
 9     @Override
10     public synchronized void notify(URL registryUrl, List<URL> urls) {
。。。 省略。。。

22         // 通知都是全量通知,在设入新的referer后,cluster内部需要把不再使用的referer进行回收,避免资源泄漏
23         // 
24 
25         // 判断urls中是否包含权重信息,并通知loadbalance。
26         processWeights(urls);
27 
28         List<Referer<T>> newReferers = new ArrayList<Referer<T>>();
29         for (URL u : urls) {
30             if (!u.canServe(url)) {
31                 continue;
32             }
33             Referer<T> referer = getExistingReferer(u, registryReferers.get(registryUrl));
34             if (referer == null) {
35                 // careful u: serverURL, refererURL的配置会被serverURL的配置覆盖
36                 URL refererURL = u.createCopy();
37                 mergeClientConfigs(refererURL);
           // protocol 的类 :ProtocolFilterDecorator
                   // debug进入代码内部可以发现,里面是referer的创建和初始化操作。referer可以看成是RPC客户端的操作的类
                   // 里面,创建了NettyClient,在初始化操作中,跟server的类似,委托NettyClient进行transport层的操作
38                 referer = protocol.refer(interfaceClass, refererURL, u);
39             }
40             if (referer != null) {
41                 newReferers.add(referer);
42             }
43         }
44 
45         if (CollectionUtil.isEmpty(newReferers)) {
46             onRegistryEmpty(registryUrl);
47             return;
48         }
49 
50         // 此处不销毁referers,由cluster进行销毁
51         registryReferers.put(registryUrl, newReferers);
52         refreshCluster();
53     }



netty的部分暂时不进行分析。那么,到这里为止,referer就可以成功生成,这样的话,在前面initRef方法中createClusterSupport的部分就结束了,接下来就是创建代理的部分了。

回顾一下initRef方法



1 public synchronized void initRef() {
。。。23 
。。。28         for (ProtocolConfig protocol : protocols) {
。。。36 
37             String path = StringUtils.isBlank(serviceInterface) ? interfaceClass.getName() : serviceInterface;
38             URL refUrl = new URL(protocol.getName(), localIp, MotanConstants.DEFAULT_INT_VALUE, path, params);
39             ClusterSupport<T> clusterSupport = createClusterSupport(refUrl, configHandler, registryUrls);
40 
41             clusterSupports.add(clusterSupport);
42             clusters.add(clusterSupport.getCluster());
43 
44             if (proxy == null) {
45                 String defaultValue = StringUtils.isBlank(serviceInterface) ? URLParamType.proxy.getValue() : MotanConstants.PROXY_COMMON;
46                 proxy = refUrl.getParameter(URLParamType.proxy.getName(), defaultValue);
47             }
48         }
49 
50         ref = configHandler.refer(interfaceClass, clusters, proxy);
51 
52         initialized.set(true);
53     }



上面50行的内容如下:

使用JDK的方法来创建动态代理。



1     public <T> T refer(Class<T> interfaceClass, List<Cluster<T>> clusters, String proxyType) {
2         ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension(proxyType);
3         return proxyFactory.getProxy(interfaceClass, clusters);
4     }



当客户端初始化完毕之后,我们就能正常使用motan进行方法调用了。对接口的调用,实际上是被动态代理了,动态代理的执行入口是哪里呢?RefererInvocationHandler提供了这个入口。主要实现如下:



1 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
。。。
11 
12         DefaultRequest request = new DefaultRequest();
13         request.setRequestId(RequestIdGenerator.getRequestId());
14         request.setArguments(args);
15         String methodName = method.getName();
16         boolean async = false;
17         if (methodName.endsWith(MotanConstants.ASYNC_SUFFIX) && method.getReturnType().equals(ResponseFuture.class)) {
18             methodName = MotanFrameworkUtil.removeAsyncSuffix(methodName);
19             async = true;
20         }
21         request.setMethodName(methodName);
22         request.setParamtersDesc(ReflectUtil.getMethodParamDesc(method));
23         request.setInterfaceName(interfaceName);
24 
25         return invokeRequest(request, getRealReturnType(async, this.clz, method, methodName), async);
26     }



 

 



1 Object invokeRequest(Request request, Class returnType, boolean async) throws Throwable {
 2         RpcContext curContext = RpcContext.getContext();
 3         curContext.putAttribute(MotanConstants.ASYNC_SUFFIX, async);
 4 
 。。。17 
18         // 当 referer配置多个protocol的时候,比如A,B,C,
19         // 那么正常情况下只会使用A,如果A被开关降级,那么就会使用B,B也被降级,那么会使用C
20         for (Cluster<T> cluster : clusters) {
21             String protocolSwitcher = MotanConstants.PROTOCOL_SWITCHER_PREFIX + cluster.getUrl().getProtocol();
22 
23             Switcher switcher = switcherService.getSwitcher(protocolSwitcher);
24 
。。。34 
35             Response response;
36             boolean throwException = Boolean.parseBoolean(cluster.getUrl().getParameter(URLParamType.throwException.getName(), URLParamType.throwException.getValue()));
37             try {
                   // 真正的执行入口在这里
38                 response = cluster.call(request);
39                 if (async) {
40                     if (response instanceof ResponseFuture) {
41                         ((ResponseFuture) response).setReturnType(returnType);
42                         return response;
43                     } else {
44                         ResponseFuture responseFuture = new DefaultResponseFuture(request, 0, cluster.getUrl());
45                         if (response.getException() != null) {
46                             responseFuture.onFailure(response);
47                         } else {
48                             responseFuture.onSuccess(response);
49                         }
50                         responseFuture.setReturnType(returnType);
51                         return responseFuture;
52                     }
53                 } else {
54                     Object value = response.getValue();
55                     if (value != null && value instanceof DeserializableObject) {
56                         try {
57                             value = ((DeserializableObject) value).deserialize(returnType);
58                         } catch (IOException e) {
59                             LoggerUtil.error("deserialize response value fail! deserialize type:" + returnType, e);
60                             throw new MotanFrameworkException("deserialize return value fail! deserialize type:" + returnType, e);
61                         }
62                     }
63                     return value;
64                 }
65             } 
。。。
85     }



Cluster.call()实际上是重头戏,他在方法执行内部代理给了haStrategy.call(),然后ha策略使用LoadBalance选择一个或一批Referer对象,并根据具体策略调用这个Referer的call()方法。Referer接口在motan协议的默认实现是DefaultRpcReferer,他在初始化的时候通过EndpointFactory创建了一个Client对象,他其实就是NettyClient,然后调用client.request(),从而使rpc请求顺利进入了transport层。(前面这段分析,参考了别的文章,写的比我有水平,自己汗颜,感觉到里面的差距)之后,通过transport层,一步步到达服务端。