与 [微服务六] 略有不同。
以商品详情场景解释本篇和 [微服务六] 还有 [微服务七] 的不同之处
微服务六: 不同端口同时支持 rpc 和 http。
场景: 商品服务 -> 商品库存等
商品服务 -> 商品库存提供 rpc: 端口(9001) 客户端可以通过 rpc 调用商品库存 rpc 接口服务,
此时客户端可能没有使用 rpc,只会 http api 调用的形式。这个时候客户端也可以通过 http, 调用用商品库存 rpc 接口提供的 http: 端口(8001)
相当于一个商品库存,通过基于 protobuf 协议封装的 grpc 即提供了 rpc 又提供了 http,而且端口还不同,这样也可以,在基于 protobuf 封装 rpc 的时候,加一个 options 参数,使用对应的路由实现。但是有一点不方便,不管是 rpc 还是 http api 变动都需要使用 proto 重新生成对应的 *.pb.go 文件,增加维护成本,如果调用方不支持 rpc 可以使用此方式
微服务七:同一个端口支持 grpc 和 http
场景: 商品服务 -> 商品库存、商品详情、商品列表等
商品服务 -> 商品库存 rpc 接口端口(9001) 客户端可以通过 rpc 调用商品库存 rpc 接口,但不能通过 http 调用商品库存 rpc 服务接口. 但是此时商品服务可能还存在一些其他的 api,比如商品详情、商品列表等,此时不想通过封装 rpc 供客户端调用,想通过简单的 http api 方式供客户端使用,但是又不想一个商品服务同时暴漏两个端口,可以采用此方法。比方客户端可以通过 http 调用商品服务另外提供的 api,端口也是 9001
不建议, 最好还是分别使用不同的端口,端口对应独立的服务
本篇文章: 不同端口提供 rpc 和 http
场景: 商品服务 -> 商品库存、商品详情、商品列表等
商品服务 -> 商品库存只提供 rpc 但是商品详情和商品列表不提供 rpc 只提供 http api,端口又不同
目录结构
一 创建项目
mkdir demo
cd demo && go mod init demo && go get google.golang.org/grpc && touch generate_pb && chmod +x generate_pb
mkdir demo-server && mkdir demo-client
cd demo-server && mkdir pb && mkdir proto && mkdir rpcServices && touch main.go
cd demo-client && touch main.go
二 代码编写
rpcDemoService.proto
syntax = "proto3";
package pb;
option go_package = "../pb";
message GetUserInfoRequest {int32 id = 1;}
message GetUserInfoResponse {
string name = 1;
int32 age = 2;
}
service RpcDemoService {rpc GetUserInfo(GetUserInfoRequest) returns (GetUserInfoResponse){}}
generate_pb
cd proto && protoc --go_out=plugins=grpc:../pb rpcDemoService.proto
执行./generate_pb, 会在 pb 目录闲生成 *.pb.go 文件
rpcDemoService.go
package rpcServices
import (
"context"
"demo/demo-server/pb"
)
type RpcDemoService struct{}
func (rd *RpcDemoService) GetUserInfo(ctx context.Context, r *pb.GetUserInfoRequest) (*pb.GetUserInfoResponse, error) {
return &pb.GetUserInfoResponse{
Name: "demo",
Age: 18,
}, nil
}
main.go
package main
import (
"demo/demo-server/pb"
"demo/demo-server/rpcServices"
"flag"
"log"
"net"
"net/http"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
var grpcPort string
var httpPort string
func init() {flag.StringVar(&grpcPort, "g", "8001", "gRPC 启动端口号 ")
flag.StringVar(&httpPort, "h", "9001", "HTTP 启动端口号 ")
flag.Parse()}
//RunHttpServer
func RunHttpServer(port string) error {serveMux := http.NewServeMux()
serveMux.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {_, _ = w.Write([]byte(`pong`))
})
return http.ListenAndServe(":"+port, serveMux)
}
//RunGrpcServer
func RunGrpcServer(port string) error {s := grpc.NewServer()
pb.RegisterRpcDemoServiceServer(s, new(rpcServices.RpcDemoService))
reflection.Register(s)
lis, err := net.Listen("tcp", ":"+port)
if err != nil {return err}
return s.Serve(lis)
}
func main() {errs := make(chan error)
go func() {err := RunHttpServer(httpPort)
if err != nil {errs <- err}
}()
go func() {err := RunGrpcServer(grpcPort)
if err != nil {errs <- err}
}()
select {
case err := <-errs:
log.Fatalf("Run Server err: %v", err)
}
}
cd demo-client
main.go
package main
import (
"context"
"demo/demo-server/pb"
"fmt"
"google.golang.org/grpc"
)
func main() {conn, err := grpc.Dial(":8001", grpc.WithInsecure())
if err != nil {panic(err)
}
defer conn.Close()
demoServiceClient := pb.NewRpcDemoServiceClient(conn)
result, err := demoServiceClient.GetUserInfo(context.Background(), &pb.GetUserInfoRequest{Id: 10,})
if err != nil {panic(err)
}
fmt.Println(result)
}
测试 grpc
执行
cd demo-server
go run main.go
postman 或者 curl 访问 127.0.0.1:9001/ping
测试 http
执行
cd demo-client && go run main.go
修改 demo-server/main.go 结合 echo 框架支持 restful api
package main
import (
"demo/demo-server/pb"
"demo/demo-server/rpcServices"
"flag"
"log"
"net"
"net/http"
"github.com/labstack/echo"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
var grpcPort string
var httpPort string
func init() {flag.StringVar(&grpcPort, "g", "8001", "gRPC 启动端口号 ")
flag.StringVar(&httpPort, "h", "9001", "HTTP 启动端口号 ")
flag.Parse()}
//RunHttpServer
func RunHttpServer(port string) {//serveMux := http.NewServeMux()
//serveMux.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {// _, _ = w.Write([]byte(`pong`))
//})
//
//return http.ListenAndServe(":"+port, serveMux)
e := echo.New()
e.GET("/ping", func(c echo.Context) error {return c.JSON(http.StatusOK, "pong...")
})
e.Logger.Fatal(e.Start(":" + port))
}
//RunGrpcServer
func RunGrpcServer(port string) error {s := grpc.NewServer()
pb.RegisterRpcDemoServiceServer(s, new(rpcServices.RpcDemoService))
reflection.Register(s)
lis, err := net.Listen("tcp", ":"+port)
if err != nil {return err}
return s.Serve(lis)
}
func main() {errs := make(chan error)
go func() {RunHttpServer(httpPort)
}()
go func() {err := RunGrpcServer(grpcPort)
if err != nil {errs <- err}
}()
select {
case err := <-errs:
log.Fatalf("Run Server err: %v", err)
}
}