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  }