php数据库redios_red

服务发现

一、什么是服务?

OASIS将服务定义为“一种允许访问一个或多个功能的机制,其中使用指定的接口提供访问,并按照服务描述指定的约束和策略执行访问”。😰😰😰

业务模块(user/mission/vip)

基础组件(ipdb/uuid)

缓存服务(redis/memcached)

持久化服务(Mysql/ELS/MNS)

网络服务(nginx/lb)

...

二、什么是服务发现?

调用方无需知道服务提供者的网络位置(ip:端口等),只需通过服务名称(如user/item/mission),即可调用服务

三、为什么需要服务发现?

在现代的基于云计算的微服务应用中,服务实例会被动态地分配网络地址。并且,因为自动伸缩、故障和升级,服务实例会动态地改变。故而,你的客户端代码需要用一种更加精密的服务发现机制。而不是偶尔更新的配置文件中读取到网络地址。

场景1: 需要新上线一个服务:

提供者:配置域名、nginx、负载均衡、部署代码

调用方:配置服务域名,调用具体业务

场景2: 某个热点事件的出现,导致流量爆增,需要扩容:

提供者:配置nginx、负载均衡、部署代码

服务发现场景1: 需要新上线一个服务:

提供者:部署代码(包含注册服务)

调用方:部署代码(包含查询服务)

服务发现场景2: 某个热点事件的出现,导致流量爆增,需要扩容:

提供者:部署代码(可配置自动扩容)

四、服务发现的流程

Title: 客户端发现

提供者->注册中心: 注册服务

提供者-->注册中心: 健康检查

消费者->注册中心: 查询服务提供者网络信息

注册中心->消费者: Ip:192.168.*.* Domain:8787

消费者->>提供者: 访问服务

Title: 服务端发现

提供者->注册中心: 注册服务

提供者-->注册中心: 健康检查

负载均衡器->注册中心: 查询服务提供者网络信息

消费者->负载均衡器: 查询服务提供者网络信息

负载均衡器->消费者: 转发 Ip:192.168.*.* Domain:8787

注册中心->负载均衡器: Ip:192.168.*.* Domain:8787

消费者->>提供者: 访问服务

客户端

服务端

请求数

少一次

多一次

消费者逻辑

内置服务发现逻辑

无需客户端服务发现逻辑

业界使用

多一些

少一些

五、服务发现的现有解决方案

ZooKeeper

Etcd

Eureka

Consul

DNSSrv

多数据中心

自带服务发现

自带健康检查

自带WebUi

分布式Key/Value存储

开源

2.0闭源

一致性

paxos

raft

raft

监控

metrics

metrics

metrics

使用接口(多语言能力)

客户端

http/grpc

http

http/dns

CAP

cp

cp

ap

cp

开发语言

JAVA

GO

JAVA

GO

源码原理

├── endpoint_cache_test.go

├── endpoint_cache.go

├── endpointer.go

├── endpointer_test.go

├── instancer.go

├── factory.go

├── benchmark_test.go

├── registrar.go

├── doc.go

├── etcd

│   ├── client_test.go

│   ├── client.go

│   ├── integration_test.go

│   ├── registrar.go

│   ├── registrar_test.go

│   ├── example_test.go

│   ├── instancer.go

│   ├── instancer_test.go

│   └── doc.go

├── zk

│   ├── client.go

│   ├── integration_test.go

│   ├── client_test.go

│   ├── instancer_test.go

│   ├── util_test.go

│   ├── instancer.go

│   ├── registrar.go

│   ├── logwrapper.go

│   └── doc.go

├── consul

│   ├── instancer_test.go

│   ├── instancer.go

│   ├── client_test.go

│   ├── integration_test.go

│   ├── client.go

│   ├── registrar.go

│   ├── registrar_test.go

│   └── doc.go

├── etcdv3

│   ├── integration_test.go

│   ├── client.go

│   ├── registrar_test.go

│   ├── registrar.go

│   ├── example_test.go

│   ├── instancer.go

│   ├── instancer_test.go

│   └── doc.go

├── lb(负载均衡)

│   ├── retry_test.go

│   ├── retry.go (多次尝试请求Endpoint)

│   ├── round_robin_test.go

│   ├── random_test.go

│   ├── round_robin.go (轮询调度Endpoint)

│   ├── random.go (随机选择Endpoint)

│   ├── balancer.go (包含一个Endpoint的接口)

│   └── doc.go

├── eureka

│   ├── util_test.go

│   ├── registrar.go

│   ├── integration_test.go

│   ├── registrar_test.go

│   ├── instancer.go

│   ├── instancer_test.go

│   └── doc.go

├── dnssrv(通过net包的dns客户端,通过SRV记录实现服务发现 [DNS SRV介绍](https://www.lijiaocn.com/%E6%8A%80%E5%B7%A7/2017/03/06/dns-srv.html))

│   ├── instancer.go

│   ├── instancer_test.go

│   ├── lookup.go

│   └── doc.go

└── internal(内部通过管道实现的应用内服务发现)

└── instance

consul

基本使用

注册

var client consulsd.Client

{

consulConfig := api.DefaultConfig()

consulConfig.Address = "localhost:8500"

consulClient, err := api.NewClient(consulConfig)

if err != 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: "基础监控检查",

}

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()

发现

var client consulsd.Client

{

consulConfig := api.DefaultConfig()

consulConfig.Address = "http://localhost:8500"

consulClient, err := api.NewClient(consulConfig)

if err != 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{}{})

底层原理

目录结构

.

├── client.go

├── client_test.go

├── doc.go

├── instancer.go

├── instancer_test.go

├── integration_test.go

├── registrar.go

└── registrar_test.go

目录中主要的是这三个文件,client.go instancer.go registrar.go

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)

}

主要包含以下4个函数

NewClient 创建consul客户端

Register 注册服务

Deregister 注销服务

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() {

if err := 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() {

if err := p.client.Deregister(p.registration); err != nil {

p.logger.Log("err", err)

} else {

p.logger.Log("action", "deregister")

}

}

包含以下3个函数

NewRegistrar 创建 Registrar

Register 调用 client.go中 Register 方法

Deregister 调用 client.go中 Deregister 方法

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()

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

// Deregister implements Instancer.

func (s *Instancer) Deregister(ch chan

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

主要包含以下5个函数

NewInstancer

调用 getInstances 函数,获取对应的一组服务地址,构造 Instancer 结构体

loop

循环查询调用 getInstances 函数,默认10毫秒调用一次

Stop

关闭服务监听, getInstances 函数获得一个 errStopped,停止loop

Register

Deregister

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值