gitee.com/liuxuezhan/go-micro-v1.18.0@v1.0.0/proxy/grpc/grpc.go (about) 1 // Package grpc transparently forwards the grpc protocol using a go-micro client. 2 package grpc 3 4 import ( 5 "context" 6 "io" 7 "strings" 8 9 "gitee.com/liuxuezhan/go-micro-v1.18.0/client" 10 "gitee.com/liuxuezhan/go-micro-v1.18.0/client/grpc" 11 "gitee.com/liuxuezhan/go-micro-v1.18.0/codec" 12 "gitee.com/liuxuezhan/go-micro-v1.18.0/config/options" 13 "gitee.com/liuxuezhan/go-micro-v1.18.0/proxy" 14 "gitee.com/liuxuezhan/go-micro-v1.18.0/server" 15 ) 16 17 // Proxy will transparently proxy requests to the backend. 18 // If no backend is specified it will call a service using the client. 19 // If the service matches the Name it will use the server.DefaultRouter. 20 type Proxy struct { 21 // The proxy options 22 options.Options 23 24 // Endpoint specified the fixed endpoint to call. 25 Endpoint string 26 27 // The client to use for outbound requests 28 Client client.Client 29 } 30 31 // read client request and write to server 32 func readLoop(r server.Request, s client.Stream) error { 33 // request to backend server 34 req := s.Request() 35 36 for { 37 // get data from client 38 // no need to decode it 39 body, err := r.Read() 40 if err == io.EOF { 41 return nil 42 } 43 if err != nil { 44 return err 45 } 46 47 // get the header from client 48 hdr := r.Header() 49 msg := &codec.Message{ 50 Type: codec.Request, 51 Header: hdr, 52 Body: body, 53 } 54 // write the raw request 55 err = req.Codec().Write(msg, nil) 56 if err == io.EOF { 57 return nil 58 } else if err != nil { 59 return err 60 } 61 } 62 } 63 64 // ProcessMessage acts as a message exchange and forwards messages to ongoing topics 65 // TODO: should we look at p.Endpoint and only send to the local endpoint? probably 66 func (p *Proxy) ProcessMessage(ctx context.Context, msg server.Message) error { 67 // TODO: check that we're not broadcast storming by sending to the same topic 68 // that we're actually subscribed to 69 70 // directly publish to the local client 71 return p.Client.Publish(ctx, msg) 72 } 73 74 // ServeRequest honours the server.Proxy interface 75 func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error { 76 // set default client 77 if p.Client == nil { 78 p.Client = grpc.NewClient() 79 } 80 81 opts := []client.CallOption{} 82 83 // service name 84 service := req.Service() 85 endpoint := req.Endpoint() 86 87 // call a specific backend 88 if len(p.Endpoint) > 0 { 89 // address:port 90 if parts := strings.Split(p.Endpoint, ":"); len(parts) > 1 { 91 opts = append(opts, client.WithAddress(p.Endpoint)) 92 // use as service name 93 } else { 94 service = p.Endpoint 95 } 96 } 97 98 // create new request with raw bytes body 99 creq := p.Client.NewRequest(service, endpoint, nil, client.WithContentType(req.ContentType())) 100 101 // create new stream 102 stream, err := p.Client.Stream(ctx, creq, opts...) 103 if err != nil { 104 return err 105 } 106 defer stream.Close() 107 108 // create client request read loop 109 go readLoop(req, stream) 110 111 // get raw response 112 resp := stream.Response() 113 114 // create server response write loop 115 for { 116 // read backend response body 117 body, err := resp.Read() 118 if err == io.EOF { 119 return nil 120 } else if err != nil { 121 return err 122 } 123 124 // read backend response header 125 hdr := resp.Header() 126 127 // write raw response header to client 128 rsp.WriteHeader(hdr) 129 130 // write raw response body to client 131 err = rsp.Write(body) 132 if err == io.EOF { 133 return nil 134 } else if err != nil { 135 return err 136 } 137 } 138 } 139 140 // NewProxy returns a new grpc proxy server 141 func NewProxy(opts ...options.Option) proxy.Proxy { 142 p := new(Proxy) 143 p.Options = options.NewOptions(opts...) 144 p.Options.Init(options.WithString("grpc")) 145 146 // get endpoint 147 ep, ok := p.Options.Values().Get("proxy.endpoint") 148 if ok { 149 p.Endpoint = ep.(string) 150 } 151 152 // get client 153 c, ok := p.Options.Values().Get("proxy.client") 154 if ok { 155 p.Client = c.(client.Client) 156 } 157 158 return p 159 } 160 161 // NewSingleHostProxy returns a router which sends requests to a single backend 162 func NewSingleHostProxy(url string) *Proxy { 163 return &Proxy{ 164 Endpoint: url, 165 } 166 }