strip back the grpc proxy
This commit is contained in:
parent
f838c33008
commit
dcf040ec9f
@ -1,29 +1,28 @@
|
|||||||
// Package grpc transparently forwards the grpc protocol using a go-micro client.
|
// Package grpc is a grpc proxy built for the go-micro/server
|
||||||
package grpc
|
package grpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/micro/go-micro/v3/client"
|
"github.com/micro/go-micro/v3/client"
|
||||||
"github.com/micro/go-micro/v3/client/grpc"
|
grpcc "github.com/micro/go-micro/v3/client/grpc"
|
||||||
"github.com/micro/go-micro/v3/codec"
|
"github.com/micro/go-micro/v3/codec"
|
||||||
|
"github.com/micro/go-micro/v3/codec/bytes"
|
||||||
|
"github.com/micro/go-micro/v3/errors"
|
||||||
|
"github.com/micro/go-micro/v3/logger"
|
||||||
"github.com/micro/go-micro/v3/proxy"
|
"github.com/micro/go-micro/v3/proxy"
|
||||||
"github.com/micro/go-micro/v3/server"
|
"github.com/micro/go-micro/v3/server"
|
||||||
|
"google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Proxy will transparently proxy requests to the backend.
|
// Proxy will transparently proxy requests to an endpoint.
|
||||||
// If no backend is specified it will call a service using the client.
|
// If no endpoint is specified it will call a service using the client.
|
||||||
// If the service matches the Name it will use the server.DefaultRouter.
|
|
||||||
type Proxy struct {
|
type Proxy struct {
|
||||||
// The proxy options
|
// embed options
|
||||||
options proxy.Options
|
options proxy.Options
|
||||||
|
|
||||||
// Endpoint specified the fixed endpoint to call.
|
// The client to use for outbound requests in the local network
|
||||||
Endpoint string
|
|
||||||
|
|
||||||
// The client to use for outbound requests
|
|
||||||
Client client.Client
|
Client client.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,6 +38,7 @@ func readLoop(r server.Request, s client.Stream) error {
|
|||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -50,6 +50,7 @@ func readLoop(r server.Request, s client.Stream) error {
|
|||||||
Header: hdr,
|
Header: hdr,
|
||||||
Body: body,
|
Body: body,
|
||||||
}
|
}
|
||||||
|
|
||||||
// write the raw request
|
// write the raw request
|
||||||
err = req.Codec().Write(msg, nil)
|
err = req.Codec().Write(msg, nil)
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
@ -66,46 +67,100 @@ func (p *Proxy) ProcessMessage(ctx context.Context, msg server.Message) error {
|
|||||||
// TODO: check that we're not broadcast storming by sending to the same topic
|
// TODO: check that we're not broadcast storming by sending to the same topic
|
||||||
// that we're actually subscribed to
|
// that we're actually subscribed to
|
||||||
|
|
||||||
|
if logger.V(logger.TraceLevel, logger.DefaultLogger) {
|
||||||
|
logger.Tracef("Proxy received message for %s", msg.Topic())
|
||||||
|
}
|
||||||
|
|
||||||
// directly publish to the local client
|
// directly publish to the local client
|
||||||
return p.Client.Publish(ctx, msg)
|
return p.Client.Publish(ctx, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeRequest honours the server.Proxy interface
|
// ServeRequest honours the server.Router interface
|
||||||
func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error {
|
func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error {
|
||||||
// set default client
|
// service name to call
|
||||||
if p.Client == nil {
|
|
||||||
p.Client = grpc.NewClient()
|
|
||||||
}
|
|
||||||
|
|
||||||
opts := []client.CallOption{}
|
|
||||||
|
|
||||||
// service name
|
|
||||||
service := req.Service()
|
service := req.Service()
|
||||||
|
// endpoint to call
|
||||||
endpoint := req.Endpoint()
|
endpoint := req.Endpoint()
|
||||||
|
|
||||||
// call a specific backend
|
if len(service) == 0 {
|
||||||
if len(p.Endpoint) > 0 {
|
return errors.BadRequest("go.micro.proxy", "service name is blank")
|
||||||
// address:port
|
|
||||||
if parts := strings.Split(p.Endpoint, ":"); len(parts) > 1 {
|
|
||||||
opts = append(opts, client.WithAddress(p.Endpoint))
|
|
||||||
// use as service name
|
|
||||||
} else {
|
|
||||||
service = p.Endpoint
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if logger.V(logger.TraceLevel, logger.DefaultLogger) {
|
||||||
|
logger.Tracef("Proxy received request for %s %s", service, endpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := []client.CallOption{
|
||||||
|
client.WithRetries(0),
|
||||||
|
}
|
||||||
|
|
||||||
|
// serve the normal way
|
||||||
|
return p.serveRequest(ctx, p.Client, service, endpoint, req, rsp, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Proxy) serveRequest(ctx context.Context, link client.Client, service, endpoint string, req server.Request, rsp server.Response, opts ...client.CallOption) error {
|
||||||
|
// read initial request
|
||||||
|
body, err := req.Read()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// create new request with raw bytes body
|
// create new request with raw bytes body
|
||||||
creq := p.Client.NewRequest(service, endpoint, nil, client.WithContentType(req.ContentType()))
|
creq := link.NewRequest(service, endpoint, &bytes.Frame{Data: body}, client.WithContentType(req.ContentType()))
|
||||||
|
|
||||||
|
// not a stream so make a client.Call request
|
||||||
|
if !req.Stream() {
|
||||||
|
crsp := new(bytes.Frame)
|
||||||
|
|
||||||
|
// make a call to the backend
|
||||||
|
if err := link.Call(ctx, creq, crsp, opts...); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the response
|
||||||
|
if err := rsp.Write(crsp.Data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// new context with cancel
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
|
||||||
// create new stream
|
// create new stream
|
||||||
stream, err := p.Client.Stream(ctx, creq, opts...)
|
stream, err := link.Stream(ctx, creq, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer stream.Close()
|
defer stream.Close()
|
||||||
|
|
||||||
// create client request read loop
|
// with a grpc stream we have to refire the initial request
|
||||||
go readLoop(req, stream)
|
// client request to start the server side
|
||||||
|
|
||||||
|
// get the header from client
|
||||||
|
msg := &codec.Message{
|
||||||
|
Type: codec.Request,
|
||||||
|
Header: req.Header(),
|
||||||
|
Body: body,
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the raw request
|
||||||
|
err = stream.Request().Codec().Write(msg, nil)
|
||||||
|
if err == io.EOF {
|
||||||
|
return nil
|
||||||
|
} else if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// create client request read loop if streaming
|
||||||
|
go func() {
|
||||||
|
err := readLoop(req, stream)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
// cancel the context
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// get raw response
|
// get raw response
|
||||||
resp := stream.Response()
|
resp := stream.Response()
|
||||||
@ -114,6 +169,15 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server
|
|||||||
for {
|
for {
|
||||||
// read backend response body
|
// read backend response body
|
||||||
body, err := resp.Read()
|
body, err := resp.Read()
|
||||||
|
if err != nil {
|
||||||
|
// when we're done if its a grpc stream we have to set the trailer
|
||||||
|
if cc, ok := stream.(grpc.ClientStream); ok {
|
||||||
|
if ss, ok := resp.Codec().(grpc.ServerStream); ok {
|
||||||
|
ss.SetTrailer(cc.Trailer())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
return nil
|
return nil
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
@ -140,23 +204,25 @@ func (p *Proxy) String() string {
|
|||||||
return "grpc"
|
return "grpc"
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewProxy returns a new grpc proxy server
|
// NewProxy returns a new proxy which will route based on mucp headers
|
||||||
func NewProxy(opts ...proxy.Option) proxy.Proxy {
|
func NewProxy(opts ...proxy.Option) proxy.Proxy {
|
||||||
var options proxy.Options
|
var options proxy.Options
|
||||||
|
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&options)
|
o(&options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create a new grpc proxy
|
||||||
p := new(Proxy)
|
p := new(Proxy)
|
||||||
p.Endpoint = options.Endpoint
|
p.options = options
|
||||||
|
|
||||||
|
// set the client
|
||||||
p.Client = options.Client
|
p.Client = options.Client
|
||||||
|
|
||||||
|
// set the default client
|
||||||
|
if p.Client == nil {
|
||||||
|
p.Client = grpcc.NewClient()
|
||||||
|
}
|
||||||
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSingleHostProxy returns a router which sends requests to a single backend
|
|
||||||
func NewSingleHostProxy(url string) *Proxy {
|
|
||||||
return &Proxy{
|
|
||||||
Endpoint: url,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user