Service discovery

What is service?

OASIS defines a service as “a mechanism that allows access to one or more functions, where access is provided using a specified interface and is performed according to the constraints and policies specified in the service description”. 😰 😰 😰

  • Business module (User/Mission/VIP)
  • Base Component (IPDB/UUID)
  • Cache service (Redis /memcached)
  • Persistence services (Mysql/ELS/MNS)
  • Network Services (Nginx/LB)
  • .

What is service discovery?

The caller does not need to know the network location of the service provider (IP: port, etc.), but only the service name (such as user/item/mission) to invoke the service

Why is service discovery needed?

In modern cloud-based microservices applications, service instances are dynamically assigned network addresses. And, because of automatic scaling, failures, and upgrades, service instances change dynamically. Therefore, your client code needs a more sophisticated service discovery mechanism. Instead of reading network addresses in occasionally updated configuration files.

  1. Scenario 1: A new service needs to be launched:

    • Provider: configure domain name, nginx, load balancing, deployment code
    • Caller: Configure the service domain name to invoke specific services
  2. Scenario 2: Traffic explodes due to a hot event and needs to be expanded.

    • Provider: configure nginx, load balancing, deploy code
  3. Service discovery Scenario 1: A new service needs to be launched:

    • Provider: Deployment code (including registration service)
    • Caller: Deploys code (including query service)
  4. Service discovery Scenario 2: Traffic explodes due to a hot event and needs to be expanded.

    • Provider: deployment code (configurable for automatic expansion)

4. The process of service discovery

Title: Client discover provider -> Registry: Register service provider --> Registry: Health Check consumer -> Registry: Query service provider Network Information Registry -> Consumer: Ip:192.168.*.* Domain:8787 Consumer ->> Provider: Access the serviceCopy the code
Title: Server discovery provider -> Registry: Register service provider -> Registry: Health check Load Balancer -> Registry: Query service provider network information consumer -> Load Balancer: Query service provider network information load balancer -> Consumer: Forwarding Ip:192.168.*.* Domain:8787 Registry -> Load balancer: Ip:192.168.*.* Domain:8787 Consumer ->> Provider: access serviceCopy the code
The client The service side
Number of requests A less More than once
Consumer logic Built-in service discovery logic Client service discovery logic is not required
The use of A little more less

Existing solutions for service discovery

Stackshare comparison page

ZooKeeper Etcd Eureka Consul DNSSrv
Multi-data center
Discovery of Built-in Services
Bring your own health check
Built-in WebUi
Distributed Key/Value storage
Open source 2.0 closed source
consistency paxos raft raft
monitoring metrics metrics metrics
Using interfaces (multilingual capability) The client http/grpc http http/dns
CAP cp cp ap cp
Development of language JAVA GO JAVA GO

Source principle

├ ─ ─ endpoint_cache_test. Go ├ ─ ─ endpoint_cache. Go ├ ─ ─ endpointer. Go ├ ─ ─ endpointer_test. Go ├ ─ ─ instancer. Go ├ ─ ─ Go Flag School ── Registrar. Go Flag School ── Go Flag School ── Registrar. Go Flag School ── Go Flag School ── Registrar Go │ ├── Registrar. Go │ ├─ Registrar. Go │ ├─ Registrar. Go │ ├─ Registrar ├── ├.go │ ├── ├.go │ ├── ├.go │ ├.go │ ├── ├.go │ ├.go │ ├── ─ Go │ ├── Registrar │ ├─ Go │ ├─ Go │ ├─ Go │ ├─ Go │ ├─ Go │ ├─ Go │ ├─ Go │ ├─ Go │ ├─ Go │ ├─ Go │ ├─ Go │ ├─ Go │ ├─ Go │ ├─ Go │ ├─ Go │ ├─ Go │ ├─ Go │ ├ ─ ─ instancer_test. Go │ ├ ─ ─ instancer. Go │ ├ ─ ─ client_test. Go │ ├ ─ ─ integration_test. Go │ ├ ─ ─ client. Go │ ├ ─ ─ Registrar. Go │ ├── EtcDV3 │ ├── Registrar. Go │ ├─ EtcDV3 │ ├── ─ Go │ ├── ─ ├── Registrar. Go │ ├── Registrar. Go │ ├── Registrar. Go │ ├── Registrar ├─ Round_robin_test. Go │ ├─ Round_robin_test. Go │ ├─ Round_robin_test Scheduling the Endpoint round_robin. Go (polling) │ ├ ─ ─ the random. Go (randomly selected Endpoint) │ ├ ─ ─ balancer. Go (contains an Endpoint interface) │ └ ─ ─ doc. Go ├ ─ ─ eureka │ ├── Registrar. Go │ ├─ Registrar. Go │ ├─ Registrar. Go │ ├─ Registrar Instancer_test.go │ ├─ ├─ DNSSRV By SRV record to realize service discovery [DNS SRV introduction] (https://www.lijiaocn.com/%E6%8A%80%E5%B7%A7/2017/03/06/dns-srv.html)) │ ├ ─ ─ instancer. Go │ ├ ─ ─ Go │ ├── lookup. Go │ ├── ├─ go ├─ go ├─ internalCopy the code

consul

The basic use

registered

var client consulsd.Client
{
    consulConfig := api.DefaultConfig()
    consulConfig.Address = "localhost:8500"
    consulClient, err := api.NewClient(consulConfig)
    iferr ! =nil {
        logger.Log("err", err)
        os.Exit(1)
    }
    client = consulsd.NewClient(consulClient)
}

check := api.AgentServiceCheck{
    HTTP:     "http://127.0.0.1:8080/health",
    Interval: "10s",
    Timeout:  "1s",
    Notes:    "Basic Monitoring inspection",
}

num := rand.Intn(100) // to make service ID unique
register := consulsd.NewRegistrar(client, &api.AgentServiceRegistration{
    ID:      "hello" + strconv.Itoa(num),
    Name:    "hello",
    Tags:    []string{"hello"."hi"},
    Port:    8080,
    Address: "http://127.0.0.1",
    Check:   &check,
}, logger)

register.Register()
Copy the code

found

var client consulsd.Client
{
    consulConfig := api.DefaultConfig()

    consulConfig.Address = "http://localhost:8500"
    consulClient, err := api.NewClient(consulConfig)
    iferr ! =nil {
        logger.Log("err", err)
        os.Exit(1)
    }
    client = consulsd.NewClient(consulClient)
}

tags := []string{}
passingOnly := true
duration := 500 * time.Millisecond
ctx := context.Background()
factory := helloFactory(ctx, "GET"."hello")
instancer := consulsd.NewInstancer(client, logger, "hello", tags, passingOnly)
endpointer := sd.NewEndpointer(instancer, factory, logger)
balancer := lb.NewRoundRobin(endpointer)
retry := lb.Retry(1, duration, balancer)
res, _ := retry(ctx, struct{} {})Copy the code

The underlying principle

The directory structure

.School exercises ── School exercises. School Exercises ── School Exercises The registrar. Go └ ─ ─ registrar_test. GoCopy the code

The main files in the directory are client.go instancer.go Registrar

client.go

// Client is a wrapper around the Consul API.
type Client interface {
	// Register a service with the local agent.
	Register(r *consul.AgentServiceRegistration) error

	// Deregister a service with the local agent.
	Deregister(r *consul.AgentServiceRegistration) error

	// Service
	Service(service, tag string, passingOnly bool, queryOpts *consul.QueryOptions) ([]*consul.ServiceEntry, *consul.QueryMeta, error)
}

type client struct {
	consul *consul.Client
}

// NewClient returns an implementation of the Client interface, wrapping a
// concrete Consul client.
func NewClient(c *consul.Client) Client {
	return &client{consul: c}
}

func (c *client) Register(r *consul.AgentServiceRegistration) error {
	return c.consul.Agent().ServiceRegister(r)
}

func (c *client) Deregister(r *consul.AgentServiceRegistration) error {
	return c.consul.Agent().ServiceDeregister(r.ID)
}

func (c *client) Service(service, tag string, passingOnly bool, queryOpts *consul.QueryOptions) ([]*consul.ServiceEntry, *consul.QueryMeta, error) {
	return c.consul.Health().Service(service, tag, passingOnly, queryOpts)
}
Copy the code

It mainly contains the following four functions

  • NewClient creates consul client
  • Register Service
  • Deregister deregisters the service
  • Service Obtains or discovers the Service

registrar.go

// Registrar registers service instance liveness information to Consul.
type Registrar struct {
	client       Client
	registration *stdconsul.AgentServiceRegistration
	logger       log.Logger
}

// NewRegistrar returns a Consul Registrar acting on the provided catalog
// registration.
func NewRegistrar(client Client, r *stdconsul.AgentServiceRegistration, logger log.Logger) *Registrar {
	return &Registrar{
		client:       client,
		registration: r,
		logger:       log.With(logger, "service", r.Name, "tags", fmt.Sprint(r.Tags), "address", r.Address),
	}
}

// Register implements sd.Registrar interface.
func (p *Registrar) Register(a) {
	iferr := p.client.Register(p.registration); err ! =nil {
		p.logger.Log("err", err)
	} else {
		p.logger.Log("action"."register")}}// Deregister implements sd.Registrar interface.
func (p *Registrar) Deregister(a) {
	iferr := p.client.Deregister(p.registration); err ! =nil {
		p.logger.Log("err", err)
	} else {
		p.logger.Log("action"."deregister")}}Copy the code

Contains the following three functions

  • NewRegistrar create the Registrar
  • Register invokes the Register method in client.go
  • Deregister calls the Deregister method in client.go

instancer.go

// Instancer yields instances for a service in Consul.
type Instancer struct {
	cache       *instance.Cache
	client      Client
	logger      log.Logger
	service     string
	tags        []string
	passingOnly bool
	quitc       chan struct{}}// NewInstancer returns a Consul instancer that publishes instances for the
// requested service. It only returns instances for which all of the passed tags
// are present.
func NewInstancer(client Client, logger log.Logger, service string, tags []string, passingOnly bool) *Instancer 

// Stop terminates the instancer.
func (s *Instancer) Stop(a)

func (s *Instancer) loop(lastIndex uint64)

func (s *Instancer) getInstances(lastIndex uint64, interruptc chan struct{}) ([]string.uint64, error)

// Register implements Instancer.
func (s *Instancer) Register(ch chan<- sd.Event)

// Deregister implements Instancer.
func (s *Instancer) Deregister(ch chan<- sd.Event)

func makeInstances(entries []*consul.ServiceEntry) []string
Copy the code

It consists of the following five functions

  • NewInstancer
    • Call the getInstances function to obtain the corresponding set of service addresses and construct the Instancer structure
  • loop
    • The circular query calls the getInstances function, which is called every 10 milliseconds by default
  • Stop
    • To close the service listening, the getInstances function gets an errStopped and stops the loop
  • Register
  • Deregister