-
Notifications
You must be signed in to change notification settings - Fork 112
zh_userguide
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端分别由endpoint
和server
两个模块实现。
Motan-go的代码结构按package进行主要功能模块进行划分,各package功能可以参考GoDoc文档。
强大的扩展能力是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方法即可。
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的管理功能是通过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/pprof/sw 调试功能开关。
调试功能默认是关闭状态,需要先打开开关,才能进行调试。操作开关使用http的header进行操作。header值ctr为op是打开调试功能,为cp时关闭调试功能。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:5, time unit:ns {"No":0,"trace":{ // No 表示采集数据条数编号,0代表第一条trace数据 "rid": 1534225814311141569, // request id "address": "127.0.0.1", // 请求来源方ip "req_spans": [ // request(请求上行时)处理阶段汇总 { "name": "recieve", // 不同阶段的名称,例如 recieve表示数据接收阶段 "address": "", // address为空表示使用请求来源ip。下同 "time": "2018-08-14T13:50:14.312066422+08:00", // 阶段发生时间点 "duration": 0 // 本阶段持续时长(与上一个阶段时间点差值) }, { "name": "decode", //解码阶段 "address": "", "time": "2018-08-14T13:50:14.312118102+08:00", "duration": 51680 }, { "name": "convert", //message转换为request阶段 "address": "", "time": "2018-08-14T13:50:14.312156928+08:00", "duration": 38826 }, { "name": "clustFilter", // clust filter执行完毕阶段 "address": "", "time": "2018-08-14T13:50:14.31216161+08:00", "duration": 4682 }, { "name": "epFilterStart", // endpoint filter开始执行阶段 "address": "127.0.0.1:8100", // 后续阶段是针对某个具体server节点的转发,address表示要请求的server节点。一次请求中,可能因为双方或者重试等HA策略,可能会有多个此阶段和后续阶段。 "time": "2018-08-14T13:50:14.312216112+08:00", "duration": 54502 }, { "name": "epFilterEnd", // endpoint filter执行完毕阶段 "address": "127.0.0.1:8100", "time": "2018-08-14T13:50:14.312228372+08:00", "duration": 12260 }, { "name": "convert", // request转换为mmessage阶段 "address": "127.0.0.1:8100", "time": "2018-08-14T13:50:14.312245718+08:00", "duration": 17346 }, { "name": "encode", // 编码阶段 "address": "127.0.0.1:8100", "time": "2018-08-14T13:50:14.312262482+08:00", "duration": 16764 }, { "name": "send", // 转发结束阶段 "address": "127.0.0.1:8100", "time": "2018-08-14T13:50:14.31227012+08:00", "duration": 7638 } ], "res_spans": [// response(请求下行时)处理阶段 { "name": "recieve", "address": "127.0.0.1:8100", "time": "2018-08-14T13:50:14.312574308+08:00", "duration": 0 }, { "name": "decode", "address": "", "time": "2018-08-14T13:50:14.312580082+08:00", "duration": 5774 }, { "name": "convert", "address": "", "time": "2018-08-14T13:50:14.31264366+08:00", "duration": 63578 }, { "name": "encode", "address": "", "time": "2018-08-14T13:50:14.3126481+08:00", "duration": 4440 }, { "name": "send", "address": "", "time": "2018-08-14T13:50:14.312667832+08:00", "duration": 19732 } ], "Values": {// 扩展字段, "totalTime": "601410" // 请求总耗时 } }}
-
/debug/pprof/ golang pprof提供的debug功能。
支持golang pprof的debug功能,使用前同样也需要打开调试开关。 使用pprof排查问题可以参考下面几个方法:
-
查看协程数、对象数、block、mutex等
-
分析cpu占用
go tool pprof -seconds 10 http://localhost:8002/debug/pprof/profile
-
分析堆对象
go tool pprof -alloc_objects http://localhost:8002/debug/pprof/heap
-
trace分析
http://localhost:8002/debug/pprof/trace 下载文件,然后执行go tool trace ./tracefile
-
pprof常用命令: top N、list XXX、tree
-
开关管理接口支持对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 查询所有开关。
TODO
Motan2协议相比Motan协议,更方便代理与跨语言解析。Motan2协议把请求中的meta信息与请求参数信息进行了分离,更适合对motan消息进行代理与路由;使用utf-8字符进行编码更适合不同语言进行解析处理。
Motan2协议分为Header、Metadata、Body三个部分。
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,其他待扩展 |
reserve | 保留 | 3 | 保留 |
requestId | 消息id | 64 | long型requestId。requestid需要保证对同一节点请求唯一。重复请求时可以使用相同requestId |
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部分为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协议目前仅支持空对象、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) + ... |