1
0
mirror of https://github.com/chai2010/advanced-go-programming-book.git synced 2025-05-24 12:32:21 +00:00

ch4-05: 增加Grpc和Web共存内容

This commit is contained in:
chai2010 2018-07-14 18:48:28 +08:00
parent b8653a5f5b
commit 88d728a746
9 changed files with 414 additions and 3 deletions

View File

@ -316,11 +316,74 @@ func filter(
不够GRPC框架中只能为每个服务设置一个截取器因此所有对截取工作只能在一个函数中完成。不过开源的grpc-ecosystem项目中的go-grpc-middleware包已经基于GRPC对截取器实现了链式截取器的支持感兴趣的同学可以参考。
<!--
## 和Web服务共存
TODO
GRPC是构建在HTTP/2协议之上的因此我们可以将GRPC服务和普通的Web服务架设在同一个端口之上。因为目前Go语言版本的GRPC实现还不够完善只有启用了TLS协议之后才能将GRPC和Web服务运行在同一个端口。
服务器证书的生成过程前文已经讲过这么不再赘述。启用https的服务器非常简单
```go
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintln(w, "hello")
})
http.ListenAndServeTLS(port, "server.crt", "server.key",
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
mux.ServeHTTP(w, r)
return
}),
)
}
```
而单独启用带证书的GRPC服务也是同样的简单
```go
func main() {
creds, err := credentials.NewServerTLSFromFile("server.crt", "server.key")
if err != nil {
log.Fatal(err)
}
grpcServer := grpc.NewServer(grpc.Creds(creds))
...
}
```
因为GRPC服务已经实现了ServeHTTP方法可以直接作为Web路由处理对象。如果将Grpc和Web服务放在一起会导致GRPC和Web路径的冲突在处理时我们需要取分两个服务。
首先GRPC是建立在HTTP/2版本之上如果HTTP不是HTTP/2协议则必然无法提供GRPC支持。同时如果每个GRPC调用请求的Content-Type类型会被标注为"application/grpc"类型。
因此我们可以通过以下方式生成同时支持Web和Grpc协议的路由处理函数
```go
func main() {
...
http.ListenAndServeTLS(port, "server.crt", "server.key",
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.ProtoMajor != 2 {
mux.ServeHTTP(w, r)
return
}
if strings.Contains(r.Header.Get("Content-Type"), "application/grpc") {
grpcServer.ServeHTTP(w, r) // GRPC Server
return
}
mux.ServeHTTP(w, r)
return
}),
)
}
```
这样我们就可以在GRPC端口上同时提供Web服务了。
<!--
## 导出Rest服务

View File

@ -0,0 +1,10 @@
run:
@go build -o a.out && ./a.out
-@rm ./a.out
gen:
protoc -I . --go_out=plugins=grpc:. helloworld.proto
clean:
-rm *.pb.go

View File

@ -0,0 +1,192 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: helloworld.proto
package main
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type HelloRequest struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *HelloRequest) Reset() { *m = HelloRequest{} }
func (m *HelloRequest) String() string { return proto.CompactTextString(m) }
func (*HelloRequest) ProtoMessage() {}
func (*HelloRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_helloworld_04dfe859c9b956ba, []int{0}
}
func (m *HelloRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_HelloRequest.Unmarshal(m, b)
}
func (m *HelloRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_HelloRequest.Marshal(b, m, deterministic)
}
func (dst *HelloRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_HelloRequest.Merge(dst, src)
}
func (m *HelloRequest) XXX_Size() int {
return xxx_messageInfo_HelloRequest.Size(m)
}
func (m *HelloRequest) XXX_DiscardUnknown() {
xxx_messageInfo_HelloRequest.DiscardUnknown(m)
}
var xxx_messageInfo_HelloRequest proto.InternalMessageInfo
func (m *HelloRequest) GetName() string {
if m != nil {
return m.Name
}
return ""
}
type HelloReply struct {
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *HelloReply) Reset() { *m = HelloReply{} }
func (m *HelloReply) String() string { return proto.CompactTextString(m) }
func (*HelloReply) ProtoMessage() {}
func (*HelloReply) Descriptor() ([]byte, []int) {
return fileDescriptor_helloworld_04dfe859c9b956ba, []int{1}
}
func (m *HelloReply) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_HelloReply.Unmarshal(m, b)
}
func (m *HelloReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_HelloReply.Marshal(b, m, deterministic)
}
func (dst *HelloReply) XXX_Merge(src proto.Message) {
xxx_messageInfo_HelloReply.Merge(dst, src)
}
func (m *HelloReply) XXX_Size() int {
return xxx_messageInfo_HelloReply.Size(m)
}
func (m *HelloReply) XXX_DiscardUnknown() {
xxx_messageInfo_HelloReply.DiscardUnknown(m)
}
var xxx_messageInfo_HelloReply proto.InternalMessageInfo
func (m *HelloReply) GetMessage() string {
if m != nil {
return m.Message
}
return ""
}
func init() {
proto.RegisterType((*HelloRequest)(nil), "main.HelloRequest")
proto.RegisterType((*HelloReply)(nil), "main.HelloReply")
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// GreeterClient is the client API for Greeter service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type GreeterClient interface {
SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
}
type greeterClient struct {
cc *grpc.ClientConn
}
func NewGreeterClient(cc *grpc.ClientConn) GreeterClient {
return &greeterClient{cc}
}
func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) {
out := new(HelloReply)
err := c.cc.Invoke(ctx, "/main.Greeter/SayHello", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// GreeterServer is the server API for Greeter service.
type GreeterServer interface {
SayHello(context.Context, *HelloRequest) (*HelloReply, error)
}
func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) {
s.RegisterService(&_Greeter_serviceDesc, srv)
}
func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(HelloRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(GreeterServer).SayHello(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/main.Greeter/SayHello",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Greeter_serviceDesc = grpc.ServiceDesc{
ServiceName: "main.Greeter",
HandlerType: (*GreeterServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "SayHello",
Handler: _Greeter_SayHello_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "helloworld.proto",
}
func init() { proto.RegisterFile("helloworld.proto", fileDescriptor_helloworld_04dfe859c9b956ba) }
var fileDescriptor_helloworld_04dfe859c9b956ba = []byte{
// 142 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xc8, 0x48, 0xcd, 0xc9,
0xc9, 0x2f, 0xcf, 0x2f, 0xca, 0x49, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0xc9, 0x4d,
0xcc, 0xcc, 0x53, 0x52, 0xe2, 0xe2, 0xf1, 0x00, 0xc9, 0x04, 0xa5, 0x16, 0x96, 0xa6, 0x16, 0x97,
0x08, 0x09, 0x71, 0xb1, 0xe4, 0x25, 0xe6, 0xa6, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x06, 0x81,
0xd9, 0x4a, 0x6a, 0x5c, 0x5c, 0x50, 0x35, 0x05, 0x39, 0x95, 0x42, 0x12, 0x5c, 0xec, 0xb9, 0xa9,
0xc5, 0xc5, 0x89, 0xe9, 0x30, 0x45, 0x30, 0xae, 0x91, 0x35, 0x17, 0xbb, 0x7b, 0x51, 0x6a, 0x6a,
0x49, 0x6a, 0x91, 0x90, 0x01, 0x17, 0x47, 0x70, 0x62, 0x25, 0x58, 0x97, 0x90, 0x90, 0x1e, 0xc8,
0x26, 0x3d, 0x64, 0x6b, 0xa4, 0x04, 0x50, 0xc4, 0x0a, 0x72, 0x2a, 0x93, 0xd8, 0xc0, 0xae, 0x32,
0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x18, 0x38, 0x13, 0xf1, 0xa9, 0x00, 0x00, 0x00,
}

View File

@ -0,0 +1,15 @@
syntax = "proto3";
package main;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}

View File

@ -0,0 +1,75 @@
package main
import (
fmt "fmt"
"log"
"net/http"
"strings"
"time"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
var port = ":5000"
type myGrpcServer struct{}
func (s *myGrpcServer) SayHello(ctx context.Context, in *HelloRequest) (*HelloReply, error) {
return &HelloReply{Message: "Hello " + in.Name}, nil
}
func main() {
go startServer()
time.Sleep(time.Second)
doClientWork()
}
func startServer() {
creds, err := credentials.NewServerTLSFromFile("tls-config/server.crt", "tls-config/server.key")
if err != nil {
log.Fatal(err)
}
grpcServer := grpc.NewServer(grpc.Creds(creds))
RegisterGreeterServer(grpcServer, new(myGrpcServer))
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintln(w, "hello")
})
http.ListenAndServeTLS(port, "tls-config/server.crt", "tls-config/server.key", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// TODO(tamird): point to merged gRPC code rather than a PR.
// This is a partial recreation of gRPC's internal checks
// https://github.com/grpc/grpc-go/pull/514/files#diff-95e9a25b738459a2d3030e1e6fa2a718R61
if r.ProtoMajor == 2 && strings.Contains(r.Header.Get("Content-Type"), "application/grpc") {
grpcServer.ServeHTTP(w, r)
} else {
mux.ServeHTTP(w, r)
}
}))
}
func doClientWork() {
creds, err := credentials.NewClientTLSFromFile("tls-config/server.crt", "server.grpc.io")
if err != nil {
log.Fatal(err)
}
conn, err := grpc.Dial("localhost"+port, grpc.WithTransportCredentials(creds))
if err != nil {
log.Fatal(err)
}
defer conn.Close()
c := NewGreeterClient(conn)
r, err := c.SayHello(context.Background(), &HelloRequest{Name: "gopher"})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("doClientWork: %s", r.Message)
}

View File

@ -0,0 +1,10 @@
default:
openssl genrsa -out server.key 2048
openssl req -new -x509 -days 3650 \
-subj "/C=GB/L=China/O=gobook/CN=server.grpc.io" \
-key server.key -out server.crt
clean:
-rm *.key *.crt *.csr *.srl

View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDCjCCAfICCQD94/YEp09k1TANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQGEwJH
QjEOMAwGA1UEBwwFQ2hpbmExDzANBgNVBAoMBmdvYm9vazEXMBUGA1UEAwwOc2Vy
dmVyLmdycGMuaW8wHhcNMTgwNzE0MTAzMTUyWhcNMjgwNzExMTAzMTUyWjBHMQsw
CQYDVQQGEwJHQjEOMAwGA1UEBwwFQ2hpbmExDzANBgNVBAoMBmdvYm9vazEXMBUG
A1UEAwwOc2VydmVyLmdycGMuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQCsE9wEkF46VQ62jwaS/udWdoB6VfTBFngYrtSLt2JEOV1DqSXVUSnKU6es
xdBpRVWuIJFoE7Nl+9HA24qsBf/RvVr7V0xcbp0E4I8V05NldQrzAr3J4hti7gA7
720wADLdKfbwmikftM3UZHFNgy5dzZIj0EDA1HyANv41JRG/h9OVWM6g6ENLs5zL
HNBKDvAJVg2q5iwfaDW5j1AmqmAXDkFqdq7W41PZA15P/NtY4vOLbq6lI0SXjqnW
i1QyDalU8v6v1oSjMBIUkmm8HSRJ1KTul8BPEUu1k1Tos1GdUroGK9R2/gDz3pdA
cqR0lcv2Wzt6RXroaqtliJosqHHtAgMBAAEwDQYJKoZIhvcNAQELBQADggEBADpA
g+wrvJ5xlLWViXOfbluU1VZ4uRovg9mpJ7uJt5rKEJanNPbpKmgSfnztZulfg55j
OsPQuoJafJGfor6tCx8RYqD1FQtevoqxPl5oY4+re9vCJTe0Tc9+V3OdCwZ0YhuZ
UMTabh73Gl93a8RdAzTFQtEaAtBeuE7EjF16xeKKx+E5u4JO6srTbPuFMDC4z30h
fpfSfscZS4DVRI2CuKv1XI1GHCzSmxFPe/keli7vb/+Bu+hVCBuFFbqbGXRD0BGX
y8qIHD3GwI166L1RflgDgmgrQgXsvrvlixajdj/d5bT3kok8xM/pY4ufqJWNr4vA
QxoCpEnWThyIQEU3aGI=
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEArBPcBJBeOlUOto8Gkv7nVnaAelX0wRZ4GK7Ui7diRDldQ6kl
1VEpylOnrMXQaUVVriCRaBOzZfvRwNuKrAX/0b1a+1dMXG6dBOCPFdOTZXUK8wK9
yeIbYu4AO+9tMAAy3Sn28JopH7TN1GRxTYMuXc2SI9BAwNR8gDb+NSURv4fTlVjO
oOhDS7OcyxzQSg7wCVYNquYsH2g1uY9QJqpgFw5Banau1uNT2QNeT/zbWOLzi26u
pSNEl46p1otUMg2pVPL+r9aEozASFJJpvB0kSdSk7pfATxFLtZNU6LNRnVK6BivU
dv4A896XQHKkdJXL9ls7ekV66GqrZYiaLKhx7QIDAQABAoIBAQCGdmJHorQu6sY7
e1nMxajp1GGitsYxl6gyyXLG1n7QBu9M7sDkhq6cLnBovo6TpB1GmqZk6HgX6gXG
qQccUvfK4idVHatK0xiZdgv5J0IXVrU+nVbGiYuS6519OCNfDdixH5iZ/CzEXNk1
7WLoCejSXc+jcpkW1TOQUOiliJjsyyMW/d5eB2vpPwqkB3q45nQjbGGTkbQIKNx/
rAZhF1dPv53wdWjG/Hlscomq8cVjRUOfSHTisZOKKsd/HHJp5C30noD/by9UtxKc
JP6KAXay4gcvvmInBtH+SalRQhPJJ3Hs82atSJKISwU2rjg6DQHjR2seOpl1fDWP
wwlYwW5hAoGBANoAs4wHjAe3+f63YvlckcDXpfNYfqTyqxettqYgOpQbGXAT+0Ze
Y/FCG8G6xmHq68BGdyTkM9gfKfLEpAYXABTykmUkNVjDMKEPQ/Vu4Wuj2lntLbug
3MJQIR8nah3AlxfFUSUHruVSQhAoKUdA7FUfmeG3I0Y3GUiDPWNYAUvJAoGBAMoR
+UzgmyT+WjarPicCMtwxSlb1iHAdiuPOXe9t/ajJTCzuyaAG2xLIMcAJr4G1Ixdk
hQ/Vh0zHQ88SAPjSgrrv3+jLMKRI3P9MkClprBLY5KRtbZaJf4zsyzU0noOYDTIl
O8lKc2BFsDxnBHq1iGvvTvfdcwo/EGo8zm4x378FAoGAcDdiNNeBrkt2pTYy/Vc4
M8MNynioIDJF/ddOqK74WFqmunmeo7dczMiRzyRcj/TES+I53ESXp01LY4rzP6tB
QIco8mU0DK/U7WMVQVZFNQpQEHA1VVrVqDRlCEtapVwIqpTIUz5kOekg1n5F2UbD
III2zOhfgPpFyny78dJQQHkCgYBNZzUnzaGgOg40gsP8DUuOLRNc3BC3YZmiSZqR
7IAN943GaHGzauzf9O+ZsBwag1g9zbNqrVB2iJ3g4/SbW4fYM6qBcdPgGkkNQja2
plPTpGFw2rnvwBurz8jHSLCvl772RmzwXiRz7D3l5VBB8Oeg8Lo/GATJF/8hsskg
TNp4QQKBgQDAlg81zWxtJOltLmndvsvS/IUJgScXMEh1NsE7MgKcPsb1J4gyV8Vn
564+uhn40UOfCEPaSyh5XCSJYCupfCHGwGfcuN53kMooaNyDr5zxVH3tDEkAd5jj
9z+M9tu4/hpi7G1cZGWGc9E5bBM+crGVBwX6YdHrQOGKPM1Gm3dnQA==
-----END RSA PRIVATE KEY-----