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方法:
1. public SofaResponse doInvoke(SofaRequestrequest) throws SofaRpcException {
2. String methodName =request.getMethodName();
3. int retries =consumerConfig.getMethodRetries(methodName);
4. int time = 0;
5. SofaRpcException throwable = null;// 异常日志
6. List<ProviderInfo>invokedProviderInfos = new ArrayList<ProviderInfo>(retries + 1);
7. do {
8. ProviderInfo providerInfo = select(request,invokedProviderInfos);
9. try {
10. SofaResponse response =filterChain(providerInfo, request);
11. if (response != null) {
12. ……略 return response;
13. } else {
14. throwable = newSofaRpcException(……略);
15. }
16. } catch (SofaRpcException e) { // 服务端异常+ 超时异常才发起rpc异常重试
17. if (e.getErrorType() ==RpcErrorType.SERVER_BUSY
18. || e.getErrorType() ==RpcErrorType.CLIENT_TIMEOUT) {
19. throwable = e;
20. time++;
21. } else {
22. throw e;
23. }
24. } catch (Exception e) { // 其它异常不重试
25. throw new SofaRpcException(……略);
26. } finally {
27. if(RpcInternalContext.isAttachmentEnable()) {
28. RpcInternalContext.getContext().setAttachment(RpcConstants.INTERNAL_KEY_INVOKE_TIMES,
29. time + 1); // 重试次数
30. }
31. }
32. invokedProviderInfos.add(providerInfo);
33. } while (time <= retries);
34.
35. throw throwable;
36. }
在FailoverCluster类invoke方法中,首先,获取调用的方法名称。然后,获取失败时重试次数,0表示不重试。最后,通过do{}while循环调用远程服务,至少调用一次。如果设置了重试次数,则在重试次数范围内,每次根据规则选择不同的服务提供者,多次调用,直至调用成功或所有调用都失败。
循环调用远程服务的主要处理逻辑如下:
一、 访问路径路由
调用AbstractCluster类select方法,根据规则进行负载均衡,选择服务提供者:
1. protected ProviderInfo select(SofaRequestmessage, List<ProviderInfo> invokedProviderInfos)
2. throws SofaRpcException {
3. // 粘滞连接,当前连接可用
4. if (consumerConfig.isSticky()) {
5. if (lastProviderInfo != null) {
6. ProviderInfo providerInfo =lastProviderInfo;
7. ClientTransport lastTransport =connectionHolder.getAvailableClientTransport(providerInfo);
8. if (lastTransport != null&& lastTransport.isAvailable()) {
9. checkAlias(providerInfo,message);
10. return providerInfo;
11. }
12. }
13. }
14. // 原始服务列表数据 --> 路由结果
15. List<ProviderInfo> providerInfos =routerChain.route(message, null);
16. if (CommonUtils.isEmpty(providerInfos)){
17. thrownoAvailableProviderException(message.getTargetServiceUniqueName());
18. }
19. if(CommonUtils.isNotEmpty(invokedProviderInfos) && providerInfos.size()> invokedProviderInfos.size()) { // 总数大于已调用数
20. providerInfos.removeAll(invokedProviderInfos);//已经调用异常的本次不再重试
21. }
22.
23. String targetIP = null;
24. ProviderInfo providerInfo;
25. RpcInternalContext context =RpcInternalContext.peekContext();
26. if (context != null) {
27. targetIP = (String)RpcInternalContext.getContext().getAttachment(RpcConstants.HIDDEN_KEY_PINPOINT);
28. }
29. if (StringUtils.isNotBlank(targetIP)) {
30. // 如果指定了调用地址
31. providerInfo =selectPinpointProvider(targetIP, providerInfos);
32. if (providerInfo == null) {
33. // 指定的不存在
34. throw unavailableProviderException(message.getTargetServiceUniqueName(),targetIP);
35. }
36. ClientTransport clientTransport =selectByProvider(message, providerInfo);
37. if (clientTransport == null) {
38. // 指定的不存在或已死,抛出异常
39. throwunavailableProviderException(message.getTargetServiceUniqueName(), targetIP);
40. }
41. return providerInfo;
42. } else {
43. do {
44. // 再进行负载均衡筛选
45. providerInfo = loadBalancer.select(message,providerInfos);
46. ClientTransport transport =selectByProvider(message, providerInfo);
47. if (transport != null) {
48. return providerInfo;
49. }
50. providerInfos.remove(providerInfo);
51. } while (!providerInfos.isEmpty());
52. }
53. thrownoAvailableProviderException(message.getTargetServiceUniqueName());
54. }
1. 如果服务消费者的sticky为true,则表示粘滞连接,即如果当前连接可用,使用当前连接。否则,继续选择服务提供者。此处sticky为false,继续:
2. 调用com.alipay.sofa.rpc.client.RouterChain类route方法,根据SofaRequest请求选择所有服务提供者,并存入providerInfos列表中:
1. public List<ProviderInfo>route(SofaRequest request, List<ProviderInfo> providerInfos) {
2. for (Router router : routers) {
3. providerInfos =router.route(request, providerInfos);
4. }
5. return providerInfos;
6. }
★ 依次遍历所有的Router(此处只存在一个Router,即DirectUrlRouter),调用每个Router的route方法选择服务提供者。此处调用DirectUrlRouter的route方法:
1. public List<ProviderInfo>route(SofaRequest request, List<ProviderInfo> providerInfos) {
2. AddressHolder addressHolder =consumerBootstrap.getCluster().getAddressHolder();
3. if (addressHolder != null) {
4. List<ProviderInfo> current =addressHolder.getProviderInfos(RpcConstants.ADDRESS_DIRECT_GROUP);
5. if (providerInfos != null) {
6. providerInfos.addAll(current);
7. } else {
8. providerInfos = current;
9. }
10. }
11. recordRouterWay(RPC_DIRECT_URL_ROUTER);
12. return providerInfos;
13. }
★ 根据服务列表的标签(此处为_DIRECT),获取某分组的服务列表,并增加到服务提供者信息列表providerInfos中。
3. 当服务提供者的数量大于已经调用过服务次数,则去掉已经调用异常的服务提供者,本次不再重试。
4. 获取当前线程的RpcInternalContext实例,然后从context的attachments中获取key为.pinpoint的目标IP地址targetIP;
5. 如果targetIP为空,则循环执行:
1) 调用负载均衡器LoadBalancer的select方法进行筛选:
1. public ProviderInfo select(SofaRequestrequest, List<ProviderInfo> providerInfos) throws SofaRpcException {
2. if (providerInfos.size() == 0) {
3. throw noAvailableProviderException(request.getTargetServiceUniqueName());
4. }
5. if (providerInfos.size() == 1) {
6. return providerInfos.get(0);
7. } else {
8. return doSelect(request,providerInfos);
9. }
10. }
★ 如果服务提供者数量为0,则直接抛出noAvailableProviderException异常;
★ 如果服务提供者数量为1,则直接返回那个唯一的服务提供者;
★ 如果服务提供者数量大于1,则调用doSelect方法,根据某种规则从服务提供者列表中选择一个服务提供者。例如:随机负载均衡器RandomLoadBalancer,则全部服务提供者列表按权重随机选择;轮询负载均衡器RoundRobinLoadBalancer,则全部服务提供者列表按方法级进行轮询,互不影响;其它类型负载均衡器自行查看。
★ 此处服务提供者数量为1,直接返回服务提供者列表中那个唯一的服务提供者。
2) 调用selectByProvider方法,为选择的服务提供者providerInfo得到连接;
1. protected ClientTransportselectByProvider(SofaRequest message, ProviderInfo providerInfo) {
2. ClientTransport transport =connectionHolder.getAvailableClientTransport(providerInfo);
3. if (transport != null) {
4. if (transport.isAvailable()) {
5. lastProviderInfo =providerInfo;
6. checkAlias(providerInfo, message); //检查分组
7. return transport;
8. } else {
9. connectionHolder.setUnavailable(providerInfo,transport);
10. }
11. }
12. return null;
13. }
★ 调用connectionHolder(此外为AllConnectConnectionHolder实例)的getAvailableClientTransport方法,根据providerInfo查找存活的ClientTransport,此处查找到RestClientTransport实例;
★ 如果RestClientTransport实例不可用,则设置connectionHolder为不可用,返回null。否则,返回可用的RestClientTransport实例。
3) 如果获取到RestClientTransport实例,则返回选择的providerInfo。如果没有获取到RestClientTransport实例,则从providerInfos列表中删除刚才选择的providerInfo。如果此时providerInfos不为空,则下一个循环,继续选择可用的providerInfo。如果没有可用的providerInfo,则抛noAvailableProviderException异常。
6. 如果targetIP不为空,表示指定了服务调用地址,则调用selectPinpointProvider方法,从providerInfos列表中targetIP对应的服务提供者。然后调用selectByProvider方法,为服务提供者providerInfo得到连接。如果ClientTransport实例为null,则抛出unavailableProviderException异常。如果ClientTransport实例不为null,则返回providerInfo实例。
此处targetIP为空,所以按照targetIP为空的逻辑处理,返回选择的providerInfo实例,此实例的字符串值为127.0.0.1:8341,为服务提供者的地址。
二、 调用执行链
以选择的providerInfo和SofaRequest实例为参数,调用FailoverCluster类filterChain方法,发起远程服务调用链:
1. protected SofaResponsefilterChain(ProviderInfo providerInfo, SofaRequest request) throwsSofaRpcException {
2. RpcInternalContext.getContext().setProviderInfo(providerInfo);
3. return filterChain.invoke(request);
4. }
对于FilterChain的设计原理,可以参考《FilterChain》,在此不详述。
在此案例中,过滤器执行链只存在两个节点。第一个节点是由过滤器com.alipay.sofa.rpc.filter.ConsumerExceptionFilter封装成的FilterInvoker调用器。第二个节点是com.alipay.sofa.rpc.filter.ConsumerInvoker,此类为FilterInvoker子类。
首先执行过滤器链中第一个节点,ConsumerExceptionFilter过滤器主要用来捕获消费者调用异常,所以只是在try{}catch(){}代码块中调用下一个节点ConsumerInvoker的invoke方法;
接着执行过滤器链中第二个节点,也是最后一个节点,此处为ConsumerInvoker的invoke方法:
1. public SofaResponse invoke(SofaRequestsofaRequest) throws SofaRpcException {
2. // 设置下服务器应用
3. ProviderInfo providerInfo =RpcInternalContext.getContext().getProviderInfo();
4. String appName =providerInfo.getStaticAttr(ProviderInfoAttrs.ATTR_APP_NAME);
5. if (StringUtils.isNotEmpty(appName)) {
6. sofaRequest.setTargetAppName(appName);
7. }
8.
9. // 目前只是通过client发送给服务端
10. returnconsumerBootstrap.getCluster().sendMsg(providerInfo, sofaRequest);
11. }
ConsumerInvoker类invoke方法主要处理逻辑如下:
- 通过RpcInternalContext(基于ThreadLocal的内部使用的上下文)获取当前线程的RpcInternalContext实例。
- 通过RpcInternalContext实例获取服务提供者信息providerInfo;
- 由于是调用restful风格的服务,所以,此处调用RestConsumerBootstrap的getCluster方法,获取Cluster接口实现FailoverCluster的实例。
- 然后调用FailoverCluster的sendMsg方法:
1. public SofaResponse sendMsg(ProviderInfoproviderInfo, SofaRequest request) throws SofaRpcException {
2. ClientTransport clientTransport =connectionHolder.getAvailableClientTransport(providerInfo);
3. if (clientTransport != null &&clientTransport.isAvailable()) {
4. return doSendMsg(providerInfo,clientTransport, request);
5. } else {
6. throwunavailableProviderException(request.getTargetServiceUniqueName(),providerInfo.getOriginUrl());
7. }
8. }
调用ConnectionHolder(此处为AllConnectConnectionHolder实例)的getAvailableClientTransport方法,根据provider查找可用的ClientTransport。
1. public ClientTransportgetAvailableClientTransport(ProviderInfo providerInfo) {
2. // 先去存活列表
3. ClientTransport transport =aliveConnections.get(providerInfo);
4. if (transport != null) {
5. return transport;
6. }
7. // 再去亚健康列表
8. transport =subHealthConnections.get(providerInfo);
9. if (transport != null) {
10. return transport;
11. }
12. // 最后看看是否第一次调用未初始化
13. transport =uninitializedConnections.get(providerInfo);
14. if (transport != null) {
15. // 未初始化则初始化
16. synchronized (this) {
17. transport =uninitializedConnections.get(providerInfo);
18. if (transport != null) {
19. initClientTransport(consumerConfig.getInterfaceId(),providerInfo, transport);
20. uninitializedConnections.remove(providerInfo);
21. }
22. returngetAvailableClientTransport(providerInfo);
23. }
24. }
25. return null;
26. }
1. protected SofaResponsedoSendMsg(ProviderInfo providerInfo, ClientTransport transport,
2. SofaRequestrequest) throws SofaRpcException {
3. RpcInternalContext context =RpcInternalContext.getContext();
4. // 添加调用的服务端远程地址
5. RpcInternalContext.getContext().setRemoteAddress(providerInfo.getHost(),providerInfo.getPort());
6. try {
7. checkProviderVersion(providerInfo, request);// 根据服务端版本特殊处理
8. String invokeType =request.getInvokeType();
9. int timeout =resolveTimeout(request, consumerConfig, providerInfo);
10.
11. SofaResponse response = null;
12. // 同步调用
13. if (RpcConstants.INVOKER_TYPE_SYNC.equals(invokeType)){
14. long start =RpcRuntimeContext.now();
15. try {
16. response = transport.syncSend(request,timeout);
17. } finally {
18. if (RpcInternalContext.isAttachmentEnable()){
19. long elapsed =RpcRuntimeContext.now() - start;
20. context.setAttachment(RpcConstants.INTERNAL_KEY_CLIENT_ELAPSE,elapsed);
21. }
22. }
23. }
24. // 单向调用
25. else if(RpcConstants.INVOKER_TYPE_ONEWAY.equals(invokeType)) {
26. long start =RpcRuntimeContext.now();
27. try {
28. transport.oneWaySend(request,timeout);
29. response = newSofaResponse();
30. } finally {
31. if(RpcInternalContext.isAttachmentEnable()) {
32. long elapsed =RpcRuntimeContext.now() - start;
33. context.setAttachment(RpcConstants.INTERNAL_KEY_CLIENT_ELAPSE,elapsed);
34. }
35. }
36. }
37. // Callback调用
38. else if(RpcConstants.INVOKER_TYPE_CALLBACK.equals(invokeType)) {
39. // 调用级别回调监听器
40. SofaResponseCallback sofaResponseCallback= request.getSofaResponseCallback();
41. if (sofaResponseCallback ==null) {
42. SofaResponseCallbackmethodResponseCallback = consumerConfig
43. .getMethodOnreturn(request.getMethodName());
44. if (methodResponseCallback !=null) { // 方法的Callback
45. request.setSofaResponseCallback(methodResponseCallback);
46. }
47. }
48. transport.asyncSend(request,timeout);
49. response = new SofaResponse();
50. }
51. // Future调用
52. else if(RpcConstants.INVOKER_TYPE_FUTURE.equals(invokeType)) {
53. // 开始调用
54. ResponseFuture future =transport.asyncSend(request, timeout);
55. // 放入线程上下文
56. RpcInternalContext.getContext().setFuture(future);
57. response = new SofaResponse();
58. } else {
59. throw newSofaRpcException(RpcErrorType.CLIENT_UNDECLARED_ERROR, "Unknown invoketype:" + invokeType);
60. }
61. return response;
62. } catch (SofaRpcException e) {
63. throw e;
64. } catch (Throwable e) { // 客户端其它异常
65. throw newSofaRpcException(RpcErrorType.CLIENT_UNDECLARED_ERROR, e);
66. }
67. }
doSendMsg方法的执行过程如上述代码注释所述。
此次请求的调用方式为sync,即同步调用。所以,调用RestClientTransport类syncSend方法发送服务请求。实际的服务请求操作在doInvokeSync方法中实现:
1. protected SofaResponsedoInvokeSync(SofaRequest request, int timeoutMillis)
2. throws InvocationTargetException,IllegalAccessException {
3. SofaResponse response = newSofaResponse();
4. Method method = getMethod(request);
5. if (method == null) {
6. throw newSofaRpcException(RpcErrorType.CLIENT_UNDECLARED_ERROR,
7. "Not found method :" +request.getInterfaceName() + "." + request.getMethodName());
8. }
9. Object o = method.invoke(proxy,request.getMethodArgs());
10. response.setAppResponse(o);
11. return response;
12. }
创建SOFA RPC远程服务响应SofaResponse实例;
根据SOFA RPC服务请求获取远程服务对应的方法;
通过反射机制实现方法调用,其中proxy为PersonService接口的代理对象,handler为实现了Java动态代理java.lang.reflect.InvocationHandler接口的org.jboss.resteasy.client.jaxrs.internal.proxy.ClientProxy。
实际调用ClientProxy类invoke方法:
1. public Object invoke(Object o, Methodmethod, Object[] args)
2. throws Throwable
3. {
4. // equals and hashCode were added forcases where the proxy is added to
5. // collections. The Spring transactionmanagement, for example, adds
6. // transactional Resources to aCollection, and it calls equals and
7. // hashCode.
8.
9. MethodInvoker clientInvoker =methodMap.get(method);
10. if (clientInvoker == null)
11. {
12. if(method.getName().equals("equals"))
13. {
14. return this.equals(o);
15. }
16. else if (method.getName().equals("hashCode"))
17. {
18. return this.hashCode();
19. }
20. else if(method.getName().equals("toString") && (args == null ||args.length == 0))
21. {
22. return this.toString();
23. }
24. }
25.
26. if (clientInvoker == null)
27. {
28. throw new RuntimeException("Couldnot find a method for: " + method);
29. }
30. return clientInvoker.invoke(args);
31. }
根据method获取MethodInvoker接口的实现,此外为org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker。
调用ClientInvoker的invoke方法:
1. public Object invoke(Object[] args)
2. {
3. ClientInvocation request =createRequest(args);
4. ClientResponse response =(ClientResponse)request.invoke();
5. ClientContext context = newClientContext(request, response, entityExtractorFactory);
6. return extractor.extractEntity(context,null);
7. }
根据请求参数创建ClientInvocation实例;
调用ClientInvocation的invoke方法:
1. public Response invoke()
2. {
3. Providers current =ResteasyProviderFactory.getContextData(Providers.class);
4. ResteasyProviderFactory.pushContext(Providers.class,configuration);
5. try
6. {
7. ClientRequestContextImplrequestContext = new ClientRequestContextImpl(this);
8. ……略
9. // spec requires that aborted responsego through filter/interceptor chains.
10. ClientResponse response = aborted;
11. if (response == null)
12. response = client.httpEngine().invoke(this);
13.
14. response.setProperties(configuration.getMutableProperties());
15. ……略
16. return response;
17. }
18. finally
19. {
20. ……略
21. }
22. }
调用org.jboss.resteasy.client.jaxrs.ResteasyClient类httpEngine方法,获取org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine实例;
调用ApacheHttpClient4Engine类invoke方法:
1. public ClientResponseinvoke(ClientInvocation request)
2. {
3. String uri = request.getUri().toString();
4. final HttpRequestBase httpMethod =createHttpMethod(uri, request.getMethod());
5. final HttpResponse res;
6. try
7. {
8. loadHttpMethod(request, httpMethod);
9.
10. res = httpClient.execute(httpMethod,httpContext);
11. }
12. catch (Exception e)
13. {
14. throw newProcessingException("Unable to invoke request", e);
15. }
16. finally
17. {
18. cleanUpAfterExecute(httpMethod);
19. }
20.
21. ……略
22. }
根据请求URL(例如:http://127.0.0.1:8341/webapi/rest/person/sayName/Mike)和请求方法名称,构造org.apache.http.client.methods.HttpRequestBase请求对象:
例如:请求对象的字符串值为:
GET http://127.0.0.1:8341/webapi/rest/person/sayName/Mike HTTP/1.1
最后,调用org.apache.http.impl.client.DefaultHttpClient类execute方法,完成restful风格的远程服务调用。
到这步为止,大家可以看到,SOFA RPC客户端底层使用HttpClient调用restful服务,具体实现细节不在详述。
再回看PersonService接口的JDK代理对象的handler: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. }
在这里采用同步调用方式,所以线程阻塞在以下代码处,等待服务端接收请求,根据请求进行业务处理,最后返回处理结果:
1. SofaResponse response =proxyInvoker.invoke(sofaRequest);
服务端响应请求的流程在《服务端响应流程》中详述,在此略过。
当服务端处理完客户端请求,返回处理结果SofaResponse。调用getAppResponse方法,获取接口所需数据类型的返回结果,此处为String类型,值为:hi Mike!。