In the
first part of our series of articles “Writing a Blog on Microservices,” we described a general approach to solving the problem.
Now it's the turn of the Gateway API or GW API.
In our c
ptimofeev GW API, we implement the following functions:
- Converting REST requests to gRPC requests and vice versa.
- Request Logging.
- Request Authentication
- Assignment of a Trace ID to each request for its further transfer between microservices along the entire request execution chain.
So let's go ...
To implement the REST / gRPC conversion function, we will use the gosh library
grpc-gateway .
Further, in the protofile of each microservice that we want to publish on REST, you need to add an option description in the description section of the service interfaces. It actually specifies the path and method by which REST access will be performed.
Based on this information, the code generation script (./bin/protogen.sh) will create the gRPC server code (in the microservice directory), the gRPC client (in the api-gw directory) and generate the latest API documentation (in the format {{service name}}. swagger.json)
Next, we need to write HTTP Proxy code, which on the one hand will be an HTTP server (for processing REST requests), and on the other hand it will be a gRPC client for our microservices (gRPC servers).
We will place this code in the file ./services/api-gw/main.go.
First, in the import section, we connect client libraries to our microservices
(protogen.sh generated them for us):
import ( … userService "./services/user/protobuf" postService "./services/post/protobuf" commentService "./services/comment/protobuf" categoryService "./services/category/protobuf" …
Next, we indicate the addresses and ports on which our gRPC services “hang” (we take the values from the environment variables):
var (
And finally, we implement the HTTP Proxy itself:
func HTTPProxy(proxyAddr string){ grpcGwMux:=runtime.NewServeMux()
In setting up the connection to microservices, we use the grpc.WithUnaryInterceptor (AccessLogInterceptor) option, into which we pass the AccessLogInterceptor function as a parameter. This is nothing more than an implementation of the middleware layer, i.e. the AccessLogInterceptor function will be executed with every gRPC call to the child microservice.
…
In turn, in the AccessLogInterceptor function, we already implement authentication, logging, and TraceId generation mechanisms.
If the authorization attribute was specified in the Header in the incoming (REST) request, then we parse and validate it in the CheckGetJWTToken function, which either returns an error or, if successful, returns UserId and UserRole.
var traceId,userId,userRole string if len(md["authorization"])>0{ tokenString:= md["authorization"][0] if tokenString!=""{ err,token:=userService.CheckGetJWTToken(tokenString) if err!=nil{ return err } userId=fmt.Sprintf("%s",token["UserID"]) userRole=fmt.Sprintf("%s",token["UserRole"]) } }
Next, we form TraceId and wrap it together with UserId and UserRole in the call context and make the gRPC call of our microservice.
And finally, we write a service call event to the log.
msg:=fmt.Sprintf("Call:%v, traceId: %v, userId: %v, userRole: %v, time: %v", method,traceId,userId,userRole,time.Since(start)) app.AccesLog(msg)
Another middleware processor is “hanging” on the answers of specific methods (SignIn, SignUp) of the User service. This handler intercepts gRPC responses, picks up the UserID and UserRole response, converts it to
JWT Token and gives it (JWT Token) in the REST response as the “Authorization” attribute Header. The middleware code described is implemented on the gRPC client side in the file ./api-gw/services/user/protobuf/functions.go.
We connect the response handler.
func init() {
An example is the SignIn response handler (the SignUp handler is similar).
func forwardSignIn(ctx context.Context, mux *runtime.ServeMux, marshaler runtime.Marshaler, w http.ResponseWriter, req *http.Request, resp proto.Message, opts ...func(context.Context, http.ResponseWriter, proto.Message) error) {
To be continued…
Yes, the demo of the project can be viewed
here , and the source code is
here .