golang GRPC 和http 服务同时使用
golang微服务组件 gokit
- 服务三层架构解析
- Transport 协议平面
- Endpoint 控制平面
- Service 数据平面
脚手架 kitcli
go get -u google.golang.org/grpc
go get -u github.com/golang/protobuf/protoc-gen-go
go get github.com/kujtimiihoxha/kit
go get github.com/go-kit/kit
# m middleware / e endpnit / t transport / dmw default middleware for service and endpoint
kit n s [service_name]
kit n s [service_name] -t grpc
kit g m hi -s [service_name]
kit g m hi -s [service_name] -e # if you want to add endpoint middleware
目录结构解析
--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/transports"
"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")), transports.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() {
errCh := make(chan error)
go httpRun(":8080", errCh)
go grpcRun(":8081", errCh)
log.Fatalf("error: %v", <-errCh)
}
func httpRun(addr string, errCh chan error) {
svc := services.HelloService{}
greetingHandler := httptransport.NewServer(
endpoints.MakeGreetingEndpoint(svc),
transports.DecodeGreetingRequest,
transports.EncodeResponse,
)
http.Handle("/greeting", greetingHandler)
errCh <- http.ListenAndServe(addr, nil)
}
func grpcRun(addr string, errCh chan error) {
grpcListener, err := net.Listen("tcp", addr)
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,
)})
errCh <- 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)
}
本文来自博客园,作者:vx_guanchaoguo0,转载请注明原文链接:https://www.cnblogs.com/guanchaoguo/p/16314754.html