近一两年来,微服务架构已经成为热门话题(microservices.io),与传统的一体化应用架构相比,微服务架构在开发、测试、部署方面都有众多吸引人之处,愈来愈多没有历史包袱的新项目都启用微服务架构的模式来开发。html
咱们这个团队通过深刻思考以后,决定在一块儿美这个APP的后端开发中,选择go做为开发语言,采用微服务模式来实现,通过近半年的实践,造成了一些心得,简单总结后分享出来,但愿可以给你们一些帮助。git
不一样的团队在选择基础框架(库)时考虑的要素不一样,咱们团队更喜欢小而美的框架,尽量不要让框架侵入业务,易于升级、维护和替换,因此咱们更愿意选择Library而不是Framework。github
在web方面,咱们选择了negroni做为middleware库,采用性能不错的httprouter替换go标准库的mux,而没有用任何web相关的框架。golang
在微服务之间的rpc调用方面,为了未来的扩展性、跨语言调用等因素,咱们没有直接用go标准库的rpc模块,而是采纳了google最新推出的grpc。但grpc自己属于比较重型的rpc框架,对业务代码有必定的侵入性, 咱们作了一系列的库(包括worpc、worc、wonaming等)来屏蔽这些没必要要的业务代码侵入 ,保持了业务代码自己的整洁。web
在微服务体系中,如何切分微服务也是一个重要的话题,在咱们的实践中,咱们遵循了以下一些原则:后端
Gateway是微服务对外提供服务的一个屏障,它的核心点在于:架构
微服务体系中,服务的注册和发现对总体架构很是重要,尤为对于同步的rpc调用,每一个服务有多少实例,每一个实例的地址等,都须要有一个统一的管理。咱们采用etcd保存服务信息,同时封装了wonaming做为微服务注册和发现的中间件,它的主要功能包括:负载均衡
/wonaming
目录进行监控,当有服务注册或者解注册时,动态维护可用服务清单。r := wonaming.NewResolver(name)
b := grpc.RoundRobin(r)
conn, err := grpc.Dial(etcd, grpc.WithInsecure(), grpc.WithBalancer(b))
grpc是一个比较重的rpc框架,当客户端经过grpc调用服务端时,须要大量的重复性代码来创建链接、调用、处理错误返回等,影响业务代码的整洁性,而且对业务代码具备很强的侵入性,为了规避这个问题,咱们封装了worc,以实现便捷的grpc调用:框架
resp, err := worc.CallRPC(ctx, "hello", "Hello", req)
grpc提供了interceptor机制,但并无提供chain来实现不一样的中间件的顺序执行,为了将不一样的中间件功能(如鉴权、日志、recover)封装在不一样的函数里,worpc提供了组合gprc interceptor为一个chain的能力,能够根据自身业务的须要,撰写不一样的grpc中间件进行组合,好比实现 grpc 的 recovery 与 log 中间件:运维
func Recovery(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
defer func() {
if r := recover(); r != nil {
// log stack
stack := make([]byte, MAXSTACKSIZE)
stack = stack[:runtime.Stack(stack, false)]
log.CtxErrorf(ctx, "panic grpc invoke: %s, err=%v, stack:\n%s", info.FullMethod, r, string(stack))
// if panic, set custom error to 'err', in order that client and sense it.
err = grpc.Errorf(codes.Internal, "panic error: %v", r)
}
}()
return handler(ctx, req)
}
func Logging(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
start := time.Now()
log.CtxInfof(ctx, "calling %s, req=%s", info.FullMethod, marshal(req))
resp, err = handler(ctx, req)
log.CtxInfof(ctx, "finished %s, took=%v, resp=%v, err=%v", info.FullMethod, time.Since(start), marshal(resp), err)
return resp, err
}
s := grpc.NewServer(grpc.UnaryInterceptor(worpc.UnaryInterceptorChain(worpc.Recovery, worpc.Logging)))
经过以上组合,可为微服务提供panic恢复能力,保障服务稳定可用;同时还将上文中提到的注入context中的trace id取出,这样Gateway与微服务的日志经过就衔接了起来,方便查错、调优等。
grpc.Errorf
封装业务中的逻辑错误,随grpc服务调用一块儿返回,将业务response与error 分离。以上是微服务架构在咱们团队的实践方案,麻雀虽小,五脏俱全。经过各中间件的灵活组合,保障业务有序与服务的高可用,还不抓紧实践起来?在后续的文章中,咱们还会介绍目前微服务测试、运维及部署方案。