Context辅助函数传递
- 传递方式:在Go语言中,通常通过函数参数将
context.Context
对象从一个微服务传递到下一个微服务。例如,在网关服务中接收到请求后,创建一个context.Context
,并将其作为参数传递给订单服务的调用函数,订单服务再将这个context
传递给库存服务等后续调用。
// 网关服务
func gatewayHandler(w http.ResponseWriter, r *http.Request) {
ctx := context.Background()
// 调用订单服务
result, err := orderService.Call(ctx)
if err != nil {
// 处理错误
}
// 处理响应并返回
}
// 订单服务
func Call(ctx context.Context) (string, error) {
// 调用库存服务
stockResult, err := stockService.Call(ctx)
if err != nil {
// 处理错误
}
// 处理库存服务响应并返回
return "order result", nil
}
// 库存服务
func Call(ctx context.Context) (string, error) {
// 处理业务逻辑
return "stock result", nil
}
- 使用context.Value传递数据:如果需要在不同服务间传递一些特定的数据(如用户认证信息、请求ID等),可以使用
context.WithValue
创建一个携带值的context
,并在后续服务中通过ctx.Value(key)
获取值。
// 网关服务
func gatewayHandler(w http.ResponseWriter, r *http.Request) {
ctx := context.Background()
ctx = context.WithValue(ctx, "requestID", "12345")
// 调用订单服务
result, err := orderService.Call(ctx)
if err != nil {
// 处理错误
}
// 处理响应并返回
}
// 订单服务
func Call(ctx context.Context) (string, error) {
requestID := ctx.Value("requestID")
// 使用requestID
// 调用库存服务
stockResult, err := stockService.Call(ctx)
if err != nil {
// 处理错误
}
// 处理库存服务响应并返回
return "order result", nil
}
使用边界界定
- 请求入口:在请求进入微服务架构的入口处(如网关)创建
context.Context
,一般使用context.Background()
作为根context
,如果需要设置超时等,可使用context.WithTimeout
或context.WithCancel
等函数创建。
- 服务调用:每个微服务在调用其他微服务时,应将接收到的
context
原封不动地传递下去,不要重新创建新的context
(除非有特殊需求,如设置新的超时时间)。
- 请求结束:当请求处理完成,不再需要
context
时,它会随着函数调用栈的退出而自然释放,无需手动处理。
处理处理逻辑不一致问题
- 统一规范:制定团队内部的
context
使用规范,明确在不同场景下(如超时设置、取消逻辑、值传递等)应该如何处理context
。例如,规定所有微服务在调用下游服务时,继承上游传递过来的超时时间,除非有特殊需求才重新设置。
- 中间件:可以使用中间件来统一处理
context
相关逻辑。例如,在每个微服务的HTTP请求处理链中添加一个中间件,负责检查和修正context
的设置,确保一致性。
func contextMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
// 检查和修正context,如设置默认超时等
newCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
r = r.WithContext(newCtx)
next.ServeHTTP(w, r)
})
}
- 错误处理:在每个微服务中,对
context
相关的错误进行统一处理。例如,如果context
超时或被取消,返回特定的错误码和错误信息给上游服务,以便进行统一的错误处理和响应。
func Call(ctx context.Context) (string, error) {
select {
case <-ctx.Done():
return "", ctx.Err()
default:
// 处理业务逻辑
return "result", nil
}
}