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)
}

posted @ 2022-05-26 19:57  vx_guanchaoguo0  阅读(267)  评论(0编辑  收藏  举报