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

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

介绍

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

客户端输出日志:

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

使用 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"}%

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

03

模拟返回 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

04

解决方法

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

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

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

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

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

05

总结

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

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

推荐阅读:

参考资料:

  1. https://github.com/go-micro/go-micro
  2. https://gin-gonic.com/docs/


目录
相关文章
|
2月前
|
监控 算法 NoSQL
Go 微服务限流与熔断最佳实践:滑动窗口、令牌桶与自适应阈值
🌟蒋星熠Jaxonic:Go微服务限流熔断实践者。分享基于滑动窗口、令牌桶与自适应阈值的智能防护体系,助力高并发系统稳定运行。
Go 微服务限流与熔断最佳实践:滑动窗口、令牌桶与自适应阈值
|
4月前
|
JSON 自然语言处理 API
gRPC凭什么成为微服务通信首选?深度解析RPC进化史
本文深入解析了分布式系统中服务通信的核心机制,重点介绍了 RPC 与 gRPC 的原理、优势及使用场景,并详解 gRPC 所依赖的序列化协议 Protocol Buffers(Protobuf)。内容涵盖 RPC 概念、gRPC 特性、Protobuf 语法及服务定义,适合微服务架构设计与维护人员阅读,助你构建高性能、低耦合的服务通信体系。
581 73
gRPC凭什么成为微服务通信首选?深度解析RPC进化史
|
2月前
|
存储 安全 Java
【Golang】(4)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法
结构体可以存储一组不同类型的数据,是一种符合类型。Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。
201 1
|
JavaScript Java Go
探索Go语言在微服务架构中的优势
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出。本文将深入探讨Go语言在构建微服务时的性能优势,包括其在内存管理、网络编程、并发模型以及工具链支持方面的特点。通过对比其他流行语言,我们将揭示Go语言如何成为微服务架构中的一股清流。
291 53
|
7月前
|
Go C++
Go语言方法与接收者 -《Go语言实战指南》
本文介绍了 Go 语言中方法的相关概念和用法。方法是绑定到特定类型上的函数,包含值接收者和指针接收者两种形式。值接收者不会改变原始数据,而指针接收者可修改原始数据,且在处理大型结构体时性能更优。文章详细对比了方法与普通函数的区别,并说明了选择指针接收者的原因,如修改原始值、提升性能及保持一致性。此外,Go 支持为任意自定义类型定义方法,不仅限于结构体。最后通过表格总结了方法的核心概念和使用场景。
211 34
|
7月前
|
分布式计算 Go C++
初探Go语言RPC编程手法
总的来说,Go语言的RPC编程是一种强大的工具,让分布式计算变得简单如同本地计算。如果你还没有试过,不妨挑战一下这个新的编程领域,你可能会发现新的世界。
192 10
|
10月前
|
Shell Go 开发工具
【环境】Rocky8使用gvm配置Go多版本管理的微服务开发环境(go-zero)
通过本文的介绍,我们详细讲解了如何在Rocky8上使用gvm来管理多个Go版本,并配置go-zero框架的开发环境。通过gvm的灵活管理,开发者可以轻松切换不同的Go版本,以适应不同项目的需求。同时,go-zero框架的使用进一步提升了微服务开发的效率和质量。希望本文能帮助开发者构建高效的Go语言开发环境,提高项目开发的灵活性和稳定性。
319 63
|
Java Apache C++
别再手写RPC了,Apache Thrift帮你自动生成RPC客户端及服务端代码
Thrift 是一个轻量级、跨语言的远程服务调用框架,由 Facebook 开发并贡献给 Apache。它通过 IDL 生成多种语言的 RPC 服务端和客户端代码,支持 C++、Java、Python 等。Thrift 的主要特点包括开发速度快、接口维护简单、学习成本低和多语言支持。广泛应用于 Cassandra、Hadoop 等开源项目及 Facebook、百度等公司。
别再手写RPC了,Apache Thrift帮你自动生成RPC客户端及服务端代码
|
监控 Go API
Go语言在微服务架构中的应用实践
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出,成为构建微服务的理想选择。本文将探讨Go语言在微服务架构中的应用实践,包括Go语言的特性如何适应微服务架构的需求,以及在实际开发中如何利用Go语言的特性来提高服务的性能和可维护性。我们将通过一个具体的案例分析,展示Go语言在微服务开发中的优势,并讨论在实际应用中可能遇到的挑战和解决方案。
|
Go 数据处理 API
Go语言在微服务架构中的应用与优势
本文摘要采用问答形式,以期提供更直接的信息获取方式。 Q1: 为什么选择Go语言进行微服务开发? A1: Go语言的并发模型、简洁的语法和高效的编译速度使其成为微服务架构的理想选择。 Q2: Go语言在微服务架构中有哪些优势? A2: 主要优势包括高性能、高并发处理能力、简洁的代码和强大的标准库。 Q3: 文章将如何展示Go语言在微服务中的应用? A3: 通过对比其他语言和展示Go语言在实际项目中的应用案例,来说明其在微服务架构中的优势。