| layout | default |
|---|---|
| title | 核心竞争优势 |
核心竞争优势
- TOC {:toc}
重新造重复的轮子,就是浪费生命。
- 基础组件:大部分轮子都要把用户绑死在一个实现上。我们需要一个中间层,通过 interface 提供 API 给直接用户,提供 SPI 做为扩展点给其他的库作者。让业务和具体的轮子解耦,使得业务逻辑可以方便迁移。通过 SPI 让轮子之间的互相组合成为可能。
- 微服务模板生成:在线业务的微服务可以分成“配置”,“存储”,“流程”和“智能”四类。提供平台使得四类微服务共性的部分可以下沉,使得写业务的同学可以专注于业务逻辑本身。
- 迁移辅助工具:对于遗留应用,改代码的成本是非常高的。如果没有辅助手段,很难升级改造。通过在网络层分析流量,在不改动原有代码的情况下,实现类似单元测试覆盖的效果。
基础组件就是一些Go的Library。也支持 PHP 运行在 Go 上。
所有 fork 出去的 goroutine 都需要进行包装,从而统一进行 panic 的 recover。对于常驻后台的 goroutine,提供 supervisor 的功能在 panic 之后重启。
主 goroutine 也需要进行包装。提供 SPI 在退出之前做一些额外操作。
所有的协议编解码都可以抽象为以下几类对象的通过Accessor实现互相拷贝。当然代码里如果有对象互相拷贝的需求,深拷贝一个对象的需求,也可以使用 Copy:
- map 顺序读 => struct 随机写(比如json绑定到struct)
- struct 顺序读 => struct 随机写 (mysql协议等有schema的协议解析过程,原始的[]byte读的时候,无法做基于offset的随机定位)
- struct 顺序读 => map 顺序写 (struct的json序列化)
- 其他同类型对象的拷贝 (简单赋值)
在拷贝的过程中,如果有类型不符等场景,可以提供 SPI 去做 Accessor 的包装。比如把一个 int 类型的 accessor 转换为 string 类型的 accessor。这样,无论是什么协议的编解码,都可以用统一的方式来做外部扩展。
所有的参数验证都可以抽象为对象图的遍历。通过 Accessor 接口,我们可以很容易遍历任意输入。很大量的参数验证的代码都可以被简化掉。
基于Accessor接口,我们可以提供一些常见的函数式编程的便利性。
- map
- fold
- filter
- sort / sorted
- to_map
- ...
- linq:语言内的sql
logger 接口提供抽象地进程对外输出“非业务” event 的能力。日志和指标是常见的两种类型的event。“非业务”主要体现在非功能性需求上:
- 写event不参与事务。不能因为写event失败,使得业务操作回滚。
- 量比较大,只保证最大努力地可靠性,在高负载下可丢弃。
- 只写不读。业务逻辑本身不依赖于这些 event。
所有的RPC服务,都可以抽象为 map[string]Handler。key是方法名,value是方法的handler。Hnandler就是一个方法
func Handle(ctx context.Context, request MyRequestType) (response MyResponseType, err error)提供 SPI 来处理:
- request/response的对象拷贝问题
- IDL,验证
- 服务注册
- 限流
- 超时控制
- 指标监控
所有的对外RPC调用可以分为两类:
- 封闭型的服务:比如特定业务的HTTP/THRIFT接口
- 开放型服务:Redis/Mysql等。常见的做法是封装DAO,把开放型的接口封闭起来。所以最后也是变成封闭型的接口。
所有的PRC调用,于是都可以抽象为
func Call(ctx context.Context, serviceName string, methodName string, request MyRequestType) (response MyResponseType, err error)提供SPI来处理:
- request/response的对象拷贝问题
- IDL,验证
- 服务发现/负载均衡/故障节点摘除
- 熔断/降级
- 指标监控
MQ 本身也是一个同步的rpc服务。只是rpc调用的是一个通用的队列服务。从调用者角度来说,MQ其实是同步的rpc,而不是异步的。写MQ和用mysql存一个event到表里面,其实并没有本质区别。所以MQ也可以适配到抽象的Client模型。
- 让 PHP 跑在 Go 上:通过 https://github.com/taowen/go-php7
- 让 Libc 的同步 i/o 跑在 Go 上
- 让 PHP 所有的 c 扩展的 i/o 跑在 Go 上,从而实现 PHP 的 i/o 异步化,同时能够复用所有的 Go 的库
框架模式,业务逻辑以插件的方式托管到平台上,快速实现新的微服务。
提供具体业务无关的配置能力。配置格式定义之后,自动生成配置界面。配置数据配送到机器,提供进程内缓存和更新的能力。常见的配置访问方式提供sdk
- 开关类配置的 sdk
- i18n配置的 sdk
- 规则类配置的 sdk
提供具体业务无关的存储能力。存储分为两部分
- 主存储:带版本的文档型存储。实质上是一个event store
- 视图:所谓从库
- 详情类视图: mysql/redis/elasticsearch,各类存储。按不同的主键重新hash
- 统计类视图:业务指标统计
核心功能
- 幂等性:业务无需自己实现幂等性。通过把业务逻辑托管给存储平台,实现所有command处理的自动幂等。
- 并发下的数据完整性保证:通过乐观锁和业务逻辑托管,保证单个entity(aggregate root)的状态永远不会违背业务规则。
- 视图的同步:托管视图的同步逻辑。实现推拉结合的视图同步方式,保证更新永远不丢(通过拉),保证更新尽可能的及时(通过推)。
提供具体业务无关的流程控制能力。有点类似 bpm。主要提供两个能力:
- 流程类逻辑的简化编程
- 最终一致性的框架性保证。
支持以下几类主要流程类型:
- 在线同步顺序更新多个主存储
- 在线+异步的业务逻辑
- saga pattern:带回滚的业务流程
- 审批类需求:把人融入到流程中
流程支持嵌套叠加。允许不同的团队负责流程的某一段。不要求全部集中在一起(无论是逻辑上,还是物理上的)。
业务系统的core domain往往体现为通过机器代替人类进行智能决策。这种智能决策有两种实现方式:
- 业务规则:比如计费,比如税的计算
- 机器学习的模型
这类微服务比较难以抽象出共性的部分进行下沉。只能是把输入输出做一些简化,还有就是类map/reduce的计算模型的抽象。
独立的工具,有界面有命令行。
通过内核抓包,知道哪个线程在收发哪些数据。通过数据的协议解码,知道这些数据是在做什么样的RPC调用。通过统计,实现指标监控。通过上下游tcp连接信息的串联,实现分布式trace。
Accessor的抽象模型是能够快速地支持各种协议编解码的关键。
流量监控抓下来的包,可以用于在线下实现单接口的流量回放,从而部分替代单元测试,解决遗留应用不敢改的问题。结合IDL,实现流量编辑,用于测试新功能。