原文地址:http://blog.csdn.net/houjixin/article/details/54914019
微服务,说比做容易!我们更关注怎样将这种架构设计思想融入到实际工作中,微服务更关注各服务之间的调用、管理,它追求软件研发过程中的自动化,例如:自动编译、打包、发布,自动化运维等等,这里的每一个自动化的地方都需要有相应的软件、脚本做支撑;在实际应用过程中,我们很难短时间内把所有的自动化基础软件熟练应用起来,一方面,时间紧迫,很难有公司会专门抽出来时间来搭建和构造这样的一套自动化系统出来,并且培训好相关的人员;另一方面,很难找到合适的人员来把这一整套系统熟练搭建病应用起来;绝大多数情况下,公司更关注的是怎样尽快把业务实现。因此,我们在实践过程中,先把与业务相关的部分做起来,然后再逐步完善各自动化模块,进而构建起微服务的整个自动化系统。
在微服务程序开发过程中,根据过往几年的经验,我们发现下面一些事项是做微服务开发时必须注意的点:
1. rpc框架选型
在做技术选型时,应该是自研还是选择开源框架?如果选择开源,那么应该选择哪个开源框架?RPC框架是微服务的核心组件之一,是服务之间调用的基础,因此其选型尤其要慎重,所选用的框架应该尽量满足:
- 成熟、稳定、高效的要求;对于框架而言,其稳定性、可靠性将压倒一切其他要求;
- 尽量能与团队技术栈相匹配,虽然很多人说开发语言不重要,其实在自己的技术水平还不够的时候,开发语言还是挺重要的!在选择框架是应该选择对团队最擅长语言支持度最好的框架;
- 最好能支持多语言,以便支持未来的发展。
基于上述原因,我们选择Thrift作为rpc框架。
2. 同一公司内部的rpc框架必须统一;
3. 服务调用sdk的封装,sdk包含两部分:底层通信连接池和业务的stub文件,rpc客户端的同步与异步,同步更简单,为提高效率(减少tcp连接建立和断开的消耗),需为客户端封装连接池,由于每个服务的接口不一样,需要提供对rpc的stub文件的封装,服务开发者不仅是提供服务端开发还要提供对stub文件和连接池的sdk封装,以方便客户端使用。
4. 强制使用metrics对每个接口进行调用统计,统计标识为:类名.函数名;
5. 每个服务实例需要有一个唯一名称,在服务启动时打印出来,在调用其它微服务时也会用到该名称;
6. 强制对外接口函数必须有caller参数,即调用一个接口函数时需在参数中表明自己是谁?
7. 规范日志格式,所有服务的日志格式必须统一,以便日志系统进行解析处理。
8. 强制使用日志ID,所有函数的参数中必须带日志ID参数,该ID在整个系统的入口处从ID生成器处获取,并在服务内部各函数之间、各服务之间进行传递,每条日志输出必须带上日志ID。
9. 构建统一日志系统,该系统需要完成以下功能:
(1) 统一收集所有服务的日志信息;
(2) 统一对日志格式进行解析处理,例如建立索引;
(3) 使用ElasticSearch构建日志检索功能;
(4) 建议使用Kibana等展示工具便于用户对ES中的日志数据进行检索。
统一日志系统能协助开发运维人员快速定位、分析问题;在微服务架构中,一个系统可能会由几十甚至上百的程序运行实例,分别部署到不同的主机上,一旦用户发现某个操作出现问题,此时就需要取出所有相关服务程序的日志进行分析,如果没有统一的日志系统,问题分析和定位将变得非常困难。
10.服务启动日志,启动成功时必须有醒目日志提示服务启动成功,以及本服务的实例名,版本号,服务端口号,明确告知运维同事启动是否成功。
11.启动时,对于服务引用其他组件或服务的初始化之前和之后必须同时打印提示日志,例如:开始初始化XXX,初始化XXX成功,防止初始化时候陷入对其他服务的处理中。
12.强制为每个服务程序提供telnet或jmx功能,每个服务都需要在运行期间对其进行动态管理,例如停止该服务、查询服务的状态等等,因此我们需要在所开发的服务中加上telnet或者jmx为用户提供一种动态运营时的管理功能。
13.版本管理,包括以下几个方面:
(1) 各服务程序的git,版本号命名需规范统一;
(2) 内部版本号最好硬编码到代码中,这样防止误修改;
14.配置分离与动态配置,配置分离包括:配置文件可自由自动位置,和配置参数的分类,不同类型的参数放在不同配置文件中,在配置参数比较多,类型比较复杂时,这种方式便于运维和后续管理;
15.连接池对负载均衡的影响
一般情况下,采用连接池将会削弱负载均衡的功效,此时负载均衡只在连接建立时起作用,一旦连接建立完成,后续业务的分配将在已有连接上进行,即便当前的连接分配已经很不均匀,也无法自动调整。
16.统一规范返回码
可以参考http的返回码定义方式,以4开头的表示客户端的错误,例如各种参数错误,以5开头的表示服务端出现的各种错误,根据业务来确定返回码长度,如果业务要求返回的信息更详细,则可以定义更长的返回码。