Go 微服务框架 go-micro 使用客户端 RPC 调用服务端方法返回 408 怎么解决?

开发 前端
本文我们介绍怎么解决客户端 RPC 调用服务端的方法,返回错误码 408 的问题,我们使用 go-micro 和 gin 构建了示例代码,并通过修改示例代码,分析出现返回错误码 408 问题的原因。

1、介绍

本文我们使用 go-micro 构建微服务的服务端和客户端,并使用 gin 集成客户端构建 HTTP Api,在代码中模拟客户端 RPC 调用服务端方法返回 408 的问题,以及怎么解决?

客户端输出日志:

{"id":"go.micro.client","code":408,"detail":"context deadline exceeded","status":"Request Timeout"}

2、使用 go-micro 构建服务端和客户端

关于 proto 和 consul 的相关内容,不是本文的重点,将不再赘述,感兴趣的读者朋友们可以查阅公众号的历史文章。

创建服务端服务的代码:

func main() {
registry := consul.NewRegistry(func(options *registry.Options) {
options.Addrs = []string{"127.0.0.1:8500"}
options.Timeout = 5 * time.Second
})
// create a new service
service := grpc.NewService(
micro.Name("go.micro.srv.user"),
micro.Registry(registry),
)
// handler
user.RegisterUserHandler(service.Server(), new(user_handler.User))
// initialise flags
service.Init()
// start the service
service.Run()
}

服务端方法:

type User struct{}

func (u *User) Login(ctx context.Context, req *user.LoginRequest, rsp *user.LoginResponse) error {
time.Sleep(10 * time.Second) // 模拟超时响应
rsp.Username = "Welcome " + req.Email
return nil
}

客户端代码:

func main() {
r := NewRouter()
server := &http.Server{
Addr: ":8080",
Handler: r,
ReadTimeout: time.Second * 20,
WriteTimeout: time.Second * 20,
MaxHeaderBytes: 1 << 20,
}
if err := server.ListenAndServe(); err != nil {
log.Fatal(err)
}
}

// 省略 User.Login 相关代码

func NewRouter() *gin.Engine {
r := gin.New()
userHandler := new(User)
r.GET("/login", userHandler.Login)
return r
}

func NewClient() user.UserService {
registry := consul.NewRegistry(func(options *registry.Options) {
options.Addrs = []string{"127.0.0.1:8500"}
options.Timeout = 5 * time.Second
})
client := grpc.NewClient(
client.DialTimeout(15*time.Second),
client.RequestTimeout(15*time.Second),
client.Registry(registry),
)
userClient := user.NewUserService("go.micro.srv.user", client)
return userClient
}

分别启动服务端和客户端, 然后使用 curl 请求 Api:

/Users/frank curl http://127.0.0.1:8080/login
{"data":"Welcome gopher@88.com"}%

阅读上面的运行结果,可以发现我们构建的服务端和客户端运行正常。

3、模拟返回 408 的问题

我们在服务端的方法中使用 time.Sleep(10 * time.Second) 模拟延长响应时长,我们修改客户端代码,将客户端超时时间也设置为 10s。

修改客户端代码:

client.DialTimeout(10*time.Second),
client.RequestTimeout(10*time.Second),

阅读上面这段代码,我们将客户端超时时间改为 10s,然后重启客户端应用,使用 curl 请求 Api:

/Users/frank curl http://127.0.0.1:8080/login

运行 curl,没有返回响应结果,我们查看客户端的日志发现:

{"id":"go.micro.client","code":408,"detail":"context deadline exceeded","status":"Request Timeout"}

原因是服务端方法中,我们在代码中使用 time.Sleep(10 * time.Second) 模拟响应需要 10s,而在客户端中,我们定义的客户端超时时间由原来的 15s 改为 10s,所以导致返回 408 的问题。

需要注意的是,go-micro 中 client 的默认超时时间是 5s。

4、解决方法

我们在了解完问题出现的原因之后,聪明的读者朋友们可能已经有了解决问题的方法。

解决该问题,有两种解决方法,第一种是修改 client 的超时时间,延长超时时间至足够接收到响应结果的时长,但是需要注意的是,http server 的读写时间也要满足可以接收到响应结果的时长,本文我们设置为 20s,如下所示:

server := &http.Server{
Addr: ":8080",
Handler: r,
ReadTimeout: time.Second * 20,
WriteTimeout: time.Second * 20,
MaxHeaderBytes: 1 << 20,
}

而且还需要注意其上下游服务之间的超时时间,避免出现雪崩等问题。

第二种是优化服务端方法的响应时间,将其响应时间缩短至客户端的超时时间以内。具体如何优化,要根据实际情况决定,比如是否因为数据库读写耗时太长,代码的时间复杂度太高等。

5、总结

本文我们介绍怎么解决客户端 RPC 调用服务端的方法,返回错误码 408 的问题,我们使用 go-micro 和 gin 构建了示例代码,并通过修改示例代码,分析出现返回错误码 408 问题的原因。

读者朋友们在遇到该问题时,建议优先采用第二种方法解决,如果使用第一种方法,需要特别注意避免分布式系统的雪崩问题。

责任编辑:武晓燕 来源: Golang语言开发栈
相关推荐

2021-07-26 11:19:43

微服务开发技术

2021-10-19 08:58:48

Java 语言 Java 基础

2024-01-02 12:17:44

Go传统远程

2024-03-06 14:58:52

客户端微服务架构

2015-01-13 10:32:23

RestfulWeb框架

2023-04-03 08:13:05

MySQLCtrl + C

2011-09-09 09:44:23

WCF

2009-08-21 16:14:52

服务端与客户端通信

2009-08-21 15:59:22

服务端与客户端通信

2009-08-21 15:54:40

服务端与客户端

2009-08-21 15:36:41

服务端与客户端

2021-10-14 08:39:17

Java Netty Java 基础

2010-03-18 17:47:07

Java 多客户端通信

2023-03-06 08:01:56

MySQLCtrl + C

2022-03-22 09:22:21

Go kitgRPC网络传输

2010-11-19 14:22:04

oracle服务端

2021-01-14 09:55:21

Java微服务Go

2021-09-06 11:34:47

架构微服务Hystrix

2021-03-05 11:09:46

Go框架微服务

2023-10-30 09:06:22

点赞
收藏

51CTO技术栈公众号