Skip to content

zh_userguide

Ray edited this page Mar 23, 2021 · 16 revisions

基本介绍

Motan是高性能的RPC服务化框架,Motan提供了强大服务治理功能以及优秀的扩展能力。Motan-go是Motan的golang版本实现,除了提供了client与server与其他语言版本进行交互外,Motan-go还提供了agent为不同语言使用Motan框架进行跨语言服务治理提供了支持。由agent的正、反向代理能够提供Service Mesh方案。

架构概述

Motan-go的基本交互方式请参考Motan架构概述

模块概述

Motan-go的各模块的作用与Motan java版本的基本一致, 但是实现方式与java版本略有不同,其中transport部分在client端与server端分别由endpointserver两个模块实现。 Motan-go的代码结构按package进行主要功能模块进行划分,各package功能可以参考GoDoc文档

配置管理

Motan服务通过配置来进行服务的订阅与发现,为了提高服务交互性能一般通过group来区分机房,达到同机房就近访问的目的。一般服务会按服务池进行服务部署,同一服务池下的服务代码、配置完全相同,不同服务池可能代码一致,但是配置文件不同。 Mesh模式下,Agent的配置支持单配置文件、服务池配置两种方式。

单配置文件方式

使用单配置文件进行配置时,可以在启动Agent时通过-c参数指定使用的配置文件。例如使用myConfig.yaml作为配置文件启动:

./motan-agent -c ./myConfig.yaml

微服务数量不大时建议使用单文件配置,使用比较简单。

服务池配置方式

在微服务数据比较庞大时,服务之间的调用关系非常复杂,在不同机房可能会需要不同的配置文件。这些配置文件中的大部分内容可能是相同的,只有部分参数可能不同机房有不同的值,例如 group参数在A机房的值为groupA,在B机房配置的值为groupB。

为了便于服务配置统一管理,让服务提供方和服务使用方可以更方便的配置服务,agent支持按服务池加载配置的方式启动。

服务池配置以约定的目录管理配置,每个目录下可以存放多个配置文件,agent启动时按固定的顺序解析配置文件并自动合成最终配置。配置目录必须满足如下格式:

  • applications/ 目录下保存所有业务方配置,配置名使用 ${application}.yaml形式,例如:openapi业务的配置文件为openapi.yaml。
  • pools/ 目录下保存业务的分池子配置。分池配置以applications目录下的对应业务配置为base,在不同服务池中只需配置各个服务池的差异化配置。
  • services/ 目录下保存所有可以使用的服务的默认配置。 一个服务提供方提供服务时,需要在这个目录下给出自己服务的基础配置,这样不同的使用方就可以通过引用的方式使用该服务。
  • basic.yaml 公共基础配置文件。文件中的配置项会在所有Mesh启动时加载,basic中声明了的配置,在其他配置中可以直接使用。例如注册中心的配置,可能不同服务都是相同的,那么就不需要在每个服务中都声明一次。

使用服务池配置

1、使用服务池配置时,配置项必须遵循服务池配置的约定目录结构。

2、使用服务池配置时,需要在Mesh(agent)启动时增加-c和-pool两个参数。其中-c参数用来指定服务池配置的目录;-pool用来指定启动时的服务池名称。

例如,服务池配置的目录位置在/data1/mesh-confs,启动服务的业务方是openapi,启动的服务器属于服务池openapi-dc1-core,那么启动agent的命令为:

./motan-agent -c /data1/mesh-confs -pool openapi-dc1-core

3、服务池名称可以分为多个部分,各部分之间使用-进行分隔。其中第一部分必须业务名,Mesh解析配置时会按此业务名在applications目录下加载业务的基础配置。其他部分可以按特定的含义进行划分,Mesh会分级加载配置。

例如:池子名为openapi-dc1-core时,由分隔符分开的第一部分openapi为业务名,Mesh解析时会先加载basic.yaml配置,然后加载applications/openapi.yaml的业务基础配置。 之后会按各部分逐级加载,先寻找pools/openapi-dc1.yaml,如果存在则加载,不存在则继续寻找pools/openapi-dc1-core.yaml进行加载。

同名配置项的优先级为 pool > appliction > service > basic,可以根据优先级关系进行配置项复写

配置项复写

当服务提供的基础配置参数不符合业务使用要求时,可以通过在应用配置中复写进行覆盖。例如abtestService默认的超时时间是1000ms,如果我的业务中想使用超时时间为500ms,则可以在我的业务配置中追加对应配置。其中配置项的层级关系需要与abtestService.yaml中的保持一致(motan-refer:abtest-rpc:requestTimeout)。

另一种定制化配置的方式是使用动态配置。 动态配置使用${动态配置名}表示,然后在dynamic-param:配置项下给出实际配置值。假设abtest服务中通过requestTimeout:${my_dynamic_request_timeout}的动态配置方式指定了请求超时,修改此配置的两种方式样例如下:

# 配置重写方式修改配置
motan-refer:
  abtest-rpc:
    requestTimeout: 500

# 动态配置方式指定配置
dynamic-param:
  my_dynamic_request_timeout: 500

使用配置池提供服务

1、提供调用方默认配置。只需要把这个服务的通用配置以服务名.yaml命名,放在services/目录下即可。例如abtestService.yaml

# 一个服务配置文件中可以提供多个service,多个service共同的参数可以提取出来放在motan-basicRefer中。
# 注意basic refer的id需要保证唯一,否则可能造成加载的参数错误。
motan-basicRefer:
  card-abtest-basicRefer: # 此basic refer id必须全局唯一!!!
    group: "${refer.abtest.group}"
    registry: vintage
    serialization: simple
    protocol: motan2
    version: 0.1
    requestTimeout: 1000
    haStrategy: failover
    loadbalance: random
    filter: "metrics"
    maxClientConnection: 10
    minClientConnection: 1
    retries: 0

#conf of refers
motan-refer:
  abtest-rpc:
    group: "${refer.abtest.group}" # 可以使用类似java 的placeholder的动态配置方式,方便业务使用方动态指定配置内容。
    path: com.weibo.api.abtest.rpc.IABTestRPCService
    basicRefer: card-abtest-basicRefer
    requestTimeout: ${my_dynamic_request_timeout}

2、设置motan-service相关配置,此配置一般直接在applicaion/目录下服务提供方业务配置文件中定义。motan-service相关参数也支持在pool中进行配置复写。

使用配置池引用服务

1、在applications/目录下,以自己的业务名称配置业务基础配置。必须配置application: "${your app}"。

2、 配置需要使用的服务。

需要使用的服务都在services/目录下,需要使用哪一个,就直接把文件名(不包括.yaml后缀)添加到import-refer下即可。样例如下:

# agent其他属性已经在basic.yaml中指定了,因此业务配置中,只需要配置application一个属性就可以。
motan-agent:
  application: "v4"

# 业务中需要使用的服务,例如配置了abtestService,就会加载services/abtestService.yaml文件中的全部配置。
import-refer:
  - abtestService
  - cardService 

自定义扩展实现

强大的扩展能力是Motan框架的特点之一,Motan-go通过扩展注册机制来支持自定义扩展,通过自定义扩展可以方便适配不同的系统或增加定制化功能。 Motan-go的各类扩展实现是通过ExtentionFactory来管理,Motan-go给出了默认实现类DefaultExtentionFactory,DefaultExtentionFactory提供了各类扩展实现类的注册与获取机制,并会提前注册所有的扩展实现。在实际使用client、server、agent时会默认使用DefaultExtentionFactory,用户只需要注册自定义实现即可,这是推荐的扩展方式。

也可以使用自定义的ExtentionFactory实现来替换默认的DefaultExtentionFactory,但需要对所有扩展的使用方式比较了解,不建议一般用户通过此种方式进行扩展。

主要扩展点

Motan-go对各主要功能都提供了可扩展能力,并提供了多种默认实现可供选择。主要扩展点如下:

  • Registry: 不同注册中心扩展。目前支持zookeeper和direct,后续会支持其他注册中心,例如consul等
  • Filter: filter是在请求处理中对reques和response进行处理的机制,提供endpointFilter和clusterFilter两个插入点。使用filter进行功能扩展是推荐的扩展方式。默认提供accesslog、metric等filter。
  • HaStrategy: 高可用策略扩展点,默认提供failover。后续将提供backup request等策略
  • LoadBalance: 负载均衡策略扩展点。默认实现random、roundrobin。同时给出了支持跨group权重处理的warper实现类,方便不同LB策略支持按权重跨group负载均衡
  • EndPoint: endpoint为调用远程server的扩展点,可以理解为点对点直连场景下的client。默认提供motan协议实现与grpc实现。
  • Provider: 具体rpc服务的实现类扩展,可以是调用本地代码实现,也可以是跨进程实现。默认提供本地服务实现调用与CGI跨进程调用。
  • Server: 不同类型的server实现,一般与协议绑定。默认提供MotanServer实现。
  • MessageHandler: 针对默认Server实现MotanServer的不同类型消息处理扩展。
  • Serialization: 可以进行不同类型序列化实现扩展。默认支持simple序列化。

自定义扩展注册

实现一个扩展

实现一个自定义扩展需要实现上述扩展点接口的全部方法,以实现一个endpointFilter为例,需要先实现 endpointFilter接口中声明的方法。如下:

type MyEndPointFilter struct {
	url  *motancore.URL
	next motancore.EndPointFilter
}

// 获取此filter的index。index用来决定不同filter的执行顺序,index越小会越先执行。
func (m *MyEndPointFilter) GetIndex() int {
	return 20
}

func (m *MyEndPointFilter) GetName() string {
	return "myfilter"
}

// filter必须为多例模式,NewFilter用来创建一个新的filter实例。
func (m *MyEndPointFilter) NewFilter(url *motancore.URL) motancore.Filter {
	return &MyEndPointFilter{url: url}
}

//filter调用是嵌套模式,filter的最内层会调用实际endpoint进行远程请求。
func (m *MyEndPointFilter) Filter(caller motancore.Caller, request motancore.Request) motancore.Response {
	fmt.Printf("before call. request:%+v\n", request)
	// must call next filter in Filter implement
	response := m.GetNext().Filter(caller, request)
	fmt.Printf("after call. response:%+v\n", response)
	return response
}

func (m *MyEndPointFilter) HasNext() bool {
	return m.next != nil
}

// 设置filter链中的下一个filter
func (m *MyEndPointFilter) SetNext(nextFilter motancore.EndPointFilter) {
	m.next = nextFilter
}

// 获取filter链中的下一个filter
func (m *MyEndPointFilter) GetNext() motancore.EndPointFilter {
	return m.next
}

// filter 类型。filter分为endpointFilter 与clusterFilter,分别在调用的不同位置执行filter过滤行为
func (m *MyEndPointFilter) GetType() int32 {
	return motancore.EndPointFilterType
}

注册实现扩展类

实现了扩展类之后,需要把扩展类注册到ExtentionFactory,注册时需要一个扩展类的唯一标识,以及一个创建新扩展类的方法。用上面的MyEndPointFilter为例,注册到默认工厂类的方式如下:

weiboExtFactory := motan.GetDefaultExtFactory() //获取默认注册工厂类。
weiboExtFactory.RegistExtFilter("myfilter", func() motancore.Filter {
	return &MyEndPointFilter{}
}) //注册的唯一标识,即name为`myfilter`,注册的是创建一个新的`myfilter`类的匿名方法。

自定义扩展使用

在完成了自定义扩展的注册后,可以通过配置使用自定义扩展类。一般是通过在refer、service的对应配置项中使用注册时的name即可。例如上例中的myfilter,使用方法如下:

motan-refer:
  mytest-motan2:
    path: com.weibo.motan2.test.Motan2TestService
    registry: direct-registry
    serialization: simple
    basicRefer: mybasicRefer
    filter: "accessLog, myfilter" #此处配置了两个filter,一个`accessLog`为系统自带的访问日志记录filter,另一个是自定义的`myfilter`。

配置后,myfilter会在调用中生效。上述自定义扩展的例子可以参考main/agentdemo.go

自定义全局策略

Motan-go中有一些全局性策略支持自定义,全局策略与自定义扩展不同,自定义扩展一般是service纬度的功能扩展,全局策略一般针对agent的全局行为。支持扩展的全局策略一般会提供public的全局变量,扩展时直接对全局变量进行替换即可。Motan-go中支持的主要全局策略如下:

  • 管理端口安全策略。

    管理端口一般是提供给管理后台或者运维系统进行远程状态查询与操作的,为了降低安全风险,管理端口支持全局安全策略。默认安全策略是NoPermissionCheck即默认不进行安全验证。

    可以通过实现PermissionCheckFunc接口来自定义管理端口的安全策略。并通过修改motan.PermissionCheck = MyCheck来进行生效。

    例如,打算对管理端口中的某几个url进行限制,只能从本机发起调用,可以如下扩展:

    var CheckPaths = make(map[string]bool)
    
    func MyCheck(req *http.Request) bool {
        if CheckPaths[req.URL.Path] && !strings.HasPrefix(req.RemoteAddr, "127.0.0.1") && !strings.HasPrefix(req.RemoteAddr, "[::1]") {
            vlog.Warningf("verify ip fail! remote:%s, path:%s\n", req.RemoteAddr, req.URL.Path)
            return false
        }
        return true
    }
        
    func main() {    	
        CheckPaths["/switcher/set"] = true // 设置/switcher/set需要进行访问权限检查
        motan.PermissionCheck = MyCheck
        agent := motan.NewAgent(nil)
        agent.StartMotanAgent()
    }    
  • Trace策略

    Mesh的管理端口支持Debug调试功能,其中的trace 策略可以进行定制。通过实现TracePolicyFunc接口,并设置全局变量core.TracePolicy的值,即可使定制的trace策略生效。

    系统提供了NoTrace(默认)、RandomTrace、AlwaysTrace以及MeshTrace几种实现。其中debug端口的调试是通过MeshTrace来实现的。

    如果想实现自定义的trace策略,可以参考motan. MeshTrace的实现及使用方式。

  • 扩展管理端口功能

    Mesh的管理端口默认实现了状态管理信息查询Debug调试开关管理等几大类功能。如果想自定义更多的管理功能,Mesh也提供了扩展能力。

    Mesh支持RegisterManageHandler功能,可以为管理端口扩展新功能,或者覆盖原有端口的默认行为。自定义实现时,需要实现http.Handler或http.HandlerFunc接口即可。如果自定义的handler中需要使用到agent的行为,则可以进一步实现motan.SetAgent接口。扩展样例如下。更多的handler样例可以参考manageHandler.go

    func MyHandler() http.HandlerFunc {
        return func(rw http.ResponseWriter, req *http.Request) {
            fmt.Fprintln(rw, "my custom handler~")
        }
    }
    
    func main() {
        agent := motan.NewAgent(ext)
        agent.RegisterManageHandler("/version", wmotan.CheckVersionHandler()) // 覆盖管理端口/version的默认行为
        agent.StartMotanAgent()
    } 
  • Metric统计数据推送

    Motan-go的Metric统计数据默认支持推送的graphit,同时预留的扩展接口,支持其他数据存储系统。

    适配其他数据存储系统时,只需要实现metrics.StatWriter接口,并调用metrics.AddWriter方法即可。

Mesh保活

Service Mesh模式下,agent会成为服务交互链路中强依赖的一环,因此需要对agent的可用性进行保障。在容器化场景下,容器管理平台可以对容器可用性进行管理,在非容器化场景,或者不依赖容器管理平台进行可用性保障的场景下,需要agent自身提供保活功能。

agent提供了多种保活方式,如果使用第三方监控工具或者脚本,可以通过agent启动时记录的agent.pid文件中的pid进行监控或者agent的管理端口进行探活。

也可以简单使用agent自身提供的自保活功能。开启自保活功能后,agent将以主-子进程模式启动服务,其中主进程用来监控保活,子进程用来进行mesh服务的处理。

启动agent自保活的功能只需要简单的以ProcessKeeper进行启动,如下:

keeper := tools.NewProcessKeeper(func() {
	agent := motan.NewAgent(nil)
	agent.StartMotanAgent()
})
keeper.Start()

Mesh管理端口

Mesh的管理功能是通过agent的管理端口提供的,目前只在agent中提供管理能力。

管理功能主要包括状态管理信息查询Debug调试开关管理等几大类功能,详细说明如下。

状态管理

状态管理主要进行server侧agent(反向代理)提供服务(200)、停用服务(503)以及当前状态查询。也可以查看当前运行的agent版本信息。

  • /   查看agent状态。

    agent提供服务时,返回的http状态码是200。;agent未提供服务时,返回http状态码为503。一般可以通过调用此接口做agent存活监控。

    agent做正向代理时,200、503只是一个状态标记,并不会影响agent正向代理相关功能。

  • /200   提供服务。

    执行成功后,返回ok. 此时调用/查询会返回200。 agent做server侧反向代理时,会向注册中心进行服务注册和心跳。

  • /503   停止服务。

    执行成功后,返回ok. 此时调用/查询会返回503。 agent做server侧反向代理时,将会把代理的服务置为unavailable状态,不在对外提供服务。

  • /version   查询agent版本号。

    执行后返回当前agent的开源版本号以及内部适配版版本号。样例如下:

    OpenSourceVersion:1.0.0
    InternalVersion:0.0.6
    

信息查询

状态管理主要进行server侧agent(反向代理)提供服务(200)、停用服务(503)以及当前状态查询。也可以查看当前运行的agent版本信息。

  • /getConfig   查看配置文件内容。

    查看agent启动时配置文件的内容。

  • /getReferService   查看agent订阅的服务。

    查看agent作为正向代理订阅的服务。返回值样例如下:

    {
        "code": 200,
        "body": {
            "service": [{
                "name": "com.weibo.motan.demo.service.MotanDemoService",
                "status": true
            }, {
                "name": "routeguide.RouteGuide",
                "status": true
            }]
        }
    }

Debug调试

  • /debug/pprof/sw   调试功能开关。

    调试功能默认是关闭状态,需要先打开开关,才能进行调试。操作开关使用http的header进行操作。header值ctr为op(open pprof)是打开调试功能,为cp(close pprof)时关闭调试功能。curl样例如下:

    curl 'http://127.0.0.1:8002/debug/pprof/sw' -H ctr:op
    
  • /debug/mesh/trace   trace请求分步耗时。

    • 调试功能需要先调用/debug/pprof/sw接口打开调试开关
    curl 'http://127.0.0.1:8002/debug/pprof/sw' -H ctr:op
    
    • 采集trace信息条件支持按 address、group、service、ratio几个方面组合,其中匹配规则为全匹配。

      • 按请求地址采集。例:address=127.0.0.1
      • 按服务分组采集。例:group=motan-demo-rpc
      • 按服务名采集。例:service=com.weibo.motan2.test.Motan2TestService
      • 按比例采集。值为1-100之间的百分数,当小于1或大于100时忽略此配置,例如ratio=50,表示采集50%的请求
        (注:此比例按随机数法过滤,所以样本越大越接近这个比率,过滤结果并不精确)。
    • 请求支持指定采集数据时间长度,默认30秒,无最大采集时间限制,默认最大采集数据1W条。

      • 例:seconds=5 表示采集5秒。
    • 完整样例如下:

    http://127.0.0.1:8002/debug/mesh/trace?seconds=5&group=motan-demo-rpc&service=com.weibo.motan2.test.Motan2TestService&addr=127.0.0.1
    
    • trace数据返回值说明: 返回值样例:
    // mesh trace finish. trace size:1197, time unit:ns
    {"No":0,"trace":{   // No 表示采集数据条数编号,0代表第一条trace数据
        "requestid": 0, // request id
        "address": "127.0.0.1",  // 请求来源方ip
        "values": {
            "remoteTime": "71150672",   // 远程耗时
            "requestTime": "2723458",   // 处理request的总耗时
            "responseTime": "346741",   // 处理response的总耗时
            "totalTime": "74220871"     // 总耗时
        },
        "request_spans": [  // request(请求上行时)处理阶段汇总
            {
                "name": "receive",  // 不同阶段的名称,例如 receive表示数据接收阶段
                "address": "",  // address为空表示使用请求来源ip。下同
                "time": "2019-01-14T13:15:21.622993638+08:00",  // 阶段发生时间点
                "duration": 0    // 本阶段持续时长(与上一个阶段时间点差值)
            },
            {
                "name": "decode",   //解码阶段
                "address": "",
                "time": "2019-01-14T13:15:21.623080083+08:00",
                "duration": 86445
            },
            {
                "name": "convert",  //message转换为request阶段
                "address": "",
                "time": "2019-01-14T13:15:21.623093016+08:00",
                "duration": 12933
            },
            {
                "name": "clusterFilter",  // cluster filter执行完毕阶段
                "address": "",
                "time": "2019-01-14T13:15:21.623106892+08:00",
                "duration": 13876
            },
            {
                "name": "selectEndpoint", // 负载均衡执行完毕阶段
                "address": "10.41.26.36:8018",
                "time": "2019-01-14T13:15:21.623110727+08:00",
                "duration": 3835
            },
            {
                "name": "endpointFilter", // endpoint filter执行完毕阶段
                "address": "10.41.26.36:8018",
                "time": "2019-01-14T13:15:21.623112122+08:00",
                "duration": 1395
            },
            {
                "name": "convert",  // request转换为message阶段
                "address": "10.41.26.36:8018",
                "time": "2019-01-14T13:15:21.62568534+08:00",
                "duration": 2573218
            },
            {
                "name": "encode",   // 编码阶段
                "address": "10.41.26.36:8018",
                "time": "2019-01-14T13:15:21.625712107+08:00",
                "duration": 26767
            },
            {
                "name": "send", // 转发结束阶段
                "address": "10.41.26.36:8018",
                "time": "2019-01-14T13:15:21.625717096+08:00",
                "duration": 4989
            }
        ],
        "response_spans": [ // response(请求下行时)处理阶段,同request
            {
                "name": "receive",
                "address": "10.41.26.36:8018",
                "time": "2019-01-14T13:15:21.696867768+08:00",
                "duration": 0
            },
            {
                "name": "decode",
                "address": "",
                "time": "2019-01-14T13:15:21.696908354+08:00",
                "duration": 40586
            },
            {
                "name": "convert",
                "address": "",
                "time": "2019-01-14T13:15:21.697154928+08:00",
                "duration": 246574
            },
            {
                "name": "endpointFilter",
                "address": "",
                "time": "2019-01-14T13:15:21.697186084+08:00",
                "duration": 31156
            },
            {
                "name": "clusterFilter",
                "address": "",
                "time": "2019-01-14T13:15:21.697187004+08:00",
                "duration": 920
            },
            {
                "name": "convert",
                "address": "",
                "time": "2019-01-14T13:15:21.697187427+08:00",
                "duration": 423
            },
            {
                "name": "encode",
                "address": "",
                "time": "2019-01-14T13:15:21.697194951+08:00",
                "duration": 7524
            },
            {
                "name": "send",
                "address": "",
                "time": "2019-01-14T13:15:21.697214509+08:00",
                "duration": 19558
            }
        ]
    }}
    
  • /debug/pprof/    golang pprof提供的debug功能。

    支持golang pprof的debug功能,使用前同样也需要打开调试开关。 使用pprof排查问题可以参考下面几个方法:

开关管理

开关管理接口支持对agent运行时全局开关状态进行管理。通过全局开关可以动态改变agent的行为,例如新功能灰度、降级等

  • /switcher/set   设置开关状态。

    参数:

    • name:开关名。 必填
    • value:开关值, 必填. bool值,true或false

    请求样例:设置开关s1的值为false

    curl 'http://127.0.0.1:8002/switcher/set?name=s1&value=false'
  • /switcher/get   查询开关状态。

    参数:

    • name:开关名。 必填

    请求样例:获取开关s1的值

    curl 'http://127.0.0.1:8002/switcher/get?name=s1'
  • /switcher/getAll   查询所有开关。

Mesh管理指令系统

Mesh的管理以指令系统与管理端口为主。其中指令系统一般用来做全局性的治理行为,如服务或机房纬度的服务流量调度等;管理端口一般用来做单机行为管理,或者需要从agent获取数据的场景。例如在单台服务器上进行trace调试,查询某个应用方的agent配置与版本等。两种管理方式可能有功能重叠的部分,功能冲突时管理端口的能力优先。

Mesh指令系统目前支持流控指令、降级指令、开关指令。指令系统一般用来做临时性动态管理,不应存在长期生效的指令,也不应依靠指令系统进行持久化控制。长期管理行为可以通过配置文件进行管理。

指令系统通过注册中心的k-v存储,agent通过订阅对应的key来实时获取管理指令的变更。由于指令系统会影响全局服务行为,因此不建议人工直接操作,而是通过通用的管理后台进行指令的预览与下发。另外注册中心对对应的key的写操作也需要进行ACL管理。

指令系统订阅的纬度有两个,一个是针对group,另外一个是针对某个业务方(调用方),两个纬度的指令会叠加生效,指令冲突时业务方的指令优先级较高。指令的实际控制粒度是service,指令key的内容是多条指令,每条指令可以针对不同的service进行生效。

指令格式为json,样例如下:

{
	"clientCommandList" : [
		{
			"index": 1,
			"version": "1.0",
			"dc": "yf",
			"commandType": 0,
			"pattern": "*",
			"mergeGroups": [
				"openapi-tc-test-rpc:1",
				"openapi-yf-test-rpc:1"
			],
			"routeRules": [],
			"remark": "切换50%流量到另外一个机房"
		},
		{
			"index": 2,
			"version": "1.0",
			"dc": "yf",
			"commandType": 0,
			"pattern": "com.weibo.xxxx.User",
			"mergeGroups": [
				"openapi-tc-test-rpc:1",
				"openapi-yf-test-rpc:1"
			],
			"routeRules": [
				"10.73.1.* to 10.75.1.*"
			],
			"remark": "跨组调用+按规则路由"
		}	
	]
}

字段含义:

  • index: 解析指令前,按index值升序排序(值越小的先被解析)
  • version: 指令版本,通过版本解决指令兼容问题
  • commandType: 指令类型,0:流控指令,1:降级指令, 2:开关指令
  • dc: 标识哪个机房,目前暂未使用
  • pattern: service(或叫path、接口类名)的匹配规则,支持*号通配,支持复合表达式
  • mergeGroups: 该group下允许被调用的group,以及它们各自的权重比
  • routeRules: 路由规则,支持*号通配,支持!取反
  • remark: 备注,简单描述这条指令的作用

流控指令

流控指令的commandType字段固定为0.支持如下功能及样例如下,其中样例仅为单条指令

  • 按权重进行跨机房的流量切换

    {
        "index": 1,
        "version": "1.0",
        "dc": "yf",
        "commandType": 0,
        "pattern": "*",
        "mergeGroups": [
            "openapi-tc-test-rpc:1",
            "openapi-yf-test-rpc:1"
        ],
        "routeRules": [],
        "remark": "切换50%流量到另外一个机房"
    }
  • 指定IP进行切换

    {
        "index": 2,
        "version": "1.0",
        "dc": "yf",
        "commandType": 0,
        "pattern": "com.weibo.xxxx.User",
        "mergeGroups": [
            "openapi-tc-test-rpc:1",
            "openapi-yf-test-rpc:1"
        ],
        "routeRules": [
            "10.73.1.* to 10.75.1.*"
        ],
        "remark": "跨组调用+按规则路由"
    }
  • 排除灰度预览机器

    {
        "index": 3,
        "version": "1.0",
        "dc": "yf",
        "commandType": 0,
        "pattern": "com.weibo.xxxx.Preview",
        "mergeGroups": [],
        "routeRules": [
            "* to !10.75.0.1"
        ],
        "remark": "灰度预览某台机器,关闭其线上流量"
    }

降级指令

降级指令一般针对service 纬度进行整体降级,可以控制某个业务方(调用方)对某个service进行降级,或者所有的调用方对某个服务进行降级。单业务方降级是针对某个application的agent下发指令,全部调用方降级则针对group下发指令。

单条降级指令的样例如下:

    {
        "index": 1,
        "version": "1.0",
        "commandType": 1,
        "pattern": "com.weibo.xxxx.Preview",
        "remark": "com.weibo.xxxx.Preview服务降级"
    }

开关指令

开关指令一般针对某个业务方使用,例如业务方 A上线了一个新功能,需要动态变更开关的值。单条开关指令样例如下:

    {
        "index": 1,
        "version": "1.0",
        "commandType": 2,
        "pattern": "switchNameA:T,switchNameB:F",
        "remark": "开关switchNameA开启,开关switchNameB关闭"
    }

Motan2协议

Motan2协议相比Motan协议,更方便代理与跨语言解析。Motan2协议把请求中的meta信息与请求参数信息进行了分离,更适合对motan消息进行代理与路由;使用utf-8字符进行编码更适合不同语言进行解析处理。

Motan2协议分为Header、Metadata、Body三个部分。

Header

Header部分主要包括魔数、版本、消息类型、序列化方式、状态、消息id等内容,总共104bit(13byte)

Header字段名称 字段说明 长度(bit) 取值及意义
megicNum 魔数 16 固定为0xF1F1
messageType 保留 3 保留,后续扩展使用
是否心跳请求 1 1为心跳请求,0为非心跳请求
是否gzip压缩 1 1为gzip压缩,0为非压缩
是否单向请求 1 1为单向请求(不需要回包),0为非单向请求
是否代理请求 1 1为代理请求,0为直接请求
消息类型 1 0为request,1为response
version 协议版本号 5 原motan协议版本号为0,motan2协议版本号为1。
status 消息状态 3 0 正常消息,1 请求处理异常。共支持2^3即8种状态。request和response可以分别表示不同状态,其他状态待扩展
serialize 序列化协议 5 0 hessian,1 grpc-pb,2 json,3 msgpack,4 hprose,5 pb,6 simple,7 grpc-pb-json,8 breeze,其他待扩展
reserve 保留 3 保留
requestId 消息id 64 long型requestId。requestid需要保证对同一节点请求唯一。重复请求时可以使用相同requestId

Metadata

Metadata中包括请求的service名、方法、group以及携带的附加信息。Metadata只支持字符类型的k-v键值对,这些k、v使用utf-8进行编码,每个字符串之间通过\n进行分隔,因此,Metadata信息中不允许使用\n字符 Metadata部分的格式为:

字段名称 字段说明 长度(bit) 取值及意义
metasize metadata的长度 32 int32类型,metadata数据字节长度,为0时没有metadata。
metadata metadata的内容 由metasize指定 metadata信息。utf8类型string,由\n分隔的多个string组成kv对。

Metadata中,Motan框架使用的信息都是以M_开头的key,因此,通过Metadata携带自定义业务参数时,不允许使用M_作为自定义key的前缀

消息类型 参数名 含义 是否必填
request M_p 请求path。即请求的service名 T
M_m 请求方法名。 T
M_g 调用group F
M_md 请求方法参数描述 F
M_a auth认证信息 F
M_s 调用方信息 F
M_v 接口版本号 F
M_mdu module服务模块。 F
M_pp agent转发协议 F
M_is meta固定信息签名 F
response M_e 请求错误信息 F
M_pt 处理耗时 F

Body

Body部分用来存放请求的参数或响应的具体内容,body部分为header中指定序列化方式序列化后的byte数组。如果header中为gzip压缩状态,则Body部分是先进行序列化,然后进行gzip压缩后的byte数组。

Body部分格式为:

字段名称 字段说明 长度(bit) 取值及意义
bodysize Body部分的长度 32 int32类型,Body数据字节长度,为0时没有Body内容。
body Body的内容 由bodysize指定 body内容。例如request时的params,和response的value。

Simple序列化

simple序列化时为跨语言调用时使用的一种简单序列化协议,simple协议目前仅支持空对象、utf-8 string、string组成的map、以及byte字节流。simple协议比较适用于不包括复杂数据结构的跨语言交互,比较适合类似Rest风格的服务进行motan服务化转化。simple序列化在motan协议中的序列化编号为6.

simple序列化的格式为 type(1byte) + size(4byte) + content 其中type表示序列化对象的类型,size是序列化数据的字节长度,content是序列化后的内容,长度由size指定。

目前支持的类型如下:

type 说明
0 空对象
1 utf-8 string。 格式为:size +utf8string
2 string map,k、v都是utf-8 string。格式为total + size + keystring + size + valuestring ...
3 byte array. 格式为 size + byte array
4 string array. 格式为 total + size + utf8string + size + utf8string ...
5 bool. 格式为 1:true, 0:false
6 byte. 格式为 1byte
7 int16. 格式为 2byte
8 int32. 格式为 zigzag32
9 int64. 格式为 zigzag64
10 float32. 格式为 4byte
11 float64. 格式为 8byte
20 map. 格式为 total + simple(key) + simple(value) + ...
21 array. 格式为 total + simple(value1) + ...