如何把项目改成微服务项目_微服务拆分那点事

Mar. 19th 2018 BY 王保平 wangbaoping@360.cn

背景

最近参与了两个项目的开发,两个项目都有多组件,各自服务功能清晰等特点,也就是所谓的微服务,再结合以前的一些单体项目的开发经验,这里主要探讨一下我所理解的微服务和单体项目的优缺点。

fig1 Microservice Architecture

我的理解

其实所谓这些服务的拆分与否都是与很多因素有关系,比如:该项目的开发人员数目,该项目的运维敏感度,项目的紧急程度,开发人员的技术熟练程度,微服务架构的基础储备程度等等。

因为我们的最终目的是将项目快速完整的实现好,而不是为了显得逼格高而微服务,不是为了人多每人分点活,而故意拆成微服务。总之不能为了微服务而微服务。

比如该项目总工就一个人开发,然后该项目你重启一下服务,对用户接入没啥敏感性,本来就是个没有并发的对内系统,那就完全没必要拆成微服务,就写个单体的,把接入层,数据处理层等通过模块化的代码方式拆开就行了,没必要增加复杂度,加上一些 rpc,把一个单体服务拆成四五个微服务,然后增加自己的运维成本和实现成本。

如果该项目是多人协作的,有一定的并发度,对用户接入比较敏感,不能随便重启,并且开发者都对微服务有一定的经验,并且底层 rpc,连接池等初始化库都有积累,然后有比较丰富的 rpc 多服务运维经验,那就可以拆微服务,拆完之后,服务的水平扩展一般是线性的,可以动态的根据流量扩容和缩容。

底层其他非接入层的服务,比如数据处理服务,session 服务的重启与上线都不会影响整个项目的接入。同时就提高了该项目的容错性。整体项目的开发进度都能以服务为维度,各自负责一个服务,快速迭代与滚动更新上线。而不像单体应用,一般上线与迭代总是牵一发而动全身。

我的对比

所以整体从以下几个维度,我来对比一下优缺点。

我怎么拆分

接下来根据一个具体的项目实例,看看如何将一个单体项目,拆分成微服务。该项目是前两天刚做的一个共享积分项目。

项目背景

该项目主要工作两部分,一部分,积分设备上报信息给服务端。第二部分:服务端根据设备上报的信息计算分配换算成工作量,然后按工作量百分比分配相应的积分。同时该项目对接别的用户系统和 boss 系统等等。

该项目总共三人:三人都参与过微服务的开发,有基础库的储备。时间比较紧,一周的开发周期。

一开始该项目其实因为复杂度不是太高,其实完全可以做成一个单体应用就行。比如项目目录:src 下,一个 collector 目录收集矿机上报,一个 finance 目录处理一些金融数据。再来一个 API 目录处理接入请求就行了。外加一些 util 的公共组件。

然后一个人开发就行了。两周的时间应该能调通。

但是前面也说了,单体应用有诸多的弊端。并且主要还剩两人,你一个人用两周的时间,如果拆成微服务,三人都有相关经验,一个礼拜肯定就可以保质保量的完成相应组件的开发与测试。

因为微服务的每个组件其实都是一个独立的单体应用。组件之间的开发是没有关联的,都是依赖公共库。互相之间的调用都是通过 rpc,所以开发是可以并行开发。互不干扰。效率肯定是非常高的。

所以现在我们开始简单将这个单体应用拆成微服务。

第一步:根据服务职责拆分

其实微服务的拆分最根本是一些代码职责的拆分和抽象,这一步和我们模块化的时候思路是一样的。比如该项目,矿机在不断地上报数据,然后我们通过上报的带宽给矿机分配虚拟币额度。这个其实细分其中的职责,我们可以看到我们需要一个收集上报数据的模块,只负责收集数据,这里抽象了各种数据来源,比如把矿机和其他业务数据接口都统一到 collect 模块。这个职责就很明确了。

同时这些数据收集上来以后得集中运算,算完之后得通过内部分配算法,给矿机分配虚拟币额度。这其实是一个类似经济系统的职责,该系统只负责处理金融信息,是个 finance 经济系统。职责也很明确。

这两个模块对外暴露各种 API 接口。这个可以抽出来,单独一个接入组件,负责对外统一处理所有的 API 请求。所以单独起一个 center 组件。

这个项目相对功能不复杂,所以拆分完,也就三个组件,职责已经比较明确了。

第二步:公共库的初始化

我们把公共的库都放在 common 里面,这里面包括了 log,config,errors 等基础库,还有 redis,mongo,mysql 等 db 的连接池初始化,还有 rpc 的连接池初始化,这里或者用 grpc 或者用户自己基于 go 自带的 rpc 的二次封装等。还有 trace 等用于追踪请求方便日志查询的基本库。

这些基础库是我们做微服务的必备,通常开始一个新项目,在前期需求讨论完后,编码前期我们会先把这些公共库的初始化工作都做了,比如 DB 的一些连接池初始化不同项目稍微有一些不同。rpc 等连接池代码基本都是能复用的,其他的公共库,直接拖到新项目里就能开搞。

第三步:组件之间接口的定义

在初始化完公共库之后,我们先不着急写代码,先把组件之间的接口定义好。这里比如我们三个组件 center,collector,finance 三个组件

center 是接入层,这里统一处理所以的 API 请求。http 或者 https,具体 API 接口是业务相关。这里不做描述。接入层可以做很轻量的数据处理,不宜过重。不做有状态的数据存储。是一个无状态服务,最终的数据处理通过 rpc 传给后端相应的组件,基本是一个纯转发的组件。

在配置文件中已经配好了,相应的组件的 rpc 地址,rpc 公共库中我们二次封装了基本的请求逻辑优先级,第一同机器,第二同机房,第三跨机房。

collect 和 finance 组件都对 center 暴露了 grpc pb 接口,因为请求从 center 进来,会通过 rpc 转到相应组件做单独处理。collect 系统和 finance 系统之间,是单向的数据流动,finance 系统需要从 collect 系统获取数据去统一运算。所以 collect 系统还需要给 finance 系统暴露 rpc 接口。

具体的 grpc pb 接口有自己的定义语言,比较简单容易上手,在相应的目录下新建 proto 文件,定义完接口以后,让自动生成 pb.go 即可,最后代码交互都是走的 pb.go 里面的结构体。具体的 pb 编写参考文档即可。

第四步:开始分工写自己的组件了

到此开始编写代码了。每个人都是相对独立的开发,因为接口定义好了,公共库也都已经初始化完毕,然后开发就是完全并行了。编写完之后,自己的组件可以依靠单元测试,做一些基本的测试。然后等联调即可。

总结

以上只是一个相对比较简单的项目拆分。这里也主要说的是一个拆分的思路,和具体实现微服务的时候一些基本工程流程。如果项目比较复杂,可能拆分出来的组件数目就相对较多。本文也主要是聊聊拆分的时候一些我理解的原则,具体实现细节,其实微服务还有很多有意思的地方,比如公共库中一些 db 的连接池初始化和 rpc 的连接池初始化,配置的集中管理,动态加载等。单独抽出来都是挺有研究意义的。

面对面:

Q:你认为微服务拆分最应该关注是么?

A:文中提到,拆分还是应该关注服务职责足够明确,至于微服务的优缺点文章已经提过了,不在赘述,这里补充一点不要为了拆分而拆分,要综合人力,时间,开发成本,业务场景,运维成本等多维度综合考虑。

Q:你后面提到的一些有意思的研究点可否提前分享下?

A:本文是我在开发了两个微服务架构后的一些理解。因为项目本身有一些并发,所以项目的初始化和没有并发的项目有一点不同,比如平常项目的一些 db 操作都是读取数据的时候才建立连接,而微服务的一般都是根据业务请求并发提前初始化一个量级的连接池,保证后端 db 的读写压力。

同时 rpc 的也是同样连接池的概念,这样就少了很多的 rpc 建立连接和释放连接的网络消耗,这在高并发场景中会很大程度上减少服务器压力。并且我们为了保证 rpc 连接的耗时,第一优先连接同宿主机,第二优先同机房机器,最后才选择跨机房等。并且在修改了 rpc 地址配置之后,不需要重启组件,底层 rpc 框架可以自适应同步配置,建立新连接和断掉旧连接。

微服务的配置一般都是集中管理,动态加载,这种我们部门内部的 qconf,或者我们当前项目自己写的 gokeeper 等,都是类似的。

以上这些点可能在后续会有更进一步分享,敬请关注。

本文链接:

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值