github.com/gogf/gf/v2@v2.7.4/net/gclient/gclient_discovery.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/gogf/gf.
     6  
     7  package gclient
     8  
     9  import (
    10  	"context"
    11  	"net/http"
    12  
    13  	"github.com/gogf/gf/v2/container/gmap"
    14  	"github.com/gogf/gf/v2/errors/gcode"
    15  	"github.com/gogf/gf/v2/errors/gerror"
    16  	"github.com/gogf/gf/v2/internal/intlog"
    17  	"github.com/gogf/gf/v2/net/gsel"
    18  	"github.com/gogf/gf/v2/net/gsvc"
    19  	"github.com/gogf/gf/v2/text/gstr"
    20  )
    21  
    22  type discoveryNode struct {
    23  	service gsvc.Service
    24  	address string
    25  }
    26  
    27  // Service is the client discovery service.
    28  func (n *discoveryNode) Service() gsvc.Service {
    29  	return n.service
    30  }
    31  
    32  // Address returns the address of the node.
    33  func (n *discoveryNode) Address() string {
    34  	return n.address
    35  }
    36  
    37  // service prefix to its selector map cache.
    38  var clientSelectorMap = gmap.New(true)
    39  
    40  // internalMiddlewareDiscovery is a client middleware that enables service discovery feature for client.
    41  func internalMiddlewareDiscovery(c *Client, r *http.Request) (response *Response, err error) {
    42  	if c.discovery == nil && !isServiceName(r.URL.Host) {
    43  		return c.Next(r)
    44  	}
    45  	var (
    46  		ctx     = r.Context()
    47  		service gsvc.Service
    48  	)
    49  	service, err = gsvc.GetAndWatchWithDiscovery(ctx, c.discovery, r.URL.Host, func(service gsvc.Service) {
    50  		intlog.Printf(ctx, `http client watching service "%s" changed`, service.GetPrefix())
    51  		if v := clientSelectorMap.Get(service.GetPrefix()); v != nil {
    52  			if err = updateSelectorNodesByService(ctx, v.(gsel.Selector), service); err != nil {
    53  				intlog.Errorf(ctx, `%+v`, err)
    54  			}
    55  		}
    56  	})
    57  	if err != nil {
    58  		if gerror.Code(err) == gcode.CodeNotFound {
    59  			intlog.Printf(
    60  				ctx,
    61  				`service discovery error with url "%s:%s":%s`,
    62  				r.Method, r.URL.String(), err.Error(),
    63  			)
    64  			return c.Next(r)
    65  		}
    66  	}
    67  	if service == nil {
    68  		return c.Next(r)
    69  	}
    70  	// Balancer.
    71  	var (
    72  		selectorMapKey   = service.GetPrefix()
    73  		selectorMapValue = clientSelectorMap.GetOrSetFuncLock(selectorMapKey, func() interface{} {
    74  			intlog.Printf(ctx, `http client create selector for service "%s"`, selectorMapKey)
    75  			selector := c.builder.Build()
    76  			// Update selector nodes.
    77  			if err = updateSelectorNodesByService(ctx, selector, service); err != nil {
    78  				return nil
    79  			}
    80  			return selector
    81  		})
    82  	)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	selector := selectorMapValue.(gsel.Selector)
    87  	// Pick one node from multiple addresses.
    88  	node, done, err := selector.Pick(ctx)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  	if done != nil {
    93  		defer done(ctx, gsel.DoneInfo{})
    94  	}
    95  	r.Host = node.Address()
    96  	r.URL.Host = node.Address()
    97  	return c.Next(r)
    98  }
    99  
   100  func updateSelectorNodesByService(ctx context.Context, selector gsel.Selector, service gsvc.Service) error {
   101  	nodes := make(gsel.Nodes, 0)
   102  	for _, endpoint := range service.GetEndpoints() {
   103  		nodes = append(nodes, &discoveryNode{
   104  			service: service,
   105  			address: endpoint.String(),
   106  		})
   107  	}
   108  	return selector.Update(ctx, nodes)
   109  }
   110  
   111  // isServiceName checks and returns whether given input parameter is service name or not.
   112  // It checks by whether the parameter is address by containing port delimiter character ':'.
   113  //
   114  // It does not contain any port number if using service discovery.
   115  func isServiceName(serviceNameOrAddress string) bool {
   116  	return !gstr.Contains(serviceNameOrAddress, gsvc.EndpointHostPortDelimiter)
   117  }