CSE REST源码分析

1 使用方法

1.1 Provider

使用示例如下:

@RestSchema(schemaId = "springmvcHello")
@RequestMapping(path = "/springmvchello", produces = MediaType.APPLICATION_JSON)
public class SpringmvcHelloEndpoint implements Hello {
    
    
  @Override
  @RequestMapping(path = "/sayhi", method = RequestMethod.POST)
  public String sayHi(@RequestParam(name = "name", defaultValue = "test") String name) {
    
    
    return "Hello " + name;
  }

  @Override
  @RequestMapping(path = "/sayhello", method = RequestMethod.POST)
  public String sayHello(@RequestBody Person person) {
    
    
    return "Hello person " + person.getName();
  }
}

public interface Hello {
    
    
  String sayHi(String name);
  String sayHello(Person person);
}

microservice.yml配置如下:

servicecomb:
  service:
    application: springmvc-sample
    name: springmvc
    version: 0.0.1

    registry:
      # Default using local service center
      address: http://localhost:30100
      # address: https://cse.cn-south-1.myhuaweicloud.com
      instance:
        watch: false
  config:
    client:
      # Default using local config center
      # serverUri: https://cse.cn-south-1.myhuaweicloud.com
      serverUri: http://localhost:30113
      refreshMode: 1

1.2 Consumer

使用示例如下:

@Component("SpringmvcHelloClient")
public class SpringmvcHelloClient {
    
    
  private static final Logger LOG = LoggerFactory.getLogger(SpringmvcHelloClient.class);

  private static RestTemplate restTemplate = RestTemplateBuilder.create();

  @RpcReference(microserviceName = "springmvc", schemaId = "springmvcHello")
  private static Hello hello;

  public void run() throws Exception {
    
    
    Person person = new Person();
    person.setName("ServiceComb/Java Chassis");

    // RestTemplate Consumer or POJO Consumer. You can choose whatever you like
    // 1 RestTemplate Consumer
    String sayHiResult =
        restTemplate.postForObject("cse://springmvc/springmvchello/sayhi?name={name}", null, String.class, "Java Chassis");
    Assertion.assertEquals("Hello Java Chassis", sayHiResult);

    String sayHiDefaultResult =
        restTemplate.postForObject("cse://springmvc/springmvchello/sayhi", null, String.class);
    Assertion.assertEquals("Hello test", sayHiDefaultResult);

    String sayHelloResult = restTemplate.postForObject("cse://springmvc/springmvchello/sayhello", person, String.class);
    Assertion.assertEquals("Hello person ServiceComb/Java Chassis", sayHelloResult);

    System.out.println("RestTemplate Consumer or POJO Consumer.  You can choose whatever you like.");
    System.out.println("RestTemplate consumer sayhi services: " + sayHiResult);
    System.out.println("RestTemplate consumer sayHiDefault services: " + sayHiDefaultResult);
    System.out.println("RestTemplate consumer sayhello services: " + sayHelloResult);

    // 2 POJO Consumer
    String pojoSayHi = hello.sayHi("Java Chassis");
    Assertion.assertEquals("Hello Java Chassis", pojoSayHi);
    String pojoSayHello = hello.sayHello(person);
    Assertion.assertEquals("Hello person ServiceComb/Java Chassis", pojoSayHello);

    System.out.println("POJO consumer sayhi services: " + pojoSayHi);
    System.out.println("POJO consumer sayhello services: " + pojoSayHello);

    // 3 AsyncRestTemplate Consumer
    // NOTICE: since 2.0.0, spring deprecated AsyncRestTemplate, user's can use CompletableFuture of RPC instead
    CseAsyncRestTemplate cseAsyncRestTemplate = new CseAsyncRestTemplate();
    ListenableFuture<ResponseEntity<String>> responseEntityListenableFuture = cseAsyncRestTemplate
        .postForEntity("cse://springmvc/springmvchello/sayhi?name={name}", null, String.class, "Java Chassis");
    ResponseEntity<String> responseEntity = responseEntityListenableFuture.get();
    Assertion.assertEquals("Hello Java Chassis", responseEntity.getBody());
    System.out.println("AsyncRestTemplate Consumer sayHi services: " + responseEntity.getBody());

    HttpEntity<Person> entity = new HttpEntity<>(person);
    ListenableFuture<ResponseEntity<String>> listenableFuture = cseAsyncRestTemplate
        .exchange("cse://springmvc/springmvchello/sayhello", HttpMethod.POST, entity, String.class);

    listenableFuture.addCallback(
        new ListenableFutureCallback<ResponseEntity<String>>() {
    
    
          @Override
          public void onFailure(Throwable ex) {
    
    
            LOG.error("AsyncResTemplate Consumer catched exception when sayHello, ", ex);
          }

          @Override
          public void onSuccess(ResponseEntity<String> result) {
    
    
            System.out.println("AsyncRestTemplate Consumer sayHello services: " + result.getBody());
          }
        });
  }
}

cse展示了三种调用provider的方式,第二种方式,使用RPC的方式调用provider,本质都是发送http请求,consumer的实现逻辑不做进一步探讨。

2 初始化

Provider

provider中重要的注解是RestSchema,RestSchema注解扫描在RestProducers类中,实现如下:

@Component
public class RestProducers implements BeanPostProcessor {
    
    
  private List<ProducerMeta> producerMetaList = new ArrayList<>();

  // 1 
  @SuppressWarnings("unchecked")
  private Class<? extends Annotation> restControllerCls = (Class<? extends Annotation>) ReflectUtils
      .getClassByName("org.springframework.web.bind.annotation.RestController");

  private boolean scanRestController = restControllerCls != null &&
      DynamicPropertyFactory.getInstance().getBooleanProperty(RestConst.PROVIDER_SCAN_REST_CONTROLLER, true).get();

  public List<ProducerMeta> getProducerMetaList() {
    
    
    return producerMetaList;
  }

  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    
    
    return bean;
  }

  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    
    
    processProvider(beanName, bean);

    return bean;
  }

  protected void processProvider(String beanName, Object bean) {
    
    
    // aop后,新的实例的父类可能是原class,也可能只是个proxy,父类不是原class
    // 所以,需要先取出原class,再取标注
    Class<?> beanCls = BeanUtils.getImplClassFromBean(bean);
    if (beanCls == null) {
    
    
      return;
    }
    RestSchema restSchema = beanCls.getAnnotation(RestSchema.class);
    if (restSchema != null) {
    
    
      ProducerMeta producerMeta = new ProducerMeta(restSchema.schemaId(), bean);
      producerMeta.setSchemaInterface(restSchema.schemaInterface());
      producerMetaList.add(producerMeta);
      return;
    }

    if (scanRestController && beanCls.getAnnotation(restControllerCls) != null) {
    
    
      ProducerMeta producerMeta = new ProducerMeta(beanCls.getName(), bean);
      producerMetaList.add(producerMeta);
    }
  }
}

在cse的实现中,首先扫描RestSchema注解,如果没有使用RestSchema注解,则扫描RestController注解,RestController注解生成的接口契约默认使用bean的名称作为schemaId,其实这就与Rpc的使用方式差别不大了。

3 调用方式

参见rpc调用方式。

猜你喜欢

转载自blog.csdn.net/qq_32907491/article/details/131502547
cse
今日推荐