1 SOFA RPC源码解析
1.1 Rest服务
1.1.1 客户端调用
当我们在SpringXML文件中使用sofa:reference引用服务以后,我们就可以在其它类中引用创建的指定接口的代理对象,并像调用本地Java类那样,调用接口的某个方法。
以下通过在Spring应用上下文中按照名字personReferenceRest查找com.alipay.sofa.boot.examples.demo.rpc.bean.PersonService接口的代理对象,然后调用其sayName方法,描述客户端调用Rest服务的流程:
1. public static void main(String[] args)throws InterruptedException {
2.
3. ConfigurableApplicationContext ac =SpringApplication.run(SofaBootRpcDemoApplication.class, args);
4.
5. PersonService personService = (PersonService)ac.getBean("personReferenceRest");
6.
7. personService.sayName("Mike");
8.
9. }
通过ConfigurableApplicationContext类getBean方法,获取名字为personReferenceRest的实例,此时返回PersonService接口的JDK代理对象,此代理对象的handler为JDKInvocationHandler,实现了PersonService接口。
当调用JDK代理对象的sayName方法时,实际执行的是JDKInvocationHandler的invoke方法:
1. public Object invoke(Object proxy, Methodmethod, Object[] paramValues)
2. throws Throwable {
3. String methodName = method.getName();
4. Class[] paramTypes =method.getParameterTypes();
5. ……略
6. SofaRequest sofaRequest =MessageBuilder.buildSofaRequest(method.getDeclaringClass(),
7. method, paramTypes, paramValues);
8. SofaResponse response =proxyInvoker.invoke(sofaRequest);
9. if (response.isError()) {
10. throw new SofaRpcException(……略);
11. }
12. Object ret = response.getAppResponse();
13. if (ret instanceof Throwable) {
14. throw (Throwable) ret;
15. } else {
16. if (ret == null) {
17. returnClassUtils.getDefaultArg(method.getReturnType());
18. }
19. return ret;
20. }
21. }
JDKInvocationHandler类invoke方法主要处理逻辑如下:
- 获取方法名和参数类型数组;
- 调用MessageBuilder类buildSofaRequest方法,根据method信息构造SOFA RPC请求SofaRequest;
- 以SofaRequest为参数,调用proxyInvoker(DefaultClientProxyInvoker类型)的invoke方法,发起SOFA远程服务调用:
1. public SofaResponse invoke(SofaRequestrequest) throws SofaRpcException {
2. SofaResponse response = null;
3. Throwable throwable = null;
4. try {
5. RpcInternalContext.pushContext();
6. RpcInternalContext context =RpcInternalContext.getContext();
7. context.setProviderSide(false);
8. // 包装请求
9. decorateRequest(request);
10. try {
11. // 产生开始调用事件
12. if(EventBus.isEnable(ClientStartInvokeEvent.class)) {
13. EventBus.post(newClientStartInvokeEvent(request));
14. }
15. // 得到结果
16. response = cluster.invoke(request);
17. } catch (SofaRpcException e) {
18. throwable = e;
19. throw e;
20. } finally {
21. // 产生调用结束事件
22. if(EventBus.isEnable(ClientEndInvokeEvent.class)) {
23. EventBus.post(newClientEndInvokeEvent(request, response, throwable));
24. }
25. }
26. // 包装响应
27. decorateResponse(response);
28. return response;
29. } finally {
30. RpcInternalContext.removeContext();
31. RpcInternalContext.popContext();
32. }
33. }
调用父类ClientProxyInvoker的invoke方法,该方法为一个模板方法,定义了远程服务调用的具体步骤,如包装请求、产生调用开始事件、实际的服务调用、产生调用结束事件、包装响应。其中:
1. 包装请求主要是根据用户设置的参数值设置SofaRequest实例相关属性,如目标服务名、序列化类型、调用类型、调用级别超时时间、调用级别回调函数、目标URL等;
2. 产生调用开始事件主要是发布ClientStartInvokeEvent事件;
3. 实际的服务调用主要是调用Cluster接口的某个实现(此处为FailoverCluster)的invoke方法,完成实际的远程服务调用;
4. 产生调用结束事件主要是发布ClientEndInvokeEvent事件;
5. 包装响应主要是处理返回的响应结果SofaResponse,如ResponseFuture的处理、透传数据的处理等;
我们主要关注第3步,实际的服务调用。先看一下FailoverCluster父类AbstractCluster的invoke方法:
1. public SofaResponse invoke(SofaRequestrequest) throws SofaRpcException {
2. SofaResponse response = null;
3. try {
4. // 做一些初始化检查,例如未连接可以连接
5. checkClusterState();
6. // 开始调用
7. countOfInvoke.incrementAndGet(); //计数+1
8. response = doInvoke(request);
9. return response;
10. } catch (SofaRpcException e) {
11. // 客户端收到异常(客户端自己的异常)
12. throw e;
13. } finally {
14. countOfInvoke.decrementAndGet(); //计数-1
15. }
16. }
又是一个模板方法,主要关注doInvoke方法: