ServiceComb与SpringMVC不同的参数解析过程

结论:当请求中不存在isTest,id 等参数,CSE解析时会赋Null值, 不建议在实体类上设置默认值

// 测试用Controler
@RequestMapping("/")
@RestSchema(schemaId = "controller")
public class Controller {

@GetMapping("hello")
public String hello(TestObj testObj) {
System.out.println(testObj.toString());
return "Hello World";
}
}


// 测试QO对象

@Data
public class TestObj {
Boolean isTest = false;
Integer id = 1;
String name;
}

一、ServiceComb 参数解析 Producer处理客户端请求

需要说明的是,一个微服务,如果仅仅对外提供服务就是Provider,如果仅仅是请求其他微服务就是Consumer,两者都涉及,那么这个

微服务,既是Provider又是Consumer,如Edge网关,就是这样,它接收外部的请求,此时是Provider角色,把请求转给下游的其他微服

务,此时是Consumer角色。通常不会存在仅仅是Consumer角色的微服务,因为单纯是Consumer实际应用中没有意义。

请求处理过程

1. Producer服务启动后,会部署vertx服务,监听端口,等待Client连接,并且客户端请求路由配置到VertxRestDispatcher.

2. Client发起请求,请求被vertx路由到VertxRestDispatcher,经过用户配置的HttpServerFilter进行接收请求后的预处理操作。其中

ServerRestArgsFilter是ServiceComb内置的filter,专门用来反序列化请求和序列化响应体的。

3. 经过HttpServerFilter的afterReceiveRequest处理后,正式进入Handler链,进行具体的业务处理,用户可以自定义多个Handler,但最后一个

Handler是ProducerOperationHandler,这个是ServiceComb内置的Handler,它负责通过反射调用请求对应的operation,对请求进行处理,得到

response.

4. 得到response后,回调处理response的方法sendResponseQuietly(resp)

5. 这个方法做的事是,调用HttpServerFilter链上每个filter的beforeSendResponseAsync,进行发送前处理。其中内置的ServerRestArgsFilter会对response对象进行序列化,变成

json字符串,响应给client

ServerRestArgsFilter

1.收到请求解析参数

@Override  
public Response afterReceiveRequest(Invocation invocation, HttpServletRequestEx requestEx){    
    OperationMeta operationMeta = invocation.getOperationMeta();
    RestOperationMeta restOperationMeta = operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION);   // 解析参数     
    Map<String, Object> swaggerArguments = RestCodec.restToArgs(requestEx, restOperationMeta);    invocation.setSwaggerArguments(swaggerArguments);    
    return null;  
}

2.发送响应前处理beforeSendResponseAsync

@Override  
public CompletableFuture<Void> beforeSendResponseAsync(Invocation invocation, HttpServletResponseEx responseEx) { 
        ...    
        // 设置响应contentType    
        responseEx.setContentType(produceProcessor.getName() + "; charset=utf-8");    
        CompletableFuture<Void> future = new CompletableFuture<>();    
        try (BufferOutputStream output = new BufferOutputStream(Unpooled.compositeBuffer())) {      // 对响应体进行编码     
            produceProcessor.encodeResponse(output, body);     // 得到编码后的byte,设置到responseEx中,用于发送      
            responseEx.setBodyBuffer(output.getBuffer());      
            future.complete(null);    
        } catch (Throwable e) {    
            ...    
        }    
        return future;  
    }

微服务间上下文传递

微服务Consumer与微服务Producer之间,通过rest进行调用,上下文通过调用rest放到header里面实现上下文传递,下面让我们看一下相关代码

  • Consumer invocation的发送: 发送请求前设置header,RestClientInvocation发送处理中setCseContext,header中的key是x-cse-context,代码中的public static final String CSE_CONTEXT = "x-cse-context";

    protected void setCseContext() {
      try {
        clientRequest.putHeader(Const.CSE_CONTEXT, JsonUtils.writeValueAsString(invocation.getContext()));
      } catch (Throwable e) {
        invocation.getTraceIdLogger().error(LOGGER, "Failed to encode and set cseContext, message={}."
            , ExceptionUtils.getExceptionMessageWithoutTrace(e));
      }
    }

  • Producer 对invocation的恢复:AbstractRestInvocation在调用handler之前,创建invocation,并把Consumer传递过来的上下文恢复到invocation中,见this.setContext();

    protected void scheduleInvocation() {
      try {
        createInvocation();
      } catch (Throwable e) {
        sendFailResponse(e);
        return;
      }
    
      try {
        this.setContext();
      } catch (Exception e) {
        LOGGER.error("failed to set invocation context", e);
        sendFailResponse(e);
        return;
      }
     ...

  • Producer最后一个Handler-ProducerOperationHandler,doInvoke业务前,设置invocation到线程上下文,业务处理结束后,移除线程上下文中的invocation。所以,在业务处理中,可以通过上下文获取invocation信息。

    public void syncInvoke(Invocation invocation, SwaggerProducerOperation producerOperation, AsyncResponse asyncResp) {
      ContextUtils.setInvocationContext(invocation);
      Response response = doInvoke(invocation, producerOperation);
      ContextUtils.removeInvocationContext();
      asyncResp.handle(response);
    }

二、SpringMVC处理请求流程

1. DispatcherServlet中的doDispatcher方法进行响应,并执行至HandlerAdapter调用handle方法进行适配。
2. 来到AbstractHandlerMethodAdapter类调用handleInternal方法,该方法最终被RequestMappingHandlerAdapter所实现,即适配所有被@RequestMapping修饰的Controller的方法。
3. 在RequestMappingHandlerAdapter类中调用invokeHandlerMethod方法,此过程将Controller类中的方法转化为HttpMethod进行解析。
4. 解析后的Method调用invokeAndHandle进行接口的调用访问。
5. 在InvocableHandlerMethod类中的getMethodArgumentValues方法来解析客户端请求传递的数据。在解析之前为每个参数找到合适的参数解析器进行解析,并读取出最终的值。


6. 将读取的值进行返回,返回给Controller端后进行后续的业务逻辑。、

参考引用原文

SpringMVC原理:解析请求参数(源码分析)_pringmvc解析方法参数源码_tracyyy_的博客-CSDN博客

微服务框架ServiceComb源码解析之一——Producer处理客户端请求

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值