Client Implementation
The API Gateway acts as a gRPC client, while each microservice acts as a gRPC server. We'll define the gRPC client in our controller properties for later use.
The client is defined using grpcx.Client.MustNewGrpcClientConn(service, opts...)
.
app/gateway/internal/controller/user/user_new.go
package user
import (
"github.com/gogf/gf/contrib/rpc/grpcx/v2"
"proxima/app/gateway/api/user"
v1 "proxima/app/user/api/account/v1"
)
type ControllerV1 struct {
AccountClient v1.AccountClient
}
func NewV1() user.IUserV1 {
var conn = grpcx.Client.MustNewGrpcClientConn("user")
return &ControllerV1{
AccountClient: v1.NewAccountClient(conn),
}
}
app/gateway/internal/controller/words/words_new.go
package words
import (
"github.com/gogf/gf/contrib/rpc/grpcx/v2"
"proxima/app/gateway/api/words"
v1 "proxima/app/word/api/words/v1"
)
type ControllerV1 struct {
WordsClient v1.WordsClient
}
func NewV1() words.IWordsV1 {
var conn = grpcx.Client.MustNewGrpcClientConn("word")
return &ControllerV1{
WordsClient: v1.NewWordsClient(conn),
}
}
Interceptors
Currently, our client doesn't have timeout handling, and gRPC's default timeout threshold is very high. If the gRPC server, etcd service, or network encounters issues, the API Gateway could hang indefinitely. Let's add a timeout interceptor to handle these situations.
Defining the Interceptor
The timeout mechanism is simple, implemented using Go's context.
app/gateway/utility/grpc.go
package utility
import (
"context"
"time"
"google.golang.org/grpc"
)
func GrpcClientTimeout(ctx context.Context, method string, req, reply interface{},
cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption,
) error {
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()
err := invoker(ctx, method, req, reply, cc, opts...)
return err
}
Using the Interceptor
app/gateway/internal/controller/user/user_new.go
package user
import (
"github.com/gogf/gf/contrib/rpc/grpcx/v2"
"proxima/app/gateway/api/user"
"proxima/app/gateway/utility"
v1 "proxima/app/user/api/account/v1"
)
type ControllerV1 struct {
AccountClient v1.AccountClient
}
func NewV1() user.IUserV1 {
var conn = grpcx.Client.MustNewGrpcClientConn("user", grpcx.Client.ChainUnary(
utility.GrpcClientTimeout,
))
return &ControllerV1{
AccountClient: v1.NewAccountClient(conn),
}
}
app/gateway/internal/controller/words/words_new.go
package words
import (
"github.com/gogf/gf/contrib/rpc/grpcx/v2"
"proxima/app/gateway/api/words"
"proxima/app/gateway/utility"
v1 "proxima/app/word/api/words/v1"
)
type ControllerV1 struct {
WordsClient v1.WordsClient
}
func NewV1() words.IWordsV1 {
var conn = grpcx.Client.MustNewGrpcClientConn("word", grpcx.Client.ChainUnary(
utility.GrpcClientTimeout,
))
return &ControllerV1{
WordsClient: v1.NewWordsClient(conn),
}
}