Skip to content

Latest commit

 

History

History
441 lines (320 loc) · 15.6 KB

File metadata and controls

441 lines (320 loc) · 15.6 KB

快速开始中,已经成功地运行了tRPC-Java的基础示例,但这还远远不够。本教程中你将继续学会:

  • 如何通过Protocol Buffer定义tRPC-Java服务
  • 如何通过YAML配置tRPC-Java服务
  • 如何用Spring Boot集成tRPC-Java服务
  • tRPC-Java具有哪些扩展能力
  • tRPC-Java支持的各种能力

为了实现跨语言的兼容性,tRPC服务依赖Protocol Buffer v3(后文简称protobuf)来定义,protobuf 的详细说明可参考其 Java官方文档

添加依赖

tRPC-Java已提供最小服务依赖,在开始自定义服务前,可引入如下依赖:

<dependency>
    <groupId>com.tencent.trpc</groupId>
    <artifactId>trpc-mini</artifactId>
    <version>${tRPC-Java.version}</version>
</dependency>

tRPC-Java仓库中的示例代码已添加该依赖,如继续运行示例代码,可跳过该节。

定义服务

为了定义一个新服务,首先需要在protobuf中声明它。下面的示例声明了一个名为GreeterService的服务。

service GreeterService {
  // ...
}

一个服务中有各种各样的方法,它们需要声明在Service的内部。比如下面的示例中,GreeterService声明了一个名为sayHello的方法,它以HelloRequest 作为请求参数,以HelloResponse作为回包参数。

service GreeterService {
  rpc sayHello (HelloRequest) returns (HelloResponse) {}
}

message HelloRequest {
  // ...
}

message HelloResponse {
  // ...
}

一个完整的tRPC服务的protobuf示例配置可参考:trpc-java-demo/src/main/proto/demo.proto

编写客户端和服务端代码

protobuf给出的是一个语言无关的服务定义,定义完成后,可以使用 trpc-maven-plugin插件 来将它生成tRPC-Java项目的桩代码。

桩代码的核心是一个包括@TRpcService@TRpcMethod注解的服务接口,如:

// TRpcService用于定义服务名,服务名为trpc开头的四段点分格式的字符串。
@TRpcService(name = "trpc.TestApp.TestServer.GreeterService")
public interface GreeterserviceAPI {

    // TRpcMethod用于定义接口名。
    @TRpcMethod(name = "sayHello")
    HelloRequestProtocol.HelloResponse sayHello(RpcContext context,
            HelloRequestProtocol.HelloRequest request);

}

示例代码可参考trpc-maven-plugin 插件生成的接口:trpc-java-demo/target/generated-sources/trpc/java/com/tencent/trpc/demo/proto/GreeterserviceAPI.java

trpc-maven-plugin会同时生成普通RPC接口或异步RPC接口,任选一个即可

继承生成的服务接口,实现服务端代码:

// 服务端接口实现
public class GreeterServiceImpl implements GreeterserviceAPI {

    @Override
    public HelloRequestProtocol.HelloResponse sayHello(RpcContext context,
            HelloRequestProtocol.HelloRequest request) {
        // do your business
        return something;
    }
}

示例代码可参考:trpc-java-demo/src/main/java/com/tencent/trpc/demo/server/impl/GreeterServiceImpl.java

客户端代码只需要拿到对应服务的服务接口(如GreeterserviceAPIGreeterserviceAsyncAPI)即可,不需要实现。

框架配置

快速开始 中,演示的是通过代码的方式配置tRPC-Java服务,本教程将演示如何通过YAML来配置tRPC-Java服务。

下面的YAML配置示例中,演示了如何在服务端配置tRPC-Java服务:

server:
  app: TestApp            # 应用名
  server: TestServer      # 服务名
  local_ip: 127.0.0.1     # 本地IP地址
  service: # 服务列表,可定义多个
    - name: trpc.TestApp.TestServer.Greeter1  # 服务名,通常用于注册到服务中心
      impls: # 服务实现类,提供具体的RPC服务
        - com.tencent.trpc.demo.server.impl.GreeterServiceImpl
      ip: 127.0.0.1       # 服务监听地址
      port: 12321         # 服务监听端口
      network: tcp        # 网络协议,如tcp或udp
      protocol: trpc      # 通信协议,默认trpc
      serialization: pb   # 序列化方式,默认pb
      transport: netty    # 通信框架,默认netty

示例配置可参考:trpc-java-demo/src/main/resources/trpc_java_server.yaml

下面的YAML配置示例中,演示了如何在客户端配置tRPC-Java服务:

server: # 本地服务信息,主要用于配置RPC调用的主调信息等
  app: TestApp  # 应用名
  server: TestServer  # 服务名
  local_ip: 127.0.0.1 # 本地IP地址

client: # 客户端配置
  service:
    - name: trpc.TestApp.TestServer.Greeter1  # 服务名
      interface: com.tencent.trpc.demo.proto.GreeterserviceAPI  # 服务接口
      naming_url: ip://127.0.0.1:12321  # 远程服务地址,这里指通过ip+port的方式访问
      network: tcp                      # 网络协议,如tcp或udp
      protocol: trpc                    # 通信协议,默认trpc
      serialization: pb                 # 序列化方式,默认pb
      transport: netty                  # 通信框架,默认netty

示例配置参考:trpc-java-demo/src/main/resources/trpc_java_client.yaml

通常一个tRPC-Java服务既可以是服务端,也可以是客户端,上述两种配置可合并定义到同一个配置文件中。

更多的配置项说明可参考 YAML配置文档

服务端YAML配置文件定义好后,可使用如下方式启动服务端:

class Server {

    public static void main(String[] args) {
        TRpcSystemProperties.setProperties(TRpcSystemProperties.CONFIG_PATH, pathToServerYaml);
        Main.main(args);
    }
}
  1. 示例代码可参考:trpc-java-demo/src/main/java/com/tencent/trpc/demo/example/yaml/ServerTest.java
  2. 示例代码执行方式:
    $ mvn exec:java -pl :trpc-java-demo -Dexec.cleanupDaemonThreads=false -Dexec.mainClass=com.tencent.trpc.demo.example.yaml.ServerTest

客户端YAML配置文件定义好后,可使用如下方式启动客户端:

class Client {

    public static void main(String[] args) {
        TRpcSystemProperties.setProperties(TRpcSystemProperties.CONFIG_PATH, pathToServerYaml);
        Main.main(args);

        GreeterserviceAPI proxy = TRpcProxy.getProxy("trpc.TestApp.TestServer.Greeter1",
                GreeterserviceAPI.class);
        // do some test with proxy
    }
}
  1. 示例代码可参考:trpc-java-demo/src/main/java/com/tencent/trpc/demo/example/yaml/ClientTest.java
  2. 示例代码执行方式:
    $ mvn exec:java -pl :trpc-java-demo -Dexec.cleanupDaemonThreads=false -Dexec.mainClass=com.tencent.trpc.demo.example.yaml.ClientTest

多协议支持

上述示例均演示的是普通RPC服务,tRPC-Java除支持普通RPC请求外,还支持流式RPCHTTP RPC等多种协议,部分说明如下文。

流式RPC

流式RPC支持客户端与服务端之间进行更加灵活的交互。它分为如下三种类型:

  • 客户端流式:允许客户端依次发送多个包,服务端全收到后,再返回一个包。它是多对一关系。
  • 服务端流式:允许服务端为一个客户端请求生成多次回包。它是一对多关系。
  • 双向流式:允许客户端和服务端可以并行地给对方发送请求。它是多对多关系。

同一次请求创建的流中,接收方收到消息的顺序与请求方发送的顺序是完全一致的。

普通RPC不同,在protobuf中声明流式RPC需要使用stream关键字,如下例中定义了三种类型的流式接口:

service StreamGreeterService {
  rpc clientSayHellos (stream HelloRequest) returns (HelloResponse);
  rpc serverSayHellos (HelloRequest) returns (stream HelloResponse);
  rpc allSayHellos (stream HelloRequest) returns (stream HelloResponse);
}

注:由于实现差异,流式RPC普通RPC的接口不支持定义在同一个Service中。

tRPC-Java的流式功能基于 Reactor 实现,如上述protobuf里定义的流式接口会分别对应成如下代码中的函数:

@TRpcService(name = "trpc.TestApp.TestServer.Greeter")
public interface StreamGreeterService {

    @TRpcMethod(name = "serverSayHellos")
    Flux<HelloRequestProtocol.HelloResponse> serverSayHellos(RpcContext ctx,
            HelloRequestProtocol.HelloRequest request);

    @TRpcMethod(name = "clientSayHellos")
    Mono<HelloRequestProtocol.HelloResponse> clientSayHellos(RpcContext ctx,
            Publisher<HelloRequestProtocol.HelloRequest> requests);

    @TRpcMethod(name = "allSayHellos")
    Flux<HelloRequestProtocol.HelloResponse> allSayHellos(RpcContext ctx,
            Publisher<HelloRequestProtocol.HelloRequest> requests);
}

由于FluxMono的消息数量要求,从函数接口即可推断出是哪种类型的流式接口。

示例配置可参考:trpc-java-demo/src/main/proto/demo.proto

流式RPC普通RPC的配置类似,详细可参考示例代码:trpc-java-demo/src/main/java/com/tencent/trpc/demo/example/stream

示例代码的启动方式如下:

  • 启动服务端示例代码
    $ mvn exec:java -pl :trpc-java-demo -Dexec.cleanupDaemonThreads=false -Dexec.mainClass=com.tencent.trpc.demo.example.stream.ServerTest
  • 启动客户端示例代码
    $ mvn exec:java -pl :trpc-java-demo -Dexec.cleanupDaemonThreads=false -Dexec.mainClass=com.tencent.trpc.demo.example.stream.ClientTest

将RPC转为HTTP服务

tRPC-Java中,将普通RPC服务转换为HTTP协议,仅需改动YAML配置文件对应service的如下两处:

  1. protocoltrpc(默认值) 改为http
  2. transporternetty(默认值)改为jetty

示例配置可参考:trpc-java-demo/src/main/resources/trpc_java_server.yaml

上述示例配置中trpc.TestApp.TestServer.Greeter3 对应的服务即为HTTP服务。对应服务的默认URL地址为/{@TRpcService.name}/{@TRpcMethod.name} (即将服务注解中的信息拼接起来),如示例trpc.TestApp.TestServer.Greeter3 对应的URL地址为http://127.0.0.1:12322/trpc.TestApp.TestServer.GreeterService/sayHello。 可使用如下命令访问对应服务:

$ curl -XPOST -H"Content-Type: application/json" -d'{"message": "tRPC-Java"}' http://127.0.0.1:12322/trpc.TestApp.TestServer.GreeterService/sayHello

除了基础的HTTP访问方式外,tRPC-Java还支持在客户端YAML配置中配置远程HTTP协议的tRPC服务,示例配置可参考:trpc-java-demo/src/main/resources/trpc_java_client.yaml

如需要更改默认的URL地址,可以在protobuf中声明服务时,加上下面的扩展:

import "trpc.proto";

service GreeterService2 {
  rpc sayHi (HelloRequest) returns (HelloResponse) {
    option (trpc.alias) = "/api/hi";
  }
}

示例配置可参考:trpc-java-demo/src/main/proto/demo.proto

修改后的URL访问方式如:

$ curl -XPOST -H"Content-Type: application/json" -d'{"message": "tRPC-Java"}' http://127.0.0.1:12322/api/hi

Spring Boot集成与HTTP

tRPC-Java与Spring Boot等Spring生态有良好的支持。在Spring Boot中,tRPC-JAVA原本的YAML配置文件可以改成Spring Boot中的application.yml来配置,示例配置可参考:trpc-spring-server-demo/src/main/resources/application.yml

配置文件配置好后,仅需在Spring Boot的启动类中,添加@EnableTRpc注解即可,无需再像trpc-java-demo中手动的方式启动tRPC-Java

@EnableTRpc
@SpringBootApplication
public class ServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServerApplication.class, args);
    }
}

示例代码可参考:trpc-spring-server-demo/src/main/com/tencent/trpc/spring/demo/server/ServerApplication.java

如果希望tRPC-Java与SpringMvc集成,则需在pom.xml中引入如下依赖:

<dependency>
    <groupId>com.tencent.trpc</groupId>
    <artifactId>trpc-springmvc</artifactId>
    <version>${tRPC-Java.version}</version>
</dependency>

之后再继续编写正常的SpringMvc Controller或tRPC-Java服务即可。

示例代码参考:trpc-spring-server-demo/src/main/com/tencent/trpc/spring/demo/server/controller/

由于示例代码未配置Spring Boot插件,可按如下的方式启动对应示例代码:

  • 启动服务端示例代码
    $ mvn exec:java -pl :trpc-spring-server-demo -Dexec.cleanupDaemonThreads=false -Dexec.mainClass=com.tencent.trpc.spring.demo.ServerApplication
  • 启动客户端示例代码
    $ mvn exec:java -pl :trpc-spring-client-demo -Dexec.cleanupDaemonThreads=false -Dexec.mainClass=com.tencent.trpc.spring.demo.ClientApplication

正常的Spring Boot程序直接打成单独的Jar包执行即可。

拦截器和插件

tRPC-Java有着丰富的可扩展性,拦截器可以在请求执行的流程中注入各种新能力,插件则能更方便地集成新功能。

拦截器

拦截器 像一棵洋葱,当tRPC-Java处理请求时,会依次经过洋葱的每一层。

如下例所示,实现一个拦截器,需继承并实现Filter接口:

public class DemoFilter implements Filter {

    @Override
    public CompletionStage<Response> filter(Invoker<?> filterChain, Request req) {
        try {
            System.out.println("enter filter");
            return filterChain.invoke(req)
                    .whenComplete((r, t) -> System.out.println("filter completed"));
        } finally {
            System.out.println("exit filter");
        }
    }
}

示例代码参考:trpc-java-demo/src/main/com/tencent/trpc/demo/filter/DemoFilter.java

拦截器代码写完后,需通过META-INF/trpc/com.tencent.trpc.core.filter.spi.Filter文件来命名并注册拦截器:

demoFilter=com.tencent.trpc.demo.filter.DemoFilter

拦截器注册好之后,即可在YAML配置文件中声明使用:

client:
  filters: # 指定客户端全局拦截器
    - filter1
    - filter2
  service:
    - name: xxx
      filters: # 指定单个客户端特有的拦截器,它们会追加在全局拦截器之后
        - filter3
server:
  filters: # 指定服务端全局拦截器
    - filter1
    - filter2
  service:
    - name: yyy
      filters: # 指定单个服务端特有的拦截器,它们会追加在全局拦截器之后
        - filter3

示例配置参考:trpc-java-demo/src/main/resources/trpc_java_server.yaml

插件

tRPC-Java支持类似Java SPI的插件扩展机制,具体代码可参考 插件

需要支持插件能力的接口,可通过@Extensible注解标注,如:

@Extensible
public interface WorkerPool {
    // ...
}

当实现插件时,可通过@Extension注解标注,如:

@Extension("thread")
public class ThreadWorkerPool implements WorkerPool {
    // ...
}

编写好的插件,需要采用类似SPI的机制,在META-INF/trpc/目录下编写对应插件的查找文件,具体可参考前文拦截器处的示例

当需要使用插件时,可通过ExtensionLoader类获取即可:

    WorkerPool workerPool=ExtensionLoader.getExtensionLoader(WorkerPool.class).getExtension("thread");
// do with workerPool

下一步