文章目录
上一章 ServiceComb 开发实战(1) —— Service-Center 安装部署 中,主要了解了一下 Service-Center(SC)的安装和启动(不太了解的童鞋可以点击链接查看)。在文章末尾我们使用了 curl 命令模拟 provider 与 consumer 服务的注册与发现流程,本章将通过官方的 api 文档,动手构建对接示例。
一. 服务注册发现
为什么使用服务注册发现
在微服务架构中,一个应用由一组职责单一化的服务组成。在分布式系统中,各个服务会被动态的部署到不同的节点,为了高可用性,一些服务亦会出现多实例的情况。面对这样一组服务,加上服务的动态伸缩容,如果仅通过人工配置的形式来管理服务之间的依赖,这将是一件无比艰巨的维护任务。于是诞生了“注册中心”这样的解决方案,它提供了注册机制,让服务将自己的信息登记到中心;提供了发现机制,供服务从中心查找其他的服务信息。
服务注册发现流程
下面是来自 Service-Center 官方的设计原理
为了方便理解,这里将整个流程与现实生活中的“找对象”流程类比:
启动流程:
1 待婚者(Provider)向 婚恋机构(Service-Center)提交 个人信息
2 婚恋机构(Service-Center)将 待婚者(Provider)个人信息存储起来(ETCD)
3 求偶者(Consumer)向 婚恋机构(Service-Center)获取符合条件的 待婚者(Provider)信息
4 求偶者(Consumer)将 待婚者(Provider)信息存储到通讯录(cache)
5 求偶者(Consumer)向 婚恋机构(Service-Center)订阅 待婚者(Provider)动态
通讯流程:
1 婚恋机构(Service-Center)制定了契约,要求 待婚者(Provider)每30s向自己报告健康状况(心跳保活),如果未收到报告,信息将会过期并被删除;
2 求偶者(Consumer)向 婚恋机构(Service-Center)订阅了 待婚者(Provider)动态,若有变化,将更新通讯录(cache);
3 求偶者(Consumer)从通讯录(cache)中获取联系方式(endpoints),并进行通讯。
二. Service-Center对接
1.创建项目
创建名为 “helloworld”的项目,以下为参考目录结构:
.
└── rest
├── common
│ ├── config
│ │ └── config.go
│ │ // 配置文件解析
│ │
│ ├── restful
│ │ └── restutil.go
│ │ // http 请求简单封装
│ │
│ └── servicecenter
│ └── v3
│ └── registery.go
│ // Service-Center v3 接口 client 实现
├── consumer
│ ├── conf
│ │ └── microservice.yaml
│ │ // 微服务配置
│ │
│ └── helloclient.go
│ // 消费端入口
│
└── provider
├── conf
│ └── microservice.yaml
│ // 微服务配置
│
└── helloserver.go
// 服务端入口
2. v3 接口 client 实现
Service-Center 的服务注册与发现是基于 RESTful 标准接口实现的,与编程语言无关,以下内容基于 golang 进行实现,仅因本人对 golang 比较熟悉,其他语言可以参考官网 API 文档 进行实现。
文件位置:rest/common/servicecenter/v3/registery.go 文件
var (
// 接口 API 定义
microServices = "/registry/v3/microservices"
svcInstances = "/registry/v3/microservices/%s/instances"
discovery = "/registry/v3/instances"
existence = "/registry/v3/existence"
heartbeats = "/registry/v3/heartbeats"
watcher = "/registry/v3/microservices/%s/watcher"
microServiceType sourceType = "microservice"
schemaType sourceType = "schema"
)
type sourceType string
type Client struct {
rawURL string
domain string
}
func NewClient(addr string, domain string) *Client {
return &Client{
rawURL: addr, domain: domain}
}
// 查询微服务是否存在
func (c *Client) existence(params url.Values) (*proto.GetExistenceResponse, error) {
reqURL := c.rawURL + existence + "?" + params.Encode()
// 对http.NewRequest接口的简单封装,详情请见 rest/common/restful/restutil.go
req, err := restful.NewRequest(http.MethodGet, reqURL, c.DefaultHeaders(), nil)
if err == nil {
respData := &proto.GetExistenceResponse{
}
// 对http.Do接口的简单封装,详情请见 rest/common/restful/restutil.go
err = restful.DoRequest(req, respData)
if err == nil {
return respData, nil
}
}
return nil, err
}
// 获取微服务服务ID
func (c *Client) GetServiceID(svc *config.ServiceConf) (string, error) {
val := url.Values{
}
val.Set("type", string(microServiceType))
val.Set("appId", svc.AppID)
val.Set("serviceName", svc.Name)
val.Set("version", svc.Version)
respData, err := c.existence(val)
if err == nil {
return respData.ServiceId, nil
}
return "", fmt.Errorf("[GetServiceID]: %s", err)
}
// 注册微服务
func (c *Client) RegisterService(svc *config.ServiceConf) (string, error) {
ms := &proto.CreateServiceRequest{
Service: &proto.MicroService{
AppId: svc.AppID,
ServiceName: svc.Name,
Version: svc.Version,
},
}
reqURL := c.rawURL + microServices
req, err := restful.NewRequest(http.MethodPost, reqURL, c.DefaultHeaders(), ms)
if err == nil {
respData := &proto.CreateServiceResponse{
}
err = restful.DoRequest(req, respData)
if err == nil {
return respData.ServiceId, nil
}
}
return "", fmt.Errorf("[RegisterService]: %s", err)
}
// 注册微服务实例
func (c *Client) RegisterInstance(svcID string, ins *config.InstanceConf) (string, error) {
endpoint := ins.Protocol + "://" + ins.ListenAddress
ms := &proto.RegisterInstanceRequest{
Instance: &proto.MicroServiceInstance{
HostName: ins.Hostname,
Endpoints: []