trpc.group/trpc-go/trpc-go@v1.0.3/docs/basics_tutorial.md (about)

     1  English | [中文](./basics_tutorial.zh_CN.md)
     2  
     3  ## Basics Tutorial
     4  
     5  In [Quick Start](./quick_start.md), you have successfully run tRPC-Go helloworld. However, we ignore many details. In this chapter, you will understand the tRPC-Go service development process in more detail. We will introduce in turn:
     6  - How to define tRPC service by protobuf?
     7  - How to configure `trpc_go.yaml`?
     8  - What extension capabilities does tRPC-Go have?
     9  - Various features supported by tRPC-Go.
    10  
    11  Our service definition depends on Protocol Buffer v3. You can refer to [Official Documents of Golang Protobuf](https://protobuf.dev/getting-started/gotutorial/).
    12  
    13  ### Define Service
    14  
    15  To define a new service, we need to declare it in protobuf first. The following example defines a service named `MyService`:
    16  ```protobuf
    17  service MyService {
    18    // ...
    19  }
    20  ```
    21  
    22  A service may have many methods which are defined inside service body. In following example, we define a method `Hello` for service `Greeter`. The `Hello` use `HelloReq` as its parameter and returns `HelloRsp`.
    23  ```protobuf
    24  service Greeter {
    25    rpc Hello(HelloReq) returns (HelloRsp) {}
    26    // ...
    27  }
    28  
    29  message HelloReq {
    30    // ...
    31  }
    32  
    33  message HelloRsp {
    34    // ...
    35  }
    36  ```
    37  
    38  Note that `Method` has a `{}` at the end, which can also have content. We will see later.
    39  
    40  ### Write Client and Server Code
    41  
    42  What protobuf gives is a language-independent service definition, and we need to use [trpc command line tool](https://github.com/trpc-group/trpc-cmdline) to translate it into a corresponding language stub code. You can see the various options it supports with `$ trpc create -h`. You can refer to the quick start [helloworld](/examples/helloworld/pb/Makefile) project to quickly create your own stub code.
    43  
    44  The stub code is mainly divided into two parts: client and server.  
    45  Below is part of the generated client code. In [Quick Start](./quick_start.md), we use `NewGreeterClientProxy` to create a client instance and call its `Hello` method:
    46  ```go
    47  type GreeterClientProxy interface {
    48      Hello(ctx context.Context, req *HelloReq, opts ...client.Option) (rsp *HelloRsp, err error)
    49  }
    50  
    51  var NewGreeterClientProxy = func(opts ...client.Option) GreeterClientProxy {
    52      return &GreeterClientProxyImpl{client: client.DefaultClient, opts: opts}
    53  }
    54  ```
    55  
    56  The following is part of the generated server code, `GreeterService` defines the interface you need to implement. `RegisterGreeterService` will register your implementation to the framework. In [Quick Start](./quick_start.md), we first create a tRPC-Go instance through `s := trpc.NewServer()`, and then register the `Greeter` structure that implements the business logic to `s`.
    57  ```go
    58  type GreeterService interface {
    59      Hello(ctx context.Context, req *HelloReq) (*HelloRsp, error)
    60  }
    61  
    62  func RegisterGreeterService(s server.Service, svr GreeterService) { /* ... */ }
    63  ```
    64  
    65  ### Configuration
    66  
    67  Maybe you have noticed a little difference between client and server. On the client side, we specified the address of the server through `client.WithTarget`, but on the server side, we did not find the corresponding address in the code. In fact, it is configured in `./server/trpc_go.yaml`.  
    68  This is the yaml configuration capability supported by tRPC-Go. Almost all tRPC-Go framework capabilities can be customized through file configuration. When you execute tRPC-Go, the framework will look for the `trpc_go.yaml` file in the current directory and load the relevant configuration. This allows you to change the behavior of your application without recompiling the service.  
    69  Below are some necessary configurations required for this tutorial, please refer to [Framework Configuration](/docs/user_guide/framework_conf.md) for complete configurations.
    70  ```yaml
    71  server:
    72    service:  # you can config multiple services
    73      - name: helloworld
    74        ip: 127.0.0.1
    75        port: 8000
    76        protocol: trpc
    77  ```
    78  
    79  ### Filter and Plugin
    80  
    81  tRPC-Go has rich scalability, you can inject various new capabilities into the RPC process through filters, and the plugin factory allows you to easily integrate new functions.
    82  
    83  #### Filter
    84  
    85  [Filter](/filter) likes an onion. An RPC goes through each layer of the onion in turn. You can customize this onion model through Filters.
    86  
    87  The client filter is defined as follows:
    88  ```go
    89  type ClientFilter func(ctx context.Context, req, rsp interface{}, next ClientHandleFunc) error
    90  type ClientHandleFunc func(ctx context.Context, req, rsp interface{}) error
    91  ```
    92  When implement your own filters:
    93  ```go
    94  func MyFilter(ctx context.Context, req, rsp interface{}, next ClientHandleFunc) error {
    95      // pre-RPC processes
    96      err := next(ctx, req, rsp)
    97      // post-RPC processes
    98      return err
    99  }
   100  ```
   101  Codes before and after `next` will be executed before and after the actual RPC, that is, pre-RPC processes and post-RPC processes. You can implement many filters, which are used when calling [`client.WithFilters`](/client/options.go). Framework Will automatically concat these filter into a chain.
   102  
   103  The signature of server filter is slightly different from client:
   104  ```go
   105  type ServerFilter func(ctx context.Context, req interface{}, next ServerHandleFunc) (rsp interface{}, err error)
   106  type ServerHandleFunc func(ctx context.Context, req interface{}) (rsp interface{}, err error)
   107  ```
   108  `rsp` is in the return value, not the argument. Server filter should be injected into the framework by [`server.WithFilters`](/server/options.go). The framework will automatically concat these filters into a chain.
   109  
   110  In addition to adding filters directly through code mentioned above, you can also load filters through configuration files.
   111  ```yaml
   112  client:
   113    filter:  # these are client global filters
   114      - client_filter_name_1
   115      - client_filter_name_2
   116    service:
   117      - name: xxx
   118        filter:  # these are special filters for service xxx, they will be appended to global filters.
   119          - client_filter_name_3
   120  server:
   121    filter:  # these are server global filters
   122      - server_filter_name_1
   123      - server_filter_name_2
   124    service:
   125      - name: yyy
   126        filter:  # these are special filters for service yyy, they will be appended to global filters.
   127          - server_filter_name_3
   128  ```
   129  These filters need to be registered in the framework in advance via [`filter.Register`](/filter/filter.go). They are automatically loaded by the framework when `trpc.NewServer` is executed.  
   130  Note that when the code and the configuration file exist at the same time, the interceptor specified by the code will be executed first, and then the interceptor specified by the configuration file.
   131  
   132  You can see an example of filter usage [here](/examples/features/filter).
   133  
   134  #### Plugin
   135  
   136  [Plugin](/plugin) is an automatic module loading mechanism designed by tRPC-Go based on yaml configuration file. Its interface is defined as follows:
   137  ```go
   138  package plugin
   139  
   140  type Factory interface {
   141      Type() string
   142      Setup(name string, dec Decoder) error
   143  }
   144  
   145  type Decoder interface {
   146      Decode(cfg interface{}) error
   147  }
   148  ```
   149  `Type` returns the type of the plugin, and `Setup` will pass in the plugin name and a `Decoder` for parsing the content of yaml. The content of plugin is configured in `trpc_go.yaml`:
   150  ```yaml
   151  plugins:
   152    __type:
   153      __name:
   154        # plugin contents
   155  ```
   156  where `__type` should be replaced by the value returned by `Factory.Type()` and `__name` should be replaced by the first parameter of `plugin.Register`.
   157  
   158  When implementing a `plugin`, you should create a `func init()` function to register your plugin via `Register`. In this way, when others use your plugin, they only need to import your package anonymously in the code. When `trpc.NewServer()` is called, the plugin will call the `Factory.Setup` function for initialization.
   159  
   160  Plugins often cooperate with filters, such as calling `filter.Register` in the `Factory.Setup` function to register a filter. The framework guarantees that plugin initialization completes before filters are loaded. This way, you can configure the behavior of the filter by modifying plugin of `trpc_go.yaml`.
   161  
   162  ### Other Supported Protocols
   163  
   164  [Quick start](./quick_start.md) introduces a common one-request-one-response RPC. tRPC-Go also supports streaming RPC, HTTP, and more.
   165  
   166  #### streaming RPC
   167  
   168  Streaming RPC supports more flexible interactions between clients and servers. It can be divided into three modes: client-side streaming, server-side streaming, and bidirectional streaming.
   169  Client streaming allows the client to send multiple packets in sequence, and the server returns a packet after receiving all of them. It is a many-to-one relationship.  
   170  Server-side streaming allows the server to generate multiple responses for a client request. It is a one-to-many relationship.  
   171  Bidirectional streaming allows the client and the server to send requests to each other in parallel, in order, just like two people in a conversation. It is a many-to-many relationship.
   172  
   173  The code in this section is based on [`example/stream`](/examples/features/stream).
   174  
   175  Unlike normal RPCs, declaring streaming RPCs in protobuf requires the use of the `stream` keyword.
   176  ```protobuf
   177  service TestStream {
   178    rpc ClientStream (stream HelloReq) returns (HelloRsp);
   179    rpc ServerStream (HelloReq) returns (stream HelloRsp);
   180    rpc BidirectionalStream (stream HelloReq) returns (stream HelloRsp);
   181  }
   182  ```
   183  When `stream` only appears before the method request, the method is client-side streaming; when `stream` only appears before the method response, the method is server-side streaming; when `steam` appears both before the method request and the method response, the method is bidirectional.
   184  
   185  The stub code generated by streaming is very different from normal RPC. Take client streaming as an example:
   186  ```go
   187  type TestStreamService interface {
   188      ClientStream(TestStream_ClientStreamServer) error
   189      // ...
   190  }
   191  
   192  type TestStream_ClientStreamServer interface {
   193      SendAndClose(*HelloRsp) error
   194      Recv() (*HelloReq, error)
   195      server.Stream
   196  }
   197  
   198  type TestStreamClientProxy interface {
   199      ClientStream(ctx context.Context, opts ...client.Option) (TestStream_ClientStreamClient, error)
   200  }
   201  
   202  type TestStream_ClientStreamClient interface {
   203      Send(*HelloReq) error
   204      CloseAndRecv() (*HelloRsp, error)
   205      client.ClientStream
   206  }
   207  ```
   208  From the stub code above, you can probably guess how the business code is written. The client sends requests multiple times by `TestStream_ClientStreamClient.Send`, and finally closes the stream by `TestStream_ClientStreamClient.CloseAndRecv` and waits for the response. The server receives the client's streaming requests by `TestStream_ClientStreamServer.Recv`, if it returns `io.EOF`, it means that the client has called `CloseAndRecv` and is waiting for the return packet, and it will finally send response by `TestStream_ClientStreamServer.SendAndClose` and confirm to close the stream. Note that the client streaming termination is initialized by the client. `CloseAndRecv` indicates that the client closes first, and then waits for a response. `SendAndClose` indicates that the server sends a response first, and then confirm close.
   209  
   210  Server-side streaming is the opposite of client-side streaming. As soon as the `TestStreamService.ServerStream` function exits, it means that the server has finished sending responses. The client obtains the stream responses by calling `TestStream_ServerStreamClient.Recv` multiple times. When the `io.EOF` error is received, it indicates that the server has completed the stream responses.
   211  
   212  Bidirectional streaming is a combination of the previous two. Their sending and reading can be interleaved, just like two people talking, and more complex interaction logic can be realized.
   213  
   214  The configuration of streaming RPC is no different from normal RPC.
   215  
   216  #### Standard HTTP Service
   217  
   218  tRPC-Go supports registering HTTP services from the Go standard library into the framework. Suppose that you need to listen an HTTP service on port 8080, first, add the following service configuration to `trpc_go.yaml`:
   219  ```yaml
   220  server:
   221    service:
   222      - name: std_http
   223        ip: 127.0.0.1
   224        port: 8080
   225        protocol: http
   226  ```
   227  Then add following code:
   228  ```go
   229  import thttp trpc.group/trpc-go/trpc-go/http
   230  
   231  func main() {
   232      s := trpc.NewServer()
   233      thttp.RegisterNoProtocolServiceMux(s.Service("std_http"), your_http_handler) 
   234      log.Info(s.Serve())
   235  }
   236  ```
   237  
   238  Note that unlike ordinary RPC, the `protocol` field in yaml needs to be changed to `http`. The first parameter of the `thttp.RegisterNoProtocolServiceMux` method needs to specify the service name in yaml, namely `s.Service("std_http")`.
   239  
   240  #### Translate RPC to HTTP Quickly
   241  
   242  In tRPC-Go, changing `server.service[i].protocol` in `trpc_go.yaml` from `trpc` to `http` can convert the service of ordinary tRPC to HTTP. When calling, the HTTP url corresponds to the [method name](/examples/helloworld/pb/helloworld.trpc.go).
   243  
   244  For example, if you change the RPC in [Quick Start](./quick_start.md) to HTTP, you need to use the following curl command to call it:
   245  ```bash
   246  $ curl -XPOST -H"Content-Type: application/json" -d'{"msg": "world"}' 127.0.0.1:8000/trpc.helloworld.Greeter/Hello
   247  {"msg":"Hello world!"}
   248  ```
   249  
   250  If you need to change the HTTP url, you can add the following extension when declaring the service in protobuf:
   251  ```protobuf
   252  import "trpc/proto/trpc_options.proto";
   253  
   254  service Greeter {
   255    rpc Hello (HelloRequest) returns (HelloReply) {
   256      option (trpc.alias) = "/alias/hello";
   257    }
   258  }
   259  ```
   260  The curl command is as follows:
   261  ```bash
   262  $ curl -XPOST -H"Content-Type: application/json" -d'{"msg": "world"}' 127.0.0.1:8000/alias/hello
   263  {"msg":"Hello world!"}
   264  ```
   265  
   266  #### RESTful HTTP
   267  
   268  Although it is convenient to quickly convert RPC to HTTP, it is impossible to customize the mapping relationship between HTTP url/parameter/body and RPC Request. RESTful provides a more flexible RPC to HTTP conversion.
   269  
   270  You can refer to [RESTful example](/examples/features/restful) to get start quickly. For more details, please refer to the [documentation](/restful) of RESTful.
   271  
   272  ### Further Reading
   273  
   274  tRPC-Go also supports other rich features, you can check their [documentations](/docs/user_guide) for more details.
   275