Welcome to go-chassis’s documentation!

Introductions

Concepts

Registry
注册中心负责微服务的注册和发现
Registrator
自注册组件,go-chassis在启动后会连接注册中心,自注册服务信息
Service Discovery
服务发现组件,负责服务发现并周期性轮询注册中心中的服务到本地缓存。
Protocol server and client
支持开发者自己将协议逻辑插入到go chassis中,接入统一的治理和微服务管理当中

Features

  • 注册发现:帮助微服务自注册并发现其他微服务
  • 插件化注册中心:默认对接Service Center和文件系统,开发者可自己扩展Kubernetes,Consul,Eureka等服务
  • 限流:提供客户端与服务端限流
  • 负载均衡:提供Filter与Strategy2种方式对实例进行选择,并可定制
  • 熔断:可通过超时时间,错误率,并发量等条件进行熔断控制,保护系统,防止雪崩效应
  • 降级:熔断后可配置降级策略
  • 处理链:支持在一次请求调用中,插入自己的处理逻辑
  • 插件化协议:默认支持http,Highway RPC 2种协议
  • 插件化Cipher:在证书,aksk等敏感数据加载时,支持使用自己的加解密算法
  • Metrics:支持导出Prometheus格式监控数据
  • Tracing:分布式调用链追踪,支持对接Zipkin
  • 日志:支持扩展自己的Writer实现,可上报给kafka,Elasticseach等组件,默认支持本地文件和stdout
  • 动态配置框架:对接不同Source,当Source中的配置项出现变化,将触发事件,让微服务感知,用户可自定义事件触发的动作
  • 配置热加载:负载均衡,熔断,降级等等配置支持运行时热加载

How it works

_images/how.png

这里解释运行时发生了什么

不同协议请求进入到各协议Server,Server将具体的协议请求转换为Invocation统一抽象模型,并传入Handler chain,在这里Chassis已经默认实现了很多的Handler,比如熔断,限流等,最终再进入Transport handler,使用具体的协议客户端传输到目标。

每次请求生成的监控数据通过http API导出的方式,由Prometheus收集处理

日志可通过扩展,输出到kafka等服务中也可使用华为公有云APM服务收集

注册中心默认对接Service center

Archaius为动态配置框架,可从各种不同的source中读取配置

Get started

最小化安装

  1. Install go 1.8+
  2. Clone the project
git clone git@github.com:ServiceComb/go-chassis.git
  1. Use glide to download deps
cd go-chassis 
glide intall
  1. Install ServiceComb service-center

使用RPC通信

安装protobuff 3.2.0 https://github.com/google/protobuf

安装golang插件 https://github.com/golang/protobuf

Writing Rest service

服务端 1个工程或者go package,推荐结构如下

server/

├── conf

│ ├── chassis.yaml

│ └── microservice.yaml

└── main.go

1.编写接口

type RestFulHello struct {}

func (r *RestFulHello) Sayhello(b *restful.Context) {
    b.Write([]byte("get user id: " + b.ReadPathParameter("userid")))
}

2.注册路由

func (s *RestFulHello) URLPatterns() []restful.Route {
    return []restful.RouteSpec{
        {http.MethodGet, "/sayhello/{userid}", "Sayhello"},
    }
}

3.注册接口

第一个参数表示你要向哪个协议注册,第三个为schema ID,为可选,会在调用中使用

chassis.RegisterSchema(“rest”, &RestFulHello{}, server.WithSchemaId(“RestHelloService”)) 说明:

想注册的rest协议的接口,必须实现URLPatterns方法定义路由 路由中暴露为API的方法都要入参均为*restful.Context

4.修改配置文件chassis.yaml

cse:
  service:
    registry:
      address: http://127.0.0.1:30100
  protocols:
    rest:
      listenAddress: 127.0.0.1:5001

5.修改microservice.yaml, 为服务起名

service_description:
  name: RESTServer

6.main.go中启动服务

func main() {
    //start all server you register in server/schemas.
    if err := chassis.Init(); err != nil {
        lager.Logger.Error("Init failed.", err)
        return
    }
    chassis.Run()
}

客户端 1个工程或者go package,推荐结构如下

client/

├── conf

│ ├── chassis.yaml

│ └── microservice.yaml

└── main.go

1.修改配置文件chassis.yaml

cse:
  service:
    registry:
      address: http://127.0.0.1:30100

2.修改microservice.yaml

service_description: name: RESTClient 3.main中调用服务端,请求包括服务名,schema,operation及参数

func main() {
    //Init framework
    if err := chassis.Init(); err != nil {
        lager.Logger.Error("Init failed.", err)
        return
    }
    req, _ := rest.NewRequest("GET", "cse://RESTServer/sayhello/world")
    defer req.Close()
    resp, err := core.NewRestInvoker().ContextDo(context.TODO(), req)
    if err != nil {
        lager.Logger.Error("error", err)
        return
    }
    defer resp.Close()
    lager.Logger.Info(string(resp.ReadBody()))
}

Notice

if conf folder is not under work dir, plz export CHASSIS_HOME=/path/to/conf/parent_folder or CHASSIS_CONF_DIR==/path/to/conf_folder

Writing RPC service

定义请求与返回结构体 1个工程或者go package,推荐结构如下

schemas

├── helloworld

│ ├──helloworld.proto

1.定义helloworld.proto文件

syntax = "proto3";

package helloworld;


// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

2.通过pb生成go文件 helloworld.pb.go

protoc –go_out=. hello.proto 将生成的go文件拷贝到目录中

schemas

├── helloworld

│ ├──helloworld.proto

│ └──helloworld.pb.go

服务端 1个工程或者go package,推荐结构如下

server/

├── conf

│ ├── chassis.yaml

│ └── microservice.yaml

└── main.go

1.编写接口

type HelloServer struct {
}
func (s *HelloServer) SayHello(ctx context.Context, in *helloworld.HelloRequest) (*helloworld.HelloReply, error) {
    return &helloworld.HelloReply{Message: "Go Hello  " + in.Name}, nil
}

2.注册接口

第一个参数表示你要向哪个协议注册,第三个为schema ID,会在调用中使用

chassis.RegisterSchema(“highway”, &HelloServer{}, server.WithSchemaId(“HelloService”)) 说明:

想暴露为API的方法都要符合以下条件

第一个参数为context.Context

第二个参数必须是结构体指针

返回的第一个必须是结构体指针

返回的第二个为error

3.修改配置文件chassis.yaml

cse:
  service:
    registry:
      address: http://127.0.0.1:30100
  protocols:
    highway:
      listenAddress: 127.0.0.1:5000

4.修改microservice.yaml

service_description:
  name: Server

5.main.go中启动服务

func main() {
    //start all server you register in server/schemas.
    if err := chassis.Init(); err != nil {
        lager.Logger.Error("Init failed.", err)
        return
    }
    chassis.Run()
}

客户端 1个工程或者go package,推荐结构如下

client/

├── conf

│ ├── chassis.yaml

│ └── microservice.yaml

└── main.go

1.拿到pb文件生成go代码

protoc –go_out=. hello.proto 2.修改配置文件chassis.yaml

cse:
  service:
    registry:
      address: http://127.0.0.1:30100

3.修改microservice.yaml

service_description:
  name: Client

4.main中调用服务端,指定微服务名,schema,operation与参数和返回

//if you use go run main.go instead of binary run, plz export CHASSIS_HOME=/path/to/conf/folder
func main() {
    //Init framework
    if err := chassis.Init(); err != nil {
        lager.Logger.Error("Init failed.", err)
        return
    }
    //declare reply struct
    reply := &helloworld.HelloReply{}
    //Invoke with microservice name, schema ID and operation ID
    if err := core.NewRPCInvoker().Invoke(context.Background(), "Server", "HelloService", "SayHello", &helloworld.HelloRequest{Name: "Peter"}, reply); err != nil {
        lager.Logger.Error("error", err)
    }
    lager.Logger.Info(reply.Message)
}

Notice

if conf folder is not under work dir, plz export CHASSIS_HOME=/path/to/conf/parent_folder or CHASSIS_CONF_DIR==/path/to/conf_folder

User guides

微服务定义

Introduction

Use microservice.yaml to describe your service

Conceptions:

  • instance: one process is a micro service instance, instances belong to one micro service
  • service: service is a static information entity in storage, it has instances

you can consider a project as an micro service, after compile,build and run, it became a micro service instance

Configurations

name

(required, string) Micro service name

hostname

(optional, string) hostname of host, it can be IP or hostname,default is hostname return by os.hostname()

APPLICATION_ID

(optional, string) Application ID, default value is “default”

version

(optional, string) version number default is 0.0.1

properties

(optional, map) micro service metadata ,usually it is defined in project, and never changed

instance_properties

(optional, map) instance metadata, during runtime, if can be different based on environment

Example

service_description:
  name: Server
  hostname: 10.244.1.3
  properties:
    project: X1
  instance_properties:
    nodeIP: 192.168.0.111

Registry

概述

微服务的注册发现默认通过服务中心完成。 用户可以配置与服务中心的通信方式,服务中心地址,以及自身注册到服务中心的信息。 微服务启动过程中,会自动向服务中心进行注册。 在微服务运行过程中,go-chassis会周期从服务中心查询其他服务的实例信息缓存到本地

配置

注册中心相关配置分布在两个yaml文件中,分别为chassis.yaml和microservice.yaml文件。 chassis.yaml中配置使用的注册中心类型、注册中心的地址信息。

  • type: 对接的服务中心类型默认加载servicecenter和file两种,registry也支持用户定制registry并注册。
  • scope: 默认不允许跨应用间访问,只允许本应用间访问,当配置为full时则允许跨应用间访问,且能发现本租户全部微服务。
  • autodiscovery: 是否开启自动发现,开启后将从服务中心注册发现注册的其他服务中心。
  • register: 配置项默认为自动注册,即框架启动时完成实例的自动注册。当配置manual时,框架只会注册配置文件中的微服务,不会注册实例,使用者可以通过服务中心对外的API完成实例注册。
  • api.version: 目前只支持v4版本。

disabled

(optional, bool) 是否开启服务注册发现模块,默认为false

type

(optional, string) 对接服务中心插件类型,默认为servicecenter

scope

(optional, bool) 默认为full,允许跨app发现,填入app以禁止跨应用发现

autodiscovery

(optional, bool) 自动发现 服务中心集群节点 默认为false

address

(optional, bool)服务中心地址 允许配置多个以逗号隔开,默认为空

register

(optional, bool) 是否自动自注册,默认为 auto,可选manual

refreshInterval

(optional, string) 更新实例缓存的时间间隔,格式为数字加单位(s/m/h),如1s/1m/1h,默认为30s

api.version

(optional, string) 访问服务中心的api版本,默认为v4

watch

(optional, bool) 是否watch实例变化事件,默认为false

API

Registry提供以下接口供用户注册微服务及实例。以下方法通过Registry提供的相关接口实现,内置有两种Registry的实现,默认为servicecenter。另外支持用户自行定义实现Registry接口的插件,用于服务注册发现。

注册微服务实例
RegisterMicroserviceInstances() error
注册微服务
RegisterMicroservice() error
自定义Registry插件
InstallPlugin(name string, f func(opts ...Option) Registry)

示例

服务中心最简化配置只需要registry的address,注册的微服务实例通过appId、服务名和版本决定。

APPLICATION_ID: default #optional
cse:
  service:
    registry:
      disabled: false            #optional: 默认开启registry模块
      type: servicecenter        #optional: 默认类型为对接服务中心
      scope: full                #optional: scope为full注册时允许跨app
      autodiscovery: true       
      address: http://10.0.0.1:30100,http://10.0.0.2:30100 
      register: auto             #optional:默认为自动 [auto manual]
      refeshInterval : 30s       
      watch: true                         
      api:
        version: v4

服务发现

概述

Service discovery 是关于如何发现服务的配置。 和Registry的区别是,他仅负责发现服务,而不负责注册服务 Service Discovery与Registry只能选择其一进行配置 启用此功能可以与Istio的Pilot集成

配置

服务发现的配置在chassis.yaml。

type

(optional, string) 对接服务中心插件类型,默认为servicecenter

address

(optional, bool)服务中心地址 允许配置多个以逗号隔开,默认为空

refreshInterval

(optional, string) 更新实例缓存的时间间隔,格式为数字加单位(s/m/h),如1s/1m/1h,默认为30s

示例

cse:
  service:
    Registry:
      serviceDiscovery:
        type: pilot      
        address: http://istio-pilot.istio-system:8080
        refeshInterval : 30s                    

通信协议

概述

go-chassis支持不同协议扩展,不同的协议都可以接入到统一的微服务管理,控制,监控中。 目前支持HTTP1/2与RPC协议highway

配置

protocols.{protocol_name}

(required, string) 协议名,目前内置rest与highway

protocols.{protocol_name}.advertiseAddress

(optional, string) 协议广播地址,也就是向注册中心注册时的地址,在发现后进行通信时使用的网络地址

protocols.{protocol_name}.listenAddress

(required, string) 协议监听地址,建议配置为0.0.0.0:{port}, go chassis会自动为你计算advertiseAddress,无需手动填写,适合运行在容器中的场景,因为ip地址无法确定。

例子

cse:
  protocols:
    rest:
      listenAddress: 0.0.0.0:5000
    highway:
      listenAddress: 0.0.0.0:6000

Handler chain

概述

处理链中包含一系列handler, 在一次调用中可以通过扩展调用链的方式来插入定制的逻辑处理,如何开发新的Handler,可参考Developer Guide,本章节只讨论如何进行配置,以及框架已经实现的handler有哪些

配置

cse:
  handler:
    Consumer:
      {name}:{handler_names}
    Provider:
      {name}:{handler_names}

Consumer表示,当你要调用别的服务时,会通过的处理链 Provider表示,当你被别人调用人,会通过的处理链 支持在Consumer和Provider中定义多个不同的chain name 如果handler配置为空那么框架会自动为Consumer与Provider加载默认的handlers,chain的名称为default

Consumer的默认chain为

名称 功能

ratelimiter-consumer 客户端限流

bizkeeper-consumer 熔断降级

router 路由策略

loadbalance 负载均衡

tracing-consumer 客户端调用链追踪

transport 各协议客户端处理请求,如果你使用自定义处理链配置,那么结尾处必须加入这个handler

Provider的默认chain为

名称 功能

ratelimiter-provider 服务端限流

tracing-provider 服务端调用链追踪

bizkeeper-provider 服务端熔断

API

当处理链配置为空,用户也可自定义自己的默认处理链

//SetDefaultConsumerChains your custom chain map for Consumer,if there is no config, this default chain will take affect
func SetDefaultConsumerChains(c map[string]string)
//SetDefaultProviderChains set your custom chain map for Provider,if there is no config, this default chain will take affect
func SetDefaultProviderChains(c map[string]string)

实例

handler:
  chain:
    Consumer:
      default: bizkeeper-consumer, router, loadbalance, ratelimiter-consumer,transport
      custom: some-handler

客户端健康检查

概述

客户端健康检查(Health Check)是指客户端对服务端实例缓存进行健康性的判断。

在网络分区或延时较大的环境下,客户端可能会出现上报心跳到服务中心失败的情况,导致结果是,客户端会将收到的实例下线事件,并移除本地实例缓存,最终影响业务调用。

为防止上述情况发生,go-chassis提供这样的健康检查机制:当客户端监听到某个(或latest)版本的服务端可用实例下降到0个时, 客户端会在同步移除实例缓存前进行一次健康检查,调用服务端暴露的健康检查接口(RESTful或Highway)并校验其返回值来确认是否要移除实例缓存。

配置

服务端注册

go-chassis默认不会主动注册服务端的健康检查接口,需要用户主动import到项目中。

// 注册健康检查接口
import _ "github.com/ServiceComb/go-chassis/healthz/provider"

加入上述代码片段后,go-chassis会按照暴露的服务协议类型对应注册健康检查接口,接口描述如下

  • RESTful:

    1. Method: GET
    2. Path: /healthz
    3. Response:
    {
      "appId": "string",
      "serviceName": "string",
      "version": "string"
    }
    
  • Highway:

    1. Schema: _chassis_highway_healthz
    2. Operation: HighwayCheck
    3. Response:
    // The response message containing the microservice key
    message Reply {
      string appId = 1;
      string serviceName = 2;
      string version = 3;
    }
    
客户端配置

客户端健康检查配置在chassis.yaml。

healthCheck

(optional, bool) 允许对服务端的实例做健康检查,默认值为false。
示例
cse:
  service:
    Registry:
      healthCheck: true
      #serviceDiscovery:
      #  healthCheck: true # 同时支持单独开启服务发现能力时的客户端健康检查

Invoker

概述


框架提供Rest调用与RPC调用2种方式

API

Rest调用

使用NewRestInvoker创建一个invoker实例,可接受chain, filters等自定义选项

ContextDo可以接受一个http request作为参数,开发者可通过request的API对request进行操作,并作为参数传入该方法

func NewRestInvoker(opt ...Option) *RestInvoker
func (ri *RestInvoker) ContextDo(ctx context.Context, req *rest.Request, options ...InvocationOption) (*rest.Response, error)
RPC调用

使用NewRPCInvoker创建invoker实例,可接受chain, filters等自定义选项

指定远端的服务名,struct name,以及func name,以及请求参数和返回接口即可进行调用

最终结果会赋值到reply参数中

func NewRPCInvoker(opt ...Option) *RPCInvoker 
func (ri *RPCInvoker) Invoke(ctx context.Context, microServiceName, schemaID, operationID string, arg interface{}, reply interface{}, options ...InvocationOption) error

无论Rest还是RPC调用方法都能够接受多种选项对一次调用进行控制,参考options.go查看更多选项

示例

RPC

添加了2个具体调用选项,使用highway rpc,并使用roundrobin路由策略

invoker.Invoke(ctx, "Server", "HelloServer", "SayHello",
    &helloworld.HelloRequest{Name: "Peter"},
    reply,
    core.WithProtocol("highway"),
    core.WithStrategy(loadbalance.StrategyRoundRobin),
    core.WithVersion("0.0.1"),

)
Rest

与普通的http调用不同的是url参数不使用ip:port而是服务名并且http://变为cse://

在初始化invoker时还指定了这次请求要经过的处理链名称custom

req, _ := rest.NewRequest("GET", "cse://RESTServer/sayhello/world")
defer req.Close()
resp, err := core.NewRestInvoker(core.ChainName("custom")).ContextDo(context.TODO(), req)

负载均衡策略

概述

用户可以通过配置选择不同的负载均衡策略,当前支持轮询、随机、基于响应时间的权值、会话保持等多种负载均衡策略。

负载均衡功能作用于客户端,且依赖注册中心。

配置

负载均衡的配置项为cse.loadbalance.[MicroServiceName].[PropertyName],其中若省略MicroServiceName,则为全局配置;若指定MicroServiceName,则为针对特定微服务的配置。优先级:针对特定微服务的配置 > 全局配置。

为便于描述,以下配置项说明仅针对PropertyName字段

strategy.name

(optional, bool) RoundRobin | 策略,可选值:RoundRobin,Random,SessionStickiness,WeightedResponse。 SessionStickiness目前只支持Rest调用。

注意:

  1. 使用SessionStickiness策略,需要业务代码存储cookie,并在http请求中带入Cookie。使用go-chassis进行调用时,http头中将返回如下信息:Set-Cookie: SERVICECOMBLB=0406060d-0009-4e06-4803-080008060f0d,若用户使用SessionStickiness策略,需要将将该头部信息保存,并在发送后续请求时带上如下http头:Cookie: SERVICECOMBLB=0406060d-0009-4e06-4803-080008060f0d**
  2. 使用 WeightedResponse策略,启用后30s 策略会计算好数据并生效,80%左右的请求会被发送到延迟最低的实例里

API

除了通过配置文件传入负载均衡策略,还支持用户客户端调用传入WithStrategy的方式。

invoker.Invoke(ctx, "Server", "HelloServer", "SayHello",
    &helloworld.HelloRequest{Name: "Peter"},
    reply,
    core.WithContentType("application/json"),
    core.WithProtocol("highway"),
    core.WithStrategy(loadbalance.StrategyRoundRobin),
)

示例

配置chassis.yaml的负载均衡部分,以及添加处理链。

cse:
  loadbalance:                 # 全局负载均衡配置
    strategy:
      name: RoundRobin
    microserviceA:              # 微服务级别的负载均衡配置
      strategy:
        name: SessionStickiness

负载均衡过滤器

概述

负载均衡过滤器实现在负载均衡模块中,它允许开发者定制自己的过滤器,使得经过负载均衡策略选择实例前,预先对实例进行筛选。在一次请求调用过程中可以使用多个过滤策略,对从本地cache或服务中心获取的实例组进行过滤,将经过精简的实例组交给负载均衡策略做后续处理。

配置

目前可配的filter只有根据Available Zone Filter。可根据微服务实例的region以及AZ信息进行过滤,优先寻找同Region与AZ的实例。

cse:
  loadbalance:
    serverListFilters: zoneaware

需要配置实例的Datacenter信息

region:
  name: us-east
  availableZone: us-east-1

API

Go-chassis支持多种实现Filter接口的过滤器。FilterEndpoint支持通过实例访问地址过滤,FilterMD支持通过元数据过滤,FilterProtocol支持通过协议过滤,FilterAvailableZoneAffinity支持根据Zone过滤。

type Filter func([]*registry.MicroServiceInstance) []*registry.MicroServiceInstance

示例

客户端实例过滤器Filter的使用支持用户通过API调用传入,并且可以一次传入多个Filter,

invoker.Invoke(ctx, "Server", "HelloServer", "SayHello",
    &helloworld.HelloRequest{Name: "Peter"},
    reply,
    core.WithProtocol("highway"),
    core.WithStrategy(loadbalance.StrategyRoundRobin),
    core.WithFilters(
     "zoneaware"
    ),
)

动态配置

概述

go-chassis提供动态配置管理能力,支持CSE配置中心,本地文件,环境变量及命令行等多种配置管理,并由archaius包提供统一的接口获取配置。

API

archaius包提供获取全部配置和四种获取指定配置值的Get方法,同时提供默认值,即若指定配置值未配置则使用默认值。另外提供Exist方法判断指定配置值是否存在。用户可使用UnmarshalConfig方法提供反序列化配置到结构体的方法。go-archaius内置5个配置源,获取配置时优先从优先级高的配置源获取指定配置项,若无则按优先级高低依次从后续配置源获取,直到遍历所有配置源。内置配置源生效的优先级由高到底分别是配置中心,命令行,环境变量,文件,外部配置源。

获取指定配置值
GetBool(key string, defaultValue bool) bool
GetFloat64(key string, defaultValue float64) float64
GetInt(key string, defaultValue int) int
GetString(key string, defaultValue string) string
Get(key string) interface{}
Exist(key string) bool
获取全部配置
GetConfigs() map[string]interface{}
反序列化配置到结构体
UnmarshalConfig(obj interface{}) error

在go-archaius默认纳入动态管理的配置文件外,提供了AddFile方法允许用户添加其他文件到动态配置管理中。AddKeyValue可额外为外部配置源添加配置对。除默认加载的配置源外,允许用户实现自己的配置源,并通过RegisterListener注册到动态配置管理框架中。

添加文件源
AddFile(file string) error
外部配置源添加配置对
AddKeyValue(key string, value interface{}) error
注册/注销动态监听
RegisterListener(listenerObj core.EventListener, key ...string) error
UnRegisterListener(listenerObj core.EventListener, key ...string) error

在对接config center配置中心时请求中需指定demensionsInfo信息来确定获取配置的实例。该接口允许为配置项分区域DI配置和查询。

添加DI及获取指定DI的配置值
GetConfigsByDI(dimensionInfo string) map[string]interface{}
GetStringByDI(dimensionInfo, key string, defaultValue string) string
AddDI(dimensionInfo string) (map[string]string, error)

示例

示例中文件配置如下,可通过archaius包的Get方法读取指定文件配置项。

cse:
  fallback:
    Consumer:
      enabled: true
      maxConcurrentRequests: 20
archaius.GetInt("cse.fallback.Consumer.Consumer.maxConcurrentRequests", 10)
archaius.GetBool("cse.fallback.Consumer.Consumer.enabled", false)

Using Ctrip Apollo as a Configuration Center

Ctrip Apollo

Ctrip Apollo is a Configuration Server which can be used to store your configurations. Go-Chassis supports retrieving the configurations from Apollo and can be used for dynamic configurations of microservices. In this guide we will explain you how to configure Go-Chassis to use Apollo as a configuration server.

Configurations

Use can use this guide to start up the Ctrip Apollo and make the Project, NamesSpace and add Configurations to it. Once your Apollo Server is setup then you can do the following modification in Go-Chassis to make it work with Apollo.Update the chassis.yaml of your microservices with the following configuration.

cse:
  config:
    client:
      serverUri: http://127.0.0.1:8080          # This should be the address of your Apollo Server
      type: apollo                              # The type should be Apollo
      refreshMode: 1                            # Refresh Mode should be 1 so that Chassis-pulls the Configuration periodically
      refreshInterval: 10                       # Chassis retrives the configurations from Apollo at this interval
      serviceName: apollo-chassis-demo          # This the name of the project in Apollo Server
      env: DEV                                  # This is the name of environment to which configurations belong in Apollo
      cluster: demo                             # This is the name of cluster to which your Project belongs in Apollo
      namespace: application                    # This is the NameSpace to which your configurations belong in the project.

Once these configurations are set the Chassis can retrieve the configurations from Apollo Server.To see the detailed use case of how to use Ctrip Apollo with Chassis please refer to this example.

Router

概述

路由策略可应用于AB测试场景和新版本的灰度升级,主要通过路由规则来根据请求的来源、目标服务、Http Header及权重将服务访问请求分发到不同版本的微服务实例中。

比如通过路由管理你可以简单的实现新老版本服务切换

配置

路由规则当前只支持在配置文件配置,支持rest和highway协议。

Consumer配置

灰度发布的路由规则只在服务的消费端配置使用,用于将特定的请求,按一定权重,分发至同一服务名的不同分组。用户可在conf/router.yaml 文件中设置:

routeRule:  
  {targetServiceName}: # 服务名
    - precedence: {number} #优先级
      match:        #匹配策略
        source: {sourceServiceName} #匹配某个服务名
        headers:          #header匹配
          {key0}:            
            regex: {regex}
          {key1}         
            exact: {=?}   
      route: #路由规则
        - weight: {percent} #权重值
          tags:
            version: {version1}
            app: {appId}
    - precedence: {number1}
      match:        
        refer: {sourceTemplateName} #参考某个source模板ID
      route:
        - weight: {percent}
          tags:
            version: {version2}
            app: {appId}        
sourceTemplate:  #定义source模板
  {templateName}: # source 模板ID
    source: {sourceServiceName} 
    sourceTags:
      {tag}:{value}
    headers:
      {key0}:
        regex: {regex}
      {key1}
        exact: {=?}
      {key2}:
        noEqu: {!=?}
      {key3}
        greater: {>?}    
      {key4}:
        less: {<?}
      {key5}
        noLess: {>=?}      
      {key6}:
        noGreater: {<=?}

路由规则说明:

  • 匹配特定请求由match配置,匹配条件是:source(源服务名)、source tags 及headers,另外也可以使用refer字段来使用source模板进行匹配。
  • Match中的Source Tags用于和服务调用请求中的sourceInfo中的tags 进行逐一匹配。
  • Header中的字段的匹配支持正则, 等于, 小于, 大, 于不等于等匹配方式。
  • 如果未定义match,则可匹配任何请求。
  • 转发权重定义在routeRule.{targetServiceName}.route下,由weight配置。
  • 服务分组定义在routeRule.{targetServiceName}.route下,由tags配置,配置内容有version和app。

API

设置Router Rules

这个接口会彻底覆盖运行时的路由规则

router.SetRouteRule(rr map[string][]*config.RouteRule)
获取Router Rules
router.GetRouteRule() 返回值 map[string][]*config.RouteRule

例子

目标服务

每个路由规则的目标服务名称都由routeRule中的Key值指定。例如下表所示,所有以“Carts”服务为目标服务的路由规则均被包含在以“Carts”为Key值的列表中。

routeRule:
  Carts:
    - precedence: 1
      route:
        - weight: 100 #percent          
          tags:            
            version: 0.0.1

Key值(目标服务名称)应该满足是一个合法的域名称。例如,一个在服务中心中注册的服务名称。

规则优先级

针对某个特定的目标服务可以定义多条路由规则,在路由规则匹配过程中的匹配顺序按照各个规则的“precedence”字段的值来确定。“precedence”字段是可选配置,默认为0,值越大则优先级越高。如果两个规则的“precedence”配置值相同,则它们的实际匹配顺序是不确定的。

一种常用的模式是为指定的目标服务提供一条或多条具有高优先级的匹配请求源/Header的路由规则,并同时提供一条低优先级的只依照版本权重分发请求的路由规则,且这条规则不设置任何匹配条件以处理剩余的其他请求。

以下面的路由规则为例,对所有访问“Carts“服务的请求,如果满足header中包含”Foo:bar“,则将请求分发到服务的”2.0“版本的实例中,剩余的其他请求全部分发到”1.0“版本的实例中。

routeRule:
  Carts:
    - precedence: 2
      match:
        headers:
          Foo:
            exact: bar
      route:
        - weight: 100           
          tags:            
            version: 2.0
    - precedence: 1
      route:
        - weight: 100   
          tags:            
            version: 1.0
请求匹配规则
match:
  refer: {templateName}
  source: {sourceServiceName}
  headers:
    {key}:
      regex: {regex}
    {key1}:
      exact: {exact}

请求的匹配规则属性配置如下:

refer

(optional, string) 引用的匹配规则模板名称,用户可选择在sourceTemplate中定义匹配规则模板,并在此处引用。若引用了匹配规则模板,则其他配置项不用配置。

source

(optional, string) 表示发送请求的服务,和consumer是一个意义。

headers

(optional, string) 匹配headers。 如果配置了多条Header 字段校验规则,则需要同时满足所有规则才可完成路由规则匹配。 匹配方式有以下几种:精确匹配(exact):header必须等于配置; 正则(regex):按正则匹配header内容;不等于(noEqu):header不等于配置值;大于等于(noLess): header不小于配置值;小于等于(noGreater):header不大于配置值;大于(greater):header大于配置值;小于(less): header小于配置值

示例:

match:
  source: vmall
  headers:
    cookie:
      regex: "^(.*?;)?(user=jason)(;.*)?$"

仅适用于来自vmall,header中的“cookie”字段包含“user=jason”的服务访问请求。

分发规则

每个路由规则中都会定义一个或多个具有权重标识的后端服务,这些后端服务对应于用标签标识的不同版本的目标服务的实例。如果某个标签对应的注册服务实例有多个,则指向该标签版本的服务请求会按照用户配置的负责均衡策略进行分发,默认会采用round-robin策略。

分发规则的属性配置如下: weight

(optional, string) 本条规则的分发比重,配置为1-100的整数,表示百分比。

tags

(optional, string) 用于区分相同服务名的不同服务分组, 支持的key值为:版本(version),默认值为0.0.1。应用(app),默认值为default。

下面的例子表示75%的访问请求会被分流到具有“version:2.0”标签的服务实例中,其余25%的访问请求会被分发到1.0版本的实例中。

route:
  - weight: 75
    tags:
      version: 2.0
  - weight: 25
    tags:
      version: 1.0
定义匹配模板

我们可以通过预定义源模板(模板中的结构为一个Match结构),并在match部分引用该模板来进行路由规则的匹配。在下面的例子中,“vmall-with-special-header”是一个预定义的源模板的Key值,并在Carts的请求匹配规则中被引用。

routeRule:
  Carts:
    - precedence: 2
      match:
        refer: vmall-with-special-header
      route:
        - weight: 100           
          tags:            
            version: 2.0
sourceTemplate:
  vmall-with-special-header:
    source: vmall
    headers:
      cookie:
        regex: "^(.*?;)?(user=jason)(;.*)?$"

Use istio to manage route

Instead of using CSE and route config to manage route, go-chassis supports istio as a control plane to set route rule and follows the envoy API reference to manage route. This page gives the examples to show how requests are routed between micro services.

Go-chassis Configurations

In Consumer router.yaml, you can set router.infra to define which router plugin go-chassis fetches from. The default router.infra is cse, which means the routerule comes from route config in CSE config-center. If router.infra is set to be pilot, the router.address is necessary, such as the in-cluster istio-pilot grpc address.

router:
  infra: pilot # pilot or cse
  address: http://istio-pilot.istio-system:15010

In Both consumer and provider registry configurations, the recommended one shows below.

cse:
  service:
    registry:
      registrator:
        disabled: true
      serviceDiscovery:
        type: pilot
        address: http://istio-pilot.istio-system:8080

Kubernetes Configurations

The provider applications of v1, v2 and v3 version could be deployed in kubernetes cluster as Deployment with differenent labels. The labels of version is necessary now, and you need to set env to generate nodeID in istio system, such as POD_NAMESPACE, POD_NAME and INSTANCE_IP.

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    version: v1
    app: pilot
    name: istioserver
  name: istioserver-v1
  namespace: default
spec:
  progressDeadlineSeconds: 600
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: pilot
      version: v1
      name: istioserver
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: pilot
        version: v1
        name: istioserver
    spec:
      containers:
      - image: gosdk-istio-server:latest
        imagePullPolicy: Always
        name: istioserver-v1
        ports:
        - containerPort: 8084
          protocol: TCP
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        env:
        - name: CSE_SERVICE_CENTER
          value: http://istio-pilot.istio-system:8080
        - name: POD_NAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.namespace
        - name: INSTANCE_IP
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: status.podIP
        volumeMounts:
        - mountPath: /etc/certs/
          name: istio-certs
          readOnly: true
      dnsPolicy: ClusterFirst
      initContainers:
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30
      volumes:
      - name: istio-certs
        secret:
          defaultMode: 420
          optional: true
          secretName: istio.default

Istio v1alpha3 router configurations

Traffic-management gives references and examples of istio new router rule schema. First, subsets is defined according to labels. Then you can set route rule of differenent weight for virtual services.

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: istioserver
spec:
  host: istioserver
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
  - name: v3
    labels:
      version: v3
NOTICE: The subsets only support labels of version to distinguish differenent virtual services, this constrains will canceled later.
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: istioserver
spec:
  hosts:
    - istioserver
  http:
  - route:
    - destination:
        host: istioserver
        subset: v1
      weight: 25
    - destination:
        host: istioserver
        subset: v2
      weight: 25
    - destination:
        host: istioserver
        subset: v3
      weight: 50

Rate limiting

概述

用户可以通过配置限流策略限制provider端或consumer端的请求频率,使每秒请求数限制在最大请求量的大小。其中provider端的配置可限制接收处理请求的频率,consumer端的配置可限制发往指定微服务的请求的频率。

配置

限流配置在rate_limiting.yaml中,同时需要在chassis.yaml的handler chain中添加handler。其中qps.limit.[service] 是指限制从service 发来的请求的处理频率,若该项未配置则global.limit生效。Consumer端不支持global全局配置,其他配置项与Provider端一致。

flowcontrol.qps.enabled

(optional, bool) 是否开启限流,默认true

flowcontrol.qps.global.limit

(optional, int) 每秒允许的请求数,默认2147483647max int)

flowcontrol.qps.limit.{service}

(optional, string) 针对某微服务每秒允许的请求数 ,默认2147483647max int)
Provider示例

provider端需要在chassis.yaml添加ratelimiter-provider。同时在rate_limiting.yaml中配置具体的请求数。

cse:
  handler:
    chain:
      Provider:
        default: ratelimiter-provider
cse:
  flowcontrol
    Provider:
      qps:
        enabled: true  # enable rate limiting or not
        global:
          limit: 100   # default limit of provider
        limit:
          Server: 100  # rate limit for request from a provider
Consumer示例

在consumer端需要添加ratelimiter-consumer这个handler。同时在rate_limiting.yaml中配置具体的请求数。

cse:
  handler:
    chain:
      Consumer:
        default: ratelimiter-consumer
cse:
  flowcontrol:
    Consumer:
      qps:
        enabled: true  # enable rate limiting or not
        limit:
          Server: 100  # rate limit for request to a provider

API

qpslimiter提供获取流控实例的接口GetQpsTrafficLimiter和相关的处理接口。其中ProcessQpsTokenReq根据目标qpsRate在handler chain当中sleep相应时间实现限流,UpdateRateLimit提供更新qpsRate限制的接口,DeleteRateLimiter提供了删除流控实例的接口。

对请求流控
qpslimiter.GetQpsTrafficLimiter().ProcessQpsTokenReq(key string, qpsRate int)
更新流控限制
qpslimiter.GetQpsTrafficLimiter().UpdateRateLimit(key string, value interface{})
删除流控实例
qpslimiter.GetQpsTrafficLimiter().DeleteRateLimiter(key string)

Fault Tolerance

概述

go-chassis提供自动重试的容错能力,用户可配置retry及backOff策略自动启用重试功能。

配置

重试功能的相关配置与客户端负载均衡策略都在chassis.yaml的cse.loadbalance.配置下。当retryEnabled配置为true时,可通过配置retryOnSame和retryOnNext定制重试次数。另外可通过backOff定制重试策略,默认支持三种backOff策略。

  • zero: 固定重试时间为0的重试策略,即失败后立即重试不等待。
  • constant: 固定时间为backoff.minMs的重试策略,即失败后等待backoff.minMs再重试。
  • jittered: 按指数增加重试时间的重试策略,初始重试时间为backoff.minMs,最大重试时间为backoff.MaxMs。

retryEnabled

(optional, bool) 是否开启重试功能, 默认值为false

retryOnSame

(optional, int) 请求失败后向同一个实例重试的次数,默认为0

retryOnNext

(optional, int) 请求失败后向其他实例重试的次数,默认为0

backoff.kind

(optional, string) 重试策略: [jittered或constant或zero] 默认为zero

backoff.MinMs

(optional, bool) 重试最小时间间隔 单位ms , 默认值为false

backoff.MaxMs

(optional, int) 重试最大时间间隔 单位ms, 默认值为0

示例

配置chassis.yaml负载均衡部分中的重试参数。

cse:
  loadbalance:
    retryEnabled: true
    retryOnNext: 2
    retryOnSame: 3
    backoff:
      kind: jittered
      MinMs: 200
      MaxMs: 400

熔断与降级

概述

降级策略是当服务请求异常时,微服务所采用的异常处理策略。

降级策略有三个相关的技术概念:“隔离”、“熔断”、“容错”:

  • “隔离”是一种异常检测机制,常用的检测方法是请求超时、流量过大等。一般的设置参数包括超时时间、同时并发请求个数等。
  • “熔断”是一种异常反应机制,“熔断”依赖于“隔离”。熔断通常基于错误率来实现。一般的设置参数包括统计请求的个数、错误率等。
  • “容错”是一种异常处理机制,“容错”依赖于“熔断”。熔断以后,会调用“容错”的方法。一般的设置参数包括调用容错方法的次数等。

把这些概念联系起来:当”隔离”措施检测到N次请求中共有M次错误的时候,”熔断”不再发送后续请求,调用”容错”处理函数。这个技术上的定义,是和Netflix Hystrix一致的,通过这个定义,非常容易理解它提供的配置项,参考:https://github.com/Netflix/Hystrix/wiki/Configuration。当前ServiceComb提供两种容错方式,分别为返回null值和抛出异常。

配置

配置格式为:

cse.{namespace}.Consumer.{serviceName}.{property}: {configuration}

字段意义:

{namespace}取值为:isolation|circuitBreaker|fallback|fallbackpolicy,分别表示隔离、熔断、降级、降级策略。

{serviceName}表示服务名,即某个服务提供者。

{property}表示具体配置项。

{configuration}表示具体配置内容。

为了方便描述,下表中的配置项均省略了Consumer和{serviceName}。

cse.isolation.timeout.enabled

(optional, bool) 是否启用超时检测,默认false

cse.isolation.timeoutInMilliseconds

(optional, int) 超时阈值,默认30000

cse.isolation.maxConcurrentRequests

(optional, int)最大并发数阈值 默认1000

cse.circuitBreaker.enabled

(optional, bool) 是否启用熔断措施,默认true

cse.circuitBreaker.forceOpen

(optional, bool) 不管失败次数,都进行熔断 默认false

cse.circuitBreaker.forceClosed

(optional, bool)任何时候都不熔断,当与forceOpen同时配置时,forceOpen优先。默认false

cse.circuitBreaker.sleepWindowInMilliseconds

(optional, int) 熔断后,多长时间恢复。恢复后,会重新计算失败情况。注意:如果恢复后的调用立即失败,那么会立即重新进入熔断。 默认15000

cse.circuitBreaker.requestVolumeThreshold

(optional, int) 10s内统计错误发生次数阈值,超过阈值则触发熔断 | 由于10秒还会被划分为10个1秒的统计周期,经过1s中后才会开始计算错误率,因此从调用开始至少经过1s,才会发生熔断 默认20

cse.circuitBreaker.errorThresholdPercentage

(optional, int) 错误率阈值,达到阈值则触发熔断 默认50

cse.fallback.enabled

(optional, bool) 是否启用出错后的故障处理措施 默认为true

cse.fallbackpolicy.policy

(optional, string) 出错后的处理策略 可选 returnnull throwexception,默认returnnull

示例

---
cse:
  isolation:
    Consumer:
      timeout:
        enabled: false
      timeoutInMilliseconds: 1
      maxConcurrentRequests: 100
      Server:
        timeout:
          enabled: true
        timeoutInMilliseconds: 1000
        maxConcurrentRequests: 1000
  circuitBreaker:
    Consumer:
      enabled: false
      forceOpen: false
      forceClosed: true
      sleepWindowInMilliseconds: 10000
      requestVolumeThreshold: 20
      errorThresholdPercentage: 10
      Server:
        enabled: true
        forceOpen: false
        forceClosed: false
        sleepWindowInMilliseconds: 10000
        requestVolumeThreshold: 20
        errorThresholdPercentage: 5
  fallback:
    Consumer:
      enabled: true
      maxConcurrentRequests: 20
  fallbackpolicy:
    Consumer:
      policy: throwexception

Tracing

概述

调用跟踪模块主要实现与“服务监控”的对接,按照服务监控的要求,产生span和SLA数据,按照配置文件的定义将产生的调用链数据输出到zipkin或文件中,用于分析调用链的调用过程和状态。

配置

使用调用链追踪功能,必须先在handler chain中添加对应handler:tracing-provider或tracing-consumer,默认存在。

tracing.collectorType

(requied, string) 定义调用数据向什么服务发送,支持 zipkinnamedPipe

tracing.collectorTarget

(requied, string) 服务的URI,比如文件路径,http地址。 collectorType为http时,collectorTarget为zipkin地址否则,namedPipe为文件路径

示例

追踪数据发送至zipkin:

cse:
  handler:
    chain:
      Provider:
        default: tracing-provider,bizkeeper-provider
tracing:
  collectorType: zipkin
  collectorTarget: http://localhost:9411/api/v1/spans

追踪数据写入linux named pipe:

tracing:
  collectorType: namedPipe
  collectorTarget: /home/chassis.trace

Metrics

概述

Metrics用于度量服务性能指标。开发者可通过配置文件来将框架自动手机的metrics导出并让prometheus收集。

如果有业务代码定制的metrics,也可以通过API来调用,来定制自己的的metrics

配置

cse.metrics.enable

(optional, bool) 是否开启metrics功能,默认为false

cse.metrics.apipath

(optional, string) metrics接口,默认为/metrics

cse.metrics.enableGoRuntimeMetrics

(optional, bool) 是否开启go runtime监测,默认为false

API

包路径

import "github.com/ServiceComb/go-chassis/metrics"

获取go-chassis的metrics registry,用户定制的metrics,可以通过这个registry来添加,最终也会自动导出到API的返回中

func GetSystemRegistry() metrics.Registry

获取go-chassis使用的prometheus registry,允许用户直接对Prometheus registry进行操作

func GetSystemPrometheusRegistry() *prometheus.Registry

创建一个特定名称的metrics registry

func GetOrCreateRegistry(name string) metrics.Registry

使用特定metrics registry向prometheus汇报metrics数据

func ReportMetricsToPrometheus(r metrics.Registry)

汇报metrics数据的http handler

func MetricsHandleFunc(req *restful.Request, rep *restful.Response)

示例

cse:
  metrics:
    apiPath: /metrics      # we can also give api path having prefix "/" ,like /adas/metrics
    enable: true
    enableGoRuntimeMetrics: true

若rest监听在127.0.0.1:8080,则作上述配置后,可通过 http://127.0.0.1:8080/metrics 获取metrics数据。

Log

概述

用户可配置微服务的运行日志的相关属性,比如输出方式,日志级别,文件路径以及日志转储相关属性。

配置

日志配置文件为lager.yaml,配置模板如下:

  • logger_level表示日志级别,由低到高分别为 DEBUG, INFO, WARN, ERROR, FATAL 共5个级别,这里设置的级别是日志输出的最低级别,只有不低于该级别的日志才会输出。
  • writers表示日志的输出方式,默认为文件和标准输出。
  • logger_file表示日志输出文件。
  • log_format_text: 默认为false,即设定日志的输出格式为 json。若为true则输出格式为plaintext,类似log4j。建议使用json格式输出的日志。
  • rollingPolicy: 默认为size,即根据大小进行日志rotate操作;若配置为daily则基于事件做日志rotate。
  • log_rotate_date: 日志rotate时间配置,单位”day”,范围为(0, 10)。
  • log_rotate_size: 日志rotate文件大小配置,单位”MB”,范围为(0,50)。
  • log_backup_count: 日志最大存储数量,单位“个”,范围为[0,100)。
---
writers: file,stdout
# LoggerLevel: |DEBUG|INFO|WARN|ERROR|FATAL
logger_level: DEBUG
logger_file: log/chassis.log
log_format_text: false

#rollingPolicy daily/size
rollingPolicy: size
#log rotate and backup settings
log_rotate_date: 1
log_rotate_size: 10
log_backup_count: 7

API

通过配置lager.yaml,go-chassis会自动为服务加载日志模块,并默认输出日志到文件或标准输出。

Debug和Info级别的日志
lager.Logger.Info(action string, data ...Data)
lager.Logger.Debug(action string, data ...Data)

lager.Logger.Debugf(format string, args ...interface{})
lager.Logger.Infof(format string, args ...interface{})
Warn Error Fatal级别的日志
lager.Logger.Warn(action string, err error, data ...Data)
lager.Logger.Error(action string, err error, data ...Data)
lager.Logger.Fatal(action string, err error, data ...Data)

lager.Logger.Warnf(err error, format string, args ...interface{})
lager.Logger.Errorf(err error, format string, args ...interface{})
lager.Logger.Fatalf(err error, format string, args ...interface{})

TLS

概述

用户可以通过配置SSL/TLS启动HTTPS通信,保障数据的安全传输。包括客户端与服务端TLS,通过配置来自动启用Consumer与Provider的TLS配置。

配置

tls可配置于chassis.yaml文件或单独的tls.yaml文件。在如下格式中,tag指明服务名以及服务类型,key指定对应configuration的配置项。

ssl:
  [tag].[key]: [configuration]
TAG

tag为空时ssl配置为公共配置。registry.consumer及configcenter.consumer是作为消费者访问服务中心和配置中心时的ssl配置。protocol.serviceType允许协议和类型的任意组合。name.protocol.serviceType在协议和类型的基础上可定制服务名。

registry.Consumer

服务注册中心TLS配置

serviceDiscovery.Consumer

服务发现TLS配置

contractDiscovery.Consumer

契约发现TLS配置

registrator.Consumer

服务注册中心TLS配置

configcenter.Consumer

配置中心TLS配置 |

{protocol}.{serviceType}

协议为任意协议目前包括 highwayrest,用户扩展协议后,即可使用新的协议配置。 类型为Consumer,Provider |

{name}.{protocol}.{serviceType}

定制某微服务的独有的TLS配置 name为微服务名
KEY

ssl支持以下配置项,其中若私钥KEY文件加密,则需要指定加解密插件及密码套件等信息进行解密。

cipherPlugin

(optional, string) 指定加解密插件 内部插件支持 default aes, 默认default |

verifyPeer

(optional, bool) | 是否验证对端,默认false

cipherSuits

(optional, string) TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 密码套件 |

protocol

(optional, string) TLS协议的最小版本,默认为TLSv1.2

caFile

(optional, string) ca文件路径

certFile

(optional, string) 私钥cert文件路径

keyFile

(optional, string) 私钥key文件路径

certPwdFile

(optional, string) 私钥key加密的密码文件

API

通过为Provider和Consumer配置ssl,go-chassis会自动为其加载相关配置。用户也可以通过chassis暴露的接口直接使用相关API。以下API主要用于获取ssl配置以及tls.Config。

获取默认SSL配置
GetDefaultSSLConfig() *common.SSLConfig
获取指定SSL配置
GetSSLConfigByService(svcName, protocol, svcType string) (*common.SSLConfig, error)
获取指定TLSConfig
GetTLSConfigByService(svcName, protocol, svcType string) (*tls.Config, *common.SSLConfig, error)

示例

Provider配置

以下为rest类型provider提供HTTPS访问的ssl配置,其中tag为protocol.serviceType的形式。

ssl:
  rest.Provider.cipherPlugin: default
  rest.Provider.verifyPeer: true
  rest.Provider.cipherSuits: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
  rest.Provider.protocol: TLSv1.2
  rest.Provider.keyFile: /etc/ssl/server_key.pem
  rest.Provider.certFile: /etc/ssl/server.cer
  rest.Provider.certPwdFile: /etc/ssl/cert_pwd_plain
  rest.Provider.caFile: /etc/ssl/trust.cer
Consumer配置

以下为访问rest类型服务的消费者的ssl配置。tag为name.protocol.serviceType的形式,其中Server为要访问的服务名,rest为协议。verifyPeer若配置为true将启动双向认证,否则客户端将忽略对服务端的校验。

ssl:
  Server.rest.Consumer.cipherPlugin: default
  Server.rest.Consumer.verifyPeer: true
  Server.rest.Consumer.cipherSuits: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
  Server.rest.Consumer.protocol: TLSv1.2
  Server.rest.Consumer.keyFile: /etc/ssl/server_key.pem
  Server.rest.Consumer.certFile: /etc/ssl/server.cer
  Server.rest.Consumer.certPwdFile: /etc/ssl/cert_pwd_plain
  Server.rest.Consumer.caFile: /etc/ssl/trust.cer

契约管理

概述

go-chassis读取服务契约并将其内容上传至注册中心。

配置

契约文件必须为yaml格式文件,契约文件应放置于go-chassis的schema目录。

schema目录位于:

1,conf/{serviceName}/schema,其中conf表示go-chassis的conf文件夹

2,${SCHEMA_ROOT}

2的优先级高于1。

API

包路径

import "github.com/ServiceComb/go-chassis/core/config/schema"

契约字典,key值为契约文件名,value为契约文件内容

var DefaultSchemaIDsMap map[string]string

示例

conf
`-- myservice
    `-- schema
        |-- myschema1.yaml
        `-- myschema2.yaml

与Istio集成

服务发现

概述

go-chassis对接Istio的pilot组件,实现服务发现能力。

配置

接入pilot进行服务发现的配置在chassis.yaml。

registrator.disabled

设置禁用服务主动注册能力,与Istio集成后,由其对接的平台进行服务注册

serviceDiscovery.type

设置启用接入pilot插件

serviceDiscovery.address

pilot服务地址 允许配置多个以逗号隔开
示例
cse:
  service:
    Registry:
      registrator:
        disabled: true
      serviceDiscovery:
        type: pilot
        address: http://istio-pilot.istio-system:8080

Communication between GO consumer and JAVA provider using highway protocol:

GO consumer

Go consumer uses invoker.Invoker() call to make highway communication

Go Consumer
`
Parameters of Invoke:
  1. Context
  2. MicroserviceName
  3. SchemaID
  4. operationID
  5. Input argument
  6. Response argument

`

In the employ.bp.go file the structure EmployeeStruct is been used as the input argument and Response argument

EmployeeStruct

Java provider:

Microservicename is the name provider in the microservice.yaml file . In this example it is “springboot”.

microservice.yaml

SchemaId is the schemaID defined in the java provider. In this example it is “hello”.

OperationId is the OperationName in the java provider. In this example it is “addAndShowEmploy”.

helloservice.png

Employ class which has the member variables “Name” and “Phone” is used as input parameter for the operation and also response for this api.

Employ

Development guides

Handler

概述

Go chassis以插件的形式支持在一次请求调用中,插入自己的处理逻辑。

实现

实现handler需要以下三个步骤:实现Handler接口,并根据其名称注册,最后在chassis.yaml中添加handler chain相关配置。其中[service_type] 可配置为Provider或Consumer,[chain_name]默认为default。

注册处理逻辑
RegisterHandler(name string, f func() Handler) error
实现处理接口
type Handler interface {
    Handle(*Chain, *invocation.Invocation, invocation.ResponseCallBack)
    Name() string
}
添加配置
cse:
  handler:
    chain:
      [service_type]:
        [chain_name]: [your_handler_name]

示例

示例中注册的是名为fake-handler的处理链,其实现的Handle方法仅记录inv的endpoint信息。

package handler
import (
    "github.com/ServiceComb/go-chassis/core/handler"
    "github.com/ServiceComb/go-chassis/core/invocation"
    "log"
)
const Name = "fake-handler"
type FakeHandler struct{}

func init()                         { handler.RegisterHandler(Name, New) }
func New() handler.Handler          { return &FakeHandler{} }
func (h *FakeHandler) Name() string { return Name }

func (h *FakeHandler) Handle(chain *handler.Chain, inv *invocation.Invocation,
    cb invocation.ResponseCallBack) {
    log.Printf("fake handler running for %v", inv.Endpoint)
    chain.Next(inv, func(r *invocation.InvocationResponse) error {
        return cb(r)
    })
}

chassis.yaml配置示例如下

cse:
  handler:
    chain:
      Provider:
        default: fake-handler

Archaius

概述

go-archaius是go-chassis的动态配置框架,目前支持CSE 配置中心,本地文件,ENV,CMD等配置管理。如果用户希望接入自己的配置服务中,可以参考本章节实现。

使用说明

go-archaius支持同时配置多种源,包括命令行,环境变量,外部源及文件等。用户可通过ConfigurationFactory接口的AddSource方法添加自己的配置源,并通过RegisterListener方法注册EventListener。

AddSource(core.ConfigSource) error
RegisterListener(listenerObj core.EventListener, key ...string) error

其中配置源需要实现ConfigSource接口。其中GetPriority和GetSourceName必须实现且有有效返回,分别用于获取配置源优先级和配置源名称。GetConfigurations和GetConfigurationByKey方法用于获取全部配置和指定配置项,需要用户实现。其他方法可以返回空。

  • GetPriority方法用于确定配置源的优先级,go-archaius内置的5个配置源优先级由高到底分别是配置中心,命令行,环境变量,文件,外部配置源,对应着0到4五个整数值。用户自己接入的配置源可自行配置优先级级别,数值越小则优先级越高。GetSourceName方法用于返回配置源名称。
  • 若没有区域区分的集中式配置中心,DemensionInfo相关接口可返回nil不实现。
  • Cleanup用于清空本地缓存的配置。
  • DynamicConfigHandler接口可根据需要实现,用于实现动态配置动态更新的回调方法。
type ConfigSource interface {
    GetSourceName() string
    GetConfigurations() (map[string]interface{}, error)
    GetConfigurationsByDI(dimensionInfo string) (map[string]interface{}, error)
    GetConfigurationByKey(string) (interface{}, error)
    GetConfigurationByKeyAndDimensionInfo(key, dimensionInfo string) (interface{}, error)
    AddDimensionInfo(dimensionInfo string) (map[string]string, error)
    DynamicConfigHandler(DynamicConfigCallback) error
    GetPriority() int
    Cleanup() error
}

注册EventListener用于在配置源更新时由Dispatcher分发事件,由注册的listener处理。

type EventListener interface {
    Event(event *Event)
}

示例

实现configSource
type fakeSource struct {
    Configuration  map[string]interface{}
    changeCallback core.DynamicConfigCallback
    sync.Mutex
}

func (*fakeSource) GetSourceName() string { return "TestingSource" }
func (*fakeSource) GetPriority() int      { return 0 }

func (f *fakeSource) GetConfigurations() (map[string]interface{}, error) {
    config := make(map[string]interface{})
    f.Lock()
    defer f.Unlock()
    for key, value := range f.Configuration {
        config[key] = value
    }
    return config, nil
}

func (f *fakeSource) GetConfigurationByKey(key string) (interface{}, error) {
    f.Lock()
    defer f.Unlock()
    configValue, ok := f.Configuration[key]
    if !ok {
        return nil, errors.New("invalid key")
    }
    return configValue, nil
}

func (f *fakeSource) DynamicConfigHandler(callback core.DynamicConfigCallback) error {
    f.Lock()
    defer f.Unlock()
    f.changeCallback = callback
    return nil
}

func (f *fakeSource) Cleanup() error {
    f.Lock()
    defer f.Unlock()
    f.Configuration = make(map[string]interface{})
    f.changeCallback = nil
    return nil
}

func (*fakeSource) AddDimensionInfo(d string) (map[string]string, error) { return nil, nil }
func (*fakeSource) GetConfigurationByKeyAndDimensionInfo(k, d string) (interface{}, error) { return nil, nil }
func (*fakeSource) GetConfigurationsByDI(d string) (map[string]interface{}, error) { return nil, nil }
添加configSource
func NewConfigSource() core.ConfigSource {
    return &fakeSource{
      Configuration: make(map[string]interface{}),
    }
}
factory, _ := goarchaius.NewConfigFactory(lager.Logger)
err := factory.AddSource(fakeSource.NewConfigSource())
注册evnetListener
type EventHandler struct{
    Factory goarchaius.ConfigurationFactory
}
func (h EventHandler) Event(e *core.Event) { 
  value := h.Factory.GetConfigurationByKey(e.Key)
  log.Printf("config value after change %s | %s", e.Key, value)
}
factory.RegisterListener(&EventHandler{Factory: factory}, "a*")

Archaius Config Source Plugin

Config Source Plugin

Config Source Plugin let’s you write your own the Config-Center client implementation for the different types of Config Source.

Instructions

Go-Chassis can support pulling the configuration from different types of config centers, currently there are 2 implementation available for Config-Client Plugin (Go-Archaius Config-center, Ctrip Apollo Config-center). If you want to implement any new client for another config-center then you have to implement the following ConfigClient Interface.

//ConfigClient is the interface of config server client, it has basic func to interact with config server
type ConfigClient interface {
    //Init the Configuration for the Server
    Init()
    //PullConfigs pull all configs from remote
    PullConfigs(serviceName, version, app, env string) (map[string]interface{}, error)
    //PullConfig pull one config from remote
    PullConfig(serviceName, version, app, env, key, contentType string) (interface{}, error)
    //PullConfigsByDI pulls the configurations with customized DimensionInfo/Project
    PullConfigsByDI(dimensionInfo , diInfo string)(map[string]map[string]interface{}, error)
}

Once you implement the above interface then you need to define the type of your configClient

config.GlobalDefinition.Cse.Config.Client.Type
cse:
  config:
    client:
      type: your_client_name   #config_center/apollo/your_client_name

Based on this type you need to load the plugin in your init()

 func init(){
    client.InstallConfigClientPlugin("NameOfYourPLugin", InitConfigYourPlugin)
 }

You need to import the package path in your application to Enable your plugin. Once the plugin is enabled then you can pull configuration using the ConfigClient

client.DefaultClient.PullConfigs(serviceName, versionName, appName, env)

Cipher

概述


Go chassis以插件的形式提供加解密组件功能,用户可以自己定制,Chassis默认提供一种华为PAAS成熟的加解密组件,该功能目前以二进制的so文件形式提供。

配置


AES Cipher 配置

1、把so文件拷贝到项目${CHASSIS_HOME}/lib目录下,或者直接放到系统/usr/lib目录下面。优先读取${CHASSIS_HOME}/lib,然后再在/usr/lib下面查找。

2、通过环境变量PAAS_CRYPTO_PATH指定物料路径(root.key, common_shared.key)

3、引入aes包,使用加解密方法

自定义Cipher

可通过实现Cipher接口,自定义Cipher

type Cipher interface {
    Encrypt(src string) (string, error)
    Decrypt(src string) (string, error)
}

API


加密
Encrypt(src string) (string, error)
解密
Decrypt(src string) (string, error)

例子


使用AES Cipher示例
import (
    _ "github.com/servicecomb/security/plugins/aes"
    "testing"
    "github.com/servicecomb/security"
    "github.com/stretchr/testify/assert"
    "log"
)

func TestAESCipher_Decrypt(t *testing.T) {
    aesFunc := security.CipherPlugins["aes"]
    cipher := aesFunc()
    s, err := cipher.Encrypt("tian")
    assert.NoError(t, err)
    log.Println(s)
    a, _ := cipher.Decrypt(s)
    assert.Equal(t, "tian", a)
}
自定义Cipher 示例
package plain

import "github.com/servicecomb/security"

type DefaultCipher struct {
}

// register self-defined plugin in the cipherPlugin map
func init() {
    security.CipherPlugins["default"] = new
}

// define a method of newing a plugin object, and register this method
func new() security.Cipher {
    return &DefaultCipher{
    }
}

// implement the Encrypt(string) method, to encrypt the clear text
func (c *DefaultCipher)Encrypt(src string) (string, error) {
    return src, nil
}

// implement the Decrypt(string) method, to decrypt the cipher text
func (c *DefaultCipher)Decrypt(src string) (string, error) {
    return src, nil
}

Protocol

概述


框架默认支持http协议以及highway RPC 协议,用户可扩展自己的RPC协议,并使用RPCInvoker调用

如何实现


客户端
  • 实现协议的客户端接口
type Client interface
  • 实现以下接口来返回客户端插件
func(...clientOption.Option) Client
  • 安装客户端插件
func InstallPlugin(protocol string, f ClientNewFunc)
  • 处理链默认自带名为transport的handler,他将根据协议名加载对应的协议客户端,指定协议的方式如下
invoker.Invoke(ctx, "Server", "HelloServer", "SayHello",
    &helloworld.HelloRequest{Name: "Peter"},
    reply,
    core.WithProtocol("grpc"),
)
服务端
  • 实现协议的Server端
type Server interface
  • 修改配置文件以启动协议监听
cse:
  protocols:
    grpc:
      listenAddress: 127.0.0.1:5000
      advertiseAddress: 127.0.0.1:5000