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/


目录
相关文章
|
8天前
|
Go API Docker
热门go与微服务15
热门go与微服务15
22 2
|
1月前
|
Dubbo Java 应用服务中间件
微服务框架Dubbo环境部署实战
微服务框架Dubbo环境部署的实战指南,涵盖了Dubbo的概述、服务部署、以及Dubbo web管理页面的部署,旨在指导读者如何搭建和使用Dubbo框架。
85 17
微服务框架Dubbo环境部署实战
|
5天前
|
JSON Go API
使用Go语言和Gin框架构建RESTful API:GET与POST请求示例
使用Go语言和Gin框架构建RESTful API:GET与POST请求示例
|
13天前
|
Kubernetes Java Android开发
用 Quarkus 框架优化 Java 微服务架构的设计与实现
Quarkus 是专为 GraalVM 和 OpenJDK HotSpot 设计的 Kubernetes Native Java 框架,提供快速启动、低内存占用及高效开发体验,显著优化了 Java 在微服务架构中的表现。它采用提前编译和懒加载技术实现毫秒级启动,通过优化类加载机制降低内存消耗,并支持多种技术和框架集成,如 Kubernetes、Docker 及 Eclipse MicroProfile,助力开发者轻松构建强大微服务应用。例如,在电商场景中,可利用 Quarkus 快速搭建商品管理和订单管理等微服务,提升系统响应速度与稳定性。
30 5
|
28天前
|
存储 Java Maven
从零到微服务专家:用Micronaut框架轻松构建未来架构
【9月更文挑战第5天】在现代软件开发中,微服务架构因提升应用的可伸缩性和灵活性而广受欢迎。Micronaut 是一个轻量级的 Java 框架,适合构建微服务。本文介绍如何从零开始使用 Micronaut 搭建微服务架构,包括设置开发环境、创建 Maven 项目并添加 Micronaut 依赖,编写主类启动应用,以及添加控制器处理 HTTP 请求。通过示例代码展示如何实现简单的 “Hello, World!” 功能,并介绍如何通过添加更多依赖来扩展应用功能,如数据访问、验证和安全性等。Micronaut 的强大和灵活性使你能够快速构建复杂的微服务系统。
61 5
|
27天前
|
缓存 Java 应用服务中间件
随着微服务架构的兴起,Spring Boot凭借其快速开发和易部署的特点,成为构建RESTful API的首选框架
【9月更文挑战第6天】随着微服务架构的兴起,Spring Boot凭借其快速开发和易部署的特点,成为构建RESTful API的首选框架。Nginx作为高性能的HTTP反向代理服务器,常用于前端负载均衡,提升应用的可用性和响应速度。本文详细介绍如何通过合理配置实现Spring Boot与Nginx的高效协同工作,包括负载均衡策略、静态资源缓存、数据压缩传输及Spring Boot内部优化(如线程池配置、缓存策略等)。通过这些方法,开发者可以显著提升系统的整体性能,打造高性能、高可用的Web应用。
58 2
|
28天前
|
Cloud Native 安全 Java
Micronaut对决Spring Boot:谁是微服务领域的王者?揭秘两者优劣,选对框架至关重要!
【9月更文挑战第5天】近年来,微服务架构备受关注,Micronaut和Spring Boot成为热门选择。Micronaut由OCI开发,基于注解的依赖注入,内置多种特性,轻量级且启动迅速;Spring Boot则简化了Spring应用开发,拥有丰富的生态支持。选择框架需考虑项目需求、团队经验、性能要求及社区支持等因素。希望本文能帮助您选择合适的微服务框架,助力您的软件开发项目取得成功!
96 2
|
1月前
|
缓存 安全 Java
如何利用Go语言提升微服务架构的性能
在当今的软件开发中,微服务架构逐渐成为主流选择,它通过将应用程序拆分为多个小服务来提升灵活性和可维护性。然而,如何确保这些微服务高效且稳定地运行是一个关键问题。Go语言,以其高效的并发处理能力和简洁的语法,成为解决这一问题的理想工具。本文将探讨如何通过Go语言优化微服务架构的性能,包括高效的并发编程、内存管理技巧以及如何利用Go生态系统中的工具来提升服务的响应速度和资源利用率。
|
2月前
|
消息中间件 缓存 Kafka
go-zero微服务实战系列(八、如何处理每秒上万次的下单请求)
go-zero微服务实战系列(八、如何处理每秒上万次的下单请求)
|
2月前
|
缓存 NoSQL Redis
go-zero微服务实战系列(七、请求量这么高该如何优化)
go-zero微服务实战系列(七、请求量这么高该如何优化)
下一篇
无影云桌面