golang GRPC 和http 服務同時使用
阿新 • • 發佈:2022-05-26
golang微服務元件 gokit
- 服務三層架構解析
- Transport 傳輸 這是通訊的入口,你將使用這一層去建立你的介面,讓你的介面去接收呼叫者的引數
- Endpoint 端點 終點 這是一個終點也是一個起點,它將讓你去處理request以及response
- Service 服務 這裡才是你介面真正的開始,接收由Endpoint帶來的資料,並處理它,返回它
目錄結構解析
--cmd -- main.go --endpoints -- endpoint.go --services -- service.go -- transports --pb -- hello.proto -- complie.bat -- transport.go -- test hello_test.go
hello.proto
syntax = "proto3";
package pb;
option go_package = ".;pb";
service HelloService {
rpc Greeting(GreetingRequest) returns (GreetingResponse) {}
}
message GreetingRequest {
string name = 1;
}
message GreetingResponse {
string message = 2;
string err = 3;
}
complie.bat
protoc --go_out=. --go-grpc_out=. hello.proto
資料的入口【協議平面】 接受原始資料 transport.go decode/encode
package transports import ( "context" "encoding/json" "errors" "net/http" "sanbox/gokit/transports/pb" "strings" ) // ErrEmpty is returned when an input name is empty. var ErrEmpty = errors.New("empty name") func DecodeGreetingRequest(_ context.Context, r *http.Request) (interface{}, error) { var request pb.GreetingRequest switch r.Method { case http.MethodGet: vars := r.URL.Query() if name, ok := vars["name"]; !ok || len(name) != 1 { return nil, ErrEmpty } else { request.Name = name[0] } case http.MethodPost: if strings.HasPrefix(r.Header["Content-Type"][0], "application/json") { if err := json.NewDecoder(r.Body).Decode(&request); err != nil { return nil, err } } } return request, nil } func EncodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { w.Header().Set("Content-Type", "application/json; charset=utf-8") return json.NewEncoder(w).Encode(response) } func DecodeGRPCGreetingRequest(_ context.Context, grpcReq interface{}) (interface{}, error) { req := grpcReq.(*pb.GreetingRequest) return pb.GreetingRequest{Name: req.Name}, nil } func EncodeGRPCResponse(_ context.Context, response interface{}) (interface{}, error) { resp := response.(pb.GreetingResponse) return &pb.GreetingResponse{Message: resp.Message, Err: resp.Err}, nil }
請求處理層【控制平面】 通過transprot結構化的資料化滿足介面的資料 request / response endpoint.go
package endpoints
import (
"context"
"github.com/go-kit/kit/endpoint"
"sanbox/gokit/services"
"sanbox/gokit/transports/pb"
)
type GreetingRequest struct {
Name string `json:"name"`
}
type GreetingResponse struct {
Message string `json:"message"`
Err string `json:"err,omitempty"` // errors don't define JSON marshaling
}
func MakeGreetingEndpoint(svc services.HelloService) endpoint.Endpoint {
return func(_ context.Context, request interface{}) (interface{}, error) {
req := request.(pb.GreetingRequest)
message, err := svc.Greeting(req.Name)
if err != nil {
return pb.GreetingResponse{Message: message, Err: err.Error()}, nil
}
return pb.GreetingResponse{Message: message, Err: ""}, nil
}
}
服務層【資料平面】 Endpoint帶來的資料
package services
import (
"context"
"fmt"
grpctransport "github.com/go-kit/kit/transport/grpc"
"sanbox/gokit/entry"
"sanbox/gokit/transports/pb"
"time"
)
// HelloServiceInterface provides greeting message.
type HelloServiceInterface interface {
Greeting(string) (string, error)
}
type HelloService struct{}
func (HelloService) Greeting(s string) (string, error) {
if s == "" {
return fmt.Sprintf("Hello guest, your visit time is %s.", time.Now().Format("2006-01-02 15:04:05")), entry.ErrEmpty
}
return fmt.Sprintf("Hello %s, your visit time is %s.", s, time.Now().Format("2006-01-02 15:04:05")), nil
}
type HelloGRPCServer struct {
GreetingHandler grpctransport.Handler
pb.UnimplementedHelloServiceServer
}
func (s *HelloGRPCServer) Greeting(ctx context.Context, req *pb.GreetingRequest) (*pb.GreetingResponse, error) {
_, res, err := s.GreetingHandler.ServeGRPC(ctx, req)
if err != nil {
return nil, err
}
return res.(*pb.GreetingResponse), nil
}
程式入口 mian.go
package main
import (
grpctransport "github.com/go-kit/kit/transport/grpc"
httptransport "github.com/go-kit/kit/transport/http"
"google.golang.org/grpc"
"log"
"net"
"net/http"
"os"
"sanbox/gokit/endpoints"
"sanbox/gokit/services"
"sanbox/gokit/transports"
"sanbox/gokit/transports/pb"
)
func main() {
go runWEB()
runGPRC()
}
func runWEB() {
svc := services.HelloService{}
greetingHandler := httptransport.NewServer(
endpoints.MakeGreetingEndpoint(svc),
transports.DecodeGreetingRequest,
transports.EncodeResponse,
)
http.Handle("/greeting", greetingHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
func runGPRC() {
grpcListener, err := net.Listen("tcp", ":8081")
if err != nil {
os.Exit(1)
}
grpcServer := grpc.NewServer()
pb.RegisterHelloServiceServer(grpcServer, &services.HelloGRPCServer{
GreetingHandler: grpctransport.NewServer(
endpoints.MakeGreetingEndpoint(services.HelloService{}),
transports.DecodeGRPCGreetingRequest,
transports.EncodeGRPCResponse,
)})
grpcServer.Serve(grpcListener)
}
測試檔案 helle_test.go
package test
import (
"context"
"github.com/valyala/fasthttp"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"log"
"sanbox/gokit/transports/pb"
"testing"
"time"
)
func TestHello(t *testing.T) {
conn, err := grpc.Dial("localhost:8081", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("connect failed! error: %v", err)
}
defer conn.Close()
c := pb.NewHelloServiceClient(conn)
name := "John"
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.Greeting(ctx, &pb.GreetingRequest{Name: name})
if err != nil {
log.Fatalf("call failed!, error: %v", err)
}
log.Printf("message: %v, err: %v", r.Message, r.Err)
}
func TestHttpGet(t *testing.T) {
code, resp, err := fasthttp.Get(nil, "http://localhost:8080/greeting?name=Jack")
if err != nil {
log.Fatalf("call failed!, error: %v", err)
}
log.Printf("message: %s \n code: %v", resp, code)
}
func TestHttpPost(t *testing.T) {
code, resp, err := fasthttp.Post([]byte(`{"name": "Jack"}`), "http://localhost:8080/greeting", nil)
if err != nil {
log.Fatalf("call failed!, error: %v", err)
}
log.Printf("message: %s \n code: %v", resp, code)
}