Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 83 additions & 46 deletions examples/features/timeout/README.md
Original file line number Diff line number Diff line change
@@ -1,49 +1,94 @@
# Timeout

The following are some brief introductions and usage examples of trpc-go timeout feature. You can understand how the timeout mechanism of trpc-go works from these examples.

## Usage

Steps to use the feature. Typically:

* start the server
```

```shell
cd server && go build -v && ./server
```

* start the client
* open another terminal, and start the forward server


open another terminal.
```shell
cd forwardserver && go build -v && ./forwardserver
```


* open another terminal, start the client


```shell
cd client && go build -v && ./client
```
In the demo, there are two RPC calls, SayHello and SayHi.
You have set different client timeout values for the TestSayHello and TestSayHi interfaces. The client timeout value for TestSayHi is 1000ms.

```go
opts := []client.Option{
client.WithTarget(addr),
client.WithTimeout(time.Millisecond * 1000),
}
````

The TestSayHello interface will call the SayHi RPC. You have set the timeout value for this call to 2000ms.
```go
opts := []client.Option{
client.WithTarget(addr),
client.WithTimeout(time.Millisecond * 2000),
}
```

In the SayHi method of the server, you have set a sleep time of 1100ms for the thread.
The above example shows the "ForwardServer Full-Link Timeout".
For more timeout scenarios, see the table in the next section.
You can modify the timeout configuration to simulate other timeout scenarios.


## Timeout Scenarios Explanation

### Call Chain
```
time.Sleep(time.Millisecond * 1100ms)
Client -> ForwardServer -> Server
```

When executing `./client`, you found that the TestSayHi interface timed out, while the TestSayHello interface returned normally.

| Timeout Scenario | Client Timeout | ForwardServer Timeout | ForwardServer Sleep | ForwardServer→Server Timeout | Server Timeout | Server Sleep | ForwardServer Error | Client Error |
|:----------------|:--------------:|:--------------------:|:------------------:|:---------------------------:|:--------------:|:------------:|:-------------------|:-------------|
| No Timeout | 4 | 5 | 1 | 3 | 2 | 1 | nil | nil |
| Silent Server Timeout | 4 | 5 | 1 | 3 | 1 | 2 | nil | nil |
| ForwardServer Normal Timeout | 4 | 2 | 3 | 3 | 2 | 1 | RetClientFullLinkTimeout | RetServerTimeout |
| Client Full-Link Timeout | 3 | 5 | 4 | 3 | 2 | 1 | RetClientFullLinkTimeout | RetClientFullLinkTimeout |
| ForwardServer→Server Client Timeout | 4 | 5 | 1 | 1 | 3 | 2 | RetClientTimeout | RetClientTimeout |
| ForwardServer Full-Link Timeout | 4 | 5 | 6 | 3 | 2 | 1 | RetClientTimeout | RetClientFullLinkTimeout |

1. **Normal Case (No Timeout)**
- All services complete within their timeout limits
- All timeouts: Client(4s) -> ForwardServer(5s) -> Server(2s)
- No errors returned

2. **Silent Server Timeout**
- Server sleeps(2s) longer than its timeout(1s)
- But since Server doesn't actively handle timeout, no error is propagated
- Both Client and ForwardServer remain unaware of the timeout

3. **Client Full-Link Timeout**
- Client timeout(3s) < ForwardServer processing time(4s)
- Results in full-link timeout propagation
- Both services receive RetClientFullLinkTimeout

## timeout mechanism in trpc-go
4. **ForwardServer->Server Client Timeout**
- ForwardServer->Server timeout(1s) < Server processing time(2s)
- Results in simple client timeout
- Both receive RetClientTimeout

5. **ForwardServer Normal Timeout**
- ForwardServer timeout(2s) < processing time(3s)
- Client receives RetServerTimeout
- ForwardServer receives RetClientFullLinkTimeout

6. **ForwardServer Full-Link Timeout**
- ForwardServer processing time(6s) exceeds all timeouts
- ForwardServer detects timeout but doesn't send response as Client already abandoned request
- ForwardServer reports RetServerFullLinkTimeout to monitoring system
- Results in RetClientTimeout and RetClientFullLinkTimeout


### Note
All times are in seconds, and errors indicate where in the chain the timeout occurred and how it propagated through the system.

## timeout mechanism in trpc-go

The timeout mechanism of trpc-go is as follows:

```
```raw
+------------------+-----------------------+
| server B | single timeout |
| | +------------> |
Expand Down Expand Up @@ -74,16 +119,15 @@ The timeout mechanism of trpc-go is as follows:

```

* Client configuration

- Client configuration

- The total timeout time of the downstream link
* The total timeout time of the downstream link

When the client initiates a request, it needs to specify the timeout period reserved for the downstream in the business agreement. After the timeout period is exceeded, the request will be canceled to avoid invalid waiting.

The total timeout time of the downstream link is configured as follows, timeout: 1000 means that the maximum processing time of all backend requests invoked by the client is 1000ms
```

```yaml
client: # Backend configuration for client calls.
timeout: 1000 # The total timeout time of the downstream link, the longest request processing time for all backends.
namespace: development # Environments for all backends.
Expand All @@ -95,21 +139,18 @@ The timeout mechanism of trpc-go is as follows:
timeout: 800 # Maximum request processing time.
```

- Single service timeout
* Single service timeout

The client may request multiple backend services at the same time. You can set the timeout period of the client call for each backend service separately. For example, the timeout: 800 configured under service above means that the timeout period for a single backend service is 800ms
- server configuration

* server configuration

A server can provide one or more service services, and supports setting the timeout period for each service. As follows, timeout: 1000 means that the server processing time of trpc.test.helloworld.Greeter service is up to 1000ms, and if it exceeds 1000ms, it will return a timeout.
```

```yaml
server: # server configuration.
app: test # Business application name.
server: Greeter # process service name.
bin_path: /usr/local/trpc/bin/ # The path where the binary executable and framework configuration files are located.
conf_path: /usr/local/trpc/conf/ # The path where the business configuration file is located.
data_path: /usr/local/trpc/data/ # The path where the business data file is located.
service: # The service provided by the business service can have multiple.
- name: trpc.test.helloworld.Greeter # service route name.
ip: 127.0.0.1 # The service listens to the ip address. You can use the placeholder ${ip}, choose one of ip and nic, and give priority to ip.
Expand All @@ -119,13 +160,9 @@ The timeout mechanism of trpc-go is as follows:
timeout: 1000 # Request maximum processing time unit milliseconds.
idletime: 300000 # Connection idle time unit milliseconds.
```
- specified in the code

* specified in the code

It supports setting the timeout period in the code. In this example, the client timeout period is set to 1000ms through the `client.WithTimeout(time.Millisecond * 1000)` method.

It is worth noting that the priority of code specification > the configuration file, set the timeout in the configuration file and the code at the same time, and finally adopt the configuration of the code specification, that is, the configuration takes precedence.




62 changes: 14 additions & 48 deletions examples/features/timeout/client/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,62 +16,28 @@ package main

import (
"context"
"fmt"
"time"

"trpc.group/trpc-go/trpc-go"
"trpc.group/trpc-go/trpc-go/client"
"trpc.group/trpc-go/trpc-go/examples/features/timeout/shared"
pb "trpc.group/trpc-go/trpc-go/testdata/trpc/helloworld"
pb "trpc.group/trpc-go/trpc-go/examples/features/timeout/proto/chat"
"trpc.group/trpc-go/trpc-go/log"
)

func main() {
fmt.Println("== testSayHello begin ==")
testSayHello()
fmt.Println("== testSayHello end ==")

fmt.Println("== testSayHi begin ==")
testSayHi()
fmt.Println("== testSayHi end ==")
sayHi(4 * time.Second)
}

// testSayHello is the test cases for SayHello method.
func testSayHello() {
ctx, cancel := context.WithTimeout(context.TODO(), time.Millisecond*2000)
func sayHi(timeout time.Duration) {
ctx, cancel := context.WithTimeout(trpc.BackgroundContext(), timeout)
defer cancel()

opts := []client.Option{
client.WithTarget(shared.Addr),
// Setting the timeout value for this call to 2000ms.
client.WithTimeout(time.Millisecond * 2000),
}

clientProxy := pb.NewGreeterClientProxy(opts...)

req := &pb.HelloRequest{
Msg: "trpc-go-client",
}
rsp, err := clientProxy.SayHello(ctx, req)
fmt.Println(rsp, err)
}

// testSayHi is the test cases for method.
func testSayHi() {
ctx, cancel := context.WithTimeout(context.TODO(), time.Millisecond*2000)
defer cancel()

opts := []client.Option{
client.WithTarget(shared.Addr),
// Setting the timeout value for this call to 1000ms.
client.WithTimeout(time.Millisecond * 1000),
}

clientProxy := pb.NewGreeterClientProxy(opts...)

req := &pb.HelloRequest{
Msg: "trpc-go-client",
c := pb.NewChatClientProxy(client.WithTarget("ip://127.0.0.1:8001"), client.WithTimeout(timeout))
rsp, err := c.UnarySayHi(ctx, &pb.SayHiRequest{
Message: "trpc-go-client",
})
if err != nil {
log.Error(err)
} else {
log.Info("rsp message: %s", rsp.Message)
}
// This rpc calling would timeout.
rsp, err := clientProxy.SayHi(ctx, req)
// Would print timeout error.
fmt.Println(rsp, err)
}
51 changes: 51 additions & 0 deletions examples/features/timeout/forwardserver/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
//
// Tencent is pleased to support the open source community by making tRPC available.
//
// Copyright (C) 2023 Tencent.
// All rights reserved.
//
// If you have downloaded a copy of the tRPC source code from Tencent,
// please note that tRPC source code is licensed under the Apache 2.0 License,
// A copy of the Apache 2.0 License is included in this file.
//
//

// Package main is the main package.
package main

import (
"context"
"time"

"trpc.group/trpc-go/trpc-go"
pb "trpc.group/trpc-go/trpc-go/examples/features/timeout/proto/chat"
"trpc.group/trpc-go/trpc-go/log"
)

//go:generate trpc create -p ../proto/chat/chat.proto --api-version 2 --rpconly -o ../proto/chat --protodir .. --mock=false --nogomod

func main() {
s := trpc.NewServer()
pb.RegisterChatService(s.Service("trpc.examples.timeout.forward-chat"), &chat{
client: pb.NewChatClientProxy(),
})
if err := s.Serve(); err != nil {
log.Error(err)
}
}

// timeoutServerImpl implements service.
type chat struct {
client pb.ChatClientProxy
}

func (c *chat) UnarySayHi(ctx context.Context, req *pb.SayHiRequest) (*pb.SayHiResponse, error) {
time.Sleep(6 * time.Second)
rsp, err := c.client.UnarySayHi(ctx, req)
if err != nil {
log.Error(err)
return nil, err
}
return &pb.SayHiResponse{Message: "SayHi: " + rsp.Message}, nil
}
22 changes: 22 additions & 0 deletions examples/features/timeout/forwardserver/trpc_go.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
global: # global config.
namespace: Development # environment type, two types: production and development.
env_name: test # environment name, names of multiple environments in informal settings.

server: # server configuration.
app: examples # business application name.
server: timeout # service process name.
service: # business service configuration, can have multiple.
- name: trpc.examples.timeout.forward-chat # the route name of the service.
ip: 127.0.0.1 # the service listening ip address, can use the placeholder ${ip}, choose one of ip and nic, priority ip.
port: 8001 # the service listening port, can use the placeholder ${port}.
network: tcp # the service listening network type, tcp or udp.
protocol: trpc # application layer protocol, trpc or http.
timeout: 5000 # maximum request processing time in milliseconds.
client:
service:
- name: trpc.examples.timeout.chat
callee: trpc.examples.chat.Chat
target: ip://127.0.0.1:8002
timeout: 3000
network: tcp
protocol: trpc
Loading
Loading