对于一个互联网企业,后端服务是必不可少的一个组成部分。抛开业务应用来说,往下的基础服务设施做到哪些才能够保证业务的稳定可靠、易维护、高可用呢?纵观整个互联网技术体系再结合公司的目前状况,个人认为必不可少或者非常关键的后端基础技术/设施如下图所示:
这里的后端基础设施主要指的是应用在线上稳定运行需要依赖的关键组件/服务等。开发或者搭建好以上的后端基础设施,一般情况下是能够支撑很长一段时间内的业务的。此外,对于一个完整的架构来说,还有很多应用感知不到的系统基础服务,如负载均衡、自动化部署、系统安全等,并没有包含在本文的描述范围内。
Api网关
在移动app的开发过程中,通常后端提供的接口需要以下功能的支持:
- 负载均衡
- api访问权限控制
- 用户鉴权
一般的做法,使用nginx做负载均衡,然后在每个业务应用里做api接口的访问权限控制和用户鉴权,更优化一点的方式则是把后两者做成公共类库供所有业务调用。但从总体上来看,这三种特性都属于业务的公共需求,更可取的方式则是集成到一起作为一个服务,既可以动态地修改权限控制和鉴权机制,也可以减少每个业务集成这些机制的成本。这种服务就是Api网关(http://blog.csdn.net/pzxwhc/article/details/49873623),可以选择自己实现,也可以使用开源软件实现,如Kong。如下图所示:
但是以上方案的一个问题是由于所有api请求都要经过网关,它很容易成为系统的性能瓶颈。因此,可以采取的方案是:去掉api网关,让业务应用直接对接统一认证中心,在基础框架层面保证每个api调用都需要先通过统一认证中心的认证,这里可以采取缓存认证结果的方式避免对统一认证中心产生过大的请求压力。
业务应用和后端基础框架
业务应用分为:在线业务应用和内部业务应用。
- 在线业务应用:直接面向互联网用户的应用、接口等,典型的特点就是:请求量大、高并发、高可用、对故障的容忍度低。
- 内部业务应用:这个是面向公司内部的应用。比如,内部数据管理平台、广告投放平台等。相比起在线业务应用,其特点: 数据保密性高、压力小、并发量小、允许故障的发生。
业务应用基于后端的基础框架开发,针对Java后端来说,应该有的几个框架如下:
- MVC框架:从十年前流行的Struts1、2到现在最为推崇的SpringMVC、Jersey以及国人开发的JFinal、阿里的WebX等等,这些框架尤其是后面流行的这些都是各有千秋的。选型的主要因素是看你的团队是否有一个对某框架能够做二次开发、定制的人在。很多时候,针对这些通用的框架,你是需要做一些特定的开发才能满足特定的需求的。比如,很多团队传递参数使用的都是UnderScore的命名法(下划线连接单词),但是Java中确是使用LowCamel命名的。对于SpringMVC,可以通过注解的alias来指定,但这样需要对每一个参数都要指定alias有点效率太低,此外ModelAttribute也不支持别名,更好的方式是在框架层面统一对参数做Camel命名的转换达到目的。
- IOC框架:ioc带来的好处无须多言。目前Java中最为流行的Spring自诞生就天然支持IOC。
- ORM框架:MyBatis是目前最为流行的orm框架。此外,Spring ORM中提供的JdbcTemplate也很不错。当然,对于分库分表、主从分离这些需求,一般就需要实现自己的ORM框架来支持了,像阿里的tddl、当当的sharding-jdbc(从datasource层面解决了分库分表、读写分离的问题,对应用透明、零侵入)。此外,为了在服务层面统一解决分库分表、主从分离、主备切换、缓存、故障恢复等问题,很多公司都是有自己的数据库中间件的,比如阿里的Cobar、360的Atlas、网易的DDB,还有官方提供的MySQL Proxy以及开源的MyCat、kingshard和收费的oneproxy。目前,线上有一定规模使用的应该是kingshard,当然如果不缺钱也可以上oneproxy。
- 缓存框架:缓存框架主要指的是对redis、memcached这些缓存服务器的操作统一封装,一般使用Spring的RedisTemplate即可,也可以使用jedis做自己的封装,支持客户端分布式方案、主从等。
- JavaEE应用性能检测框架:对于线上的JavaEE应用,需要有一个统一的框架集成到每一个业务中检测每一个请求、方法调用、jdbc连接、redis连接等的耗时、状态等。jwebap是一个可以使用的性能检测工具,但由于其已经很多年没有更新,有可能的话建议基于此项目做二次开发。
一般来说,以上几个框架即可以完成一个后端应用的雏形。
对于这些框架来说,最为关键的是根据团队技术构成选择最合适的,有能力开发自己的框架则更好。此外,这里需要提供一个后端应用的模板或生成工具(如maven的archetype)给团队成员使用,可以让大家在开发新的应用的时候,迅速的生成雏形应用,而无需再做一些框架搭建的重复性劳动。
缓存、数据库、搜索引擎、消息队列
缓存、数据库、搜索引擎、消息队列这四者都是应用依赖的后端基础服务,他们的性能直接影响到了应用的整体性能,有时候你代码写的再好也许就是因为这些服务导致应用性能无法提升上去。
缓存
如缓存五分钟法则所讲:如果一个数据频繁被访问,那么就应该放内存中。这里的缓存就是一种读写效率都非常高的存储方案,能够应对高并发的访问请求,通常情况下也不需要持久化的保证。但相对其他存储来说,缓存一般是基于内存的,成本比较昂贵,因此不能滥用。
缓存可以分为:本地缓存和分布式缓存。
- 本地缓存:主要指的是内存中的缓存机制。在Java中,Google Guava中就提供了本地缓存的实现机制。当然使用java的ConncurrentHashMap你也可以实现自己的本地缓存方案。
- 分布式缓存:指的单独的缓存服务。几年前比较流行的是memcached,但其只是一个KV的存储,支持的数据结构太少。现在最为流行的就是Redis,能够支持丰富的数据结构,基于事件驱动的单线程非阻塞IO也能够应对高并发的场景。集群方案除了官方的redis cluster, 目前比较流行的还有豌豆荚的codis、twitter的twemproxy。
对于缓存的使用,需要注意以下几点:
- 缓存的失效机制:当给某一个key设置了有效期,那么缓存何时对此key进行删除呢?一般来说会有以下几种方式:
- 守护进程定时去扫描key,找到已经失效的key,然后删除
- 读取key的时候先去判断key是否失效,如果失效则删除并返回空。
- 缓存的淘汰机制:是当缓存内存达到上限时如何删除缓存中的key。Redis提供了以下数据淘汰策略:
- volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰
- volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰
- volatile-random:从已设置过期时间的数据集中任意选择数据淘汰
- allkeys-lru:从数据集中挑选最近最少使用的数据淘汰
- allkeys-random:从数据集中任意选择数据淘汰
- no-enviction(驱逐):禁止驱逐数据
对于其具体的实现机制,可以参考《Redis设计与实现》一书
- 缓存的更新机制: 通常来说有四种方式:Cache aside, Read through, Write through, Write behind caching,具体的可见陈皓大神的这篇总结:缓存更新的套路。
- 缓存的服务过载保护:缓存的服务过载指的是由于缓存失效,而引起后端服务的压力骤增,进一步产生雪崩效应。这个现象和缓存更新是相关的,采取何种策略在缓存失效的时候去更新缓存直接决定了服务过载的保护机制。通常的分为客户端和服务端的应对方案。前者的方案有:基于超时的简单模式、基于超时的常规模式、基于刷新的简单模式、基于刷新的常规模式、基于刷新的续费模式。后者的方案则是很常见的流量控制和服务降级。具体的可以看美团技术团队总结的这篇文章:Cache应用中的服务过载案例研究。
数据库
数据库是后端开发中非常常见的一个服务组件。对于数据库的选型,要根据业务的特点和数据结构的特点来决定。
从存储介质上,数据库可以分为:
- 内存数据库: 数据主要存储在内存中,同时也可以采取措施对数据进行持久化到硬盘中。如Redis、H2DB的内存模式。对于这种数据库,由于内存成本昂贵,因此一定要做好存储的量化分析、容量预估,防止内存不足造成服务不可用。
- 硬盘数据库:数据存储在硬盘上的这种数据库是最为常见的。MySQL、Oracle、Postgresql、HBASE、H2DB、SqlLite等等都是硬盘数据库。此外,SSDB是基于SSD硬盘的KV数据库,支持的数据接口很丰富,是Redis的另外一个选择。
从存储数据类型、数据模式上,数据库可以分为:
- 关系型数据库:MySQL、Oracle、Postgresql都是关系型数据库的,是采用关系模型(关系模型指的就是二维表格模型,而一个关系型数据库就是由二维表及其之间的联系所组成的一个数据组织)来组织数据的数据库。
- 非关系型数据库:非关系型数据库是相对关系型数据库来讲的。以键值对存储,且结构不固定,每一个元组可以有不一样的字段,每个元组可以根据需要增加一些自己的键值对,这样就不会局限于固定的结构,可以减少一些时间和空间的开销。但是,其没有关系型数据库那种严格的数据模式,并不适合复杂的查询以及需要强事务管理的业务。非关系型数据库又可以分为:
- KV数据库:主要以(key,value)键值对存储数据的数据库。以Redis、RocksDB(levelDB)、SSDB为代表。
- 文档数据库:总体形式上也是键值对的形式,但是值里面又可以有各种数据结构:数组、键值对、字符串等等。以mongodb、couchdb为代表。
- 列数据库:也叫作稀疏大数据库,一般是用来存储海量数据的。相对于行数据库,这种数据库是以列为单位存储数据在介质上的。以Hbase、Cassendra为代表。
和数据库相关的一个很重要的就是数据库的索引。有一种说法是:“掌握了索引就等于掌握了数据库”。暂且不去评判此说法是否真的准确,但索引的确关系着数据库的读写性能。需要对数据库的索引原理做到足够的了解才能更好的使用各种数据库。通常来说,Mysql、Oracle、Mongodb这些都是使用的B树作为索引,是考虑到传统硬盘的特点后兼顾了读写性能以及范围查找需求的选择,而Hbase用得LSM则是为了提高写性能对读性能做了牺牲。
搜索引擎
搜索引擎也是后端应用中一个很关键的组件,尤其是对内容类、电商类的应用,通过关键词、关键字搜索内容、商品是一个很常见的用户场景。比较成熟的开源搜索引擎有Solr和Elasticsearch,很多中小型互联网公司搜索引擎都是基于这两个开源系统搭建的。它们都是基于Lucence来实现的,不同之处主要在于termIndex的存储、分布式架构的支持等等。
对于搜索引擎的使用,从系统熟悉、服务搭建、功能定制,需要花费较长时间。在这个过程中,需要注意以下问题:
- 搜索引擎与公司现有数据系统的集成。现有的持久化、供搜索的数据的载体是什么, 如何让搜索引擎在全量和增量建索引过程中无缝集成原来的数据载体,才能发挥搜索引擎自身的实时性, 水平扩展性(性能与容量和机器数量成正比)等优势。
- 和数据库一样,对搜索引擎的索引机制也需要做到深入的了解。
更为详细的对于搜索引擎的工程化实践可以参考有赞工程师的这篇文章:有赞搜索引擎实践(工程篇)
另外,搜索引擎还可以用在数据的多维分析上,就是GrowingIO、MixPanel中的可以任意维度查询数据报表的功能。当然,druid也许是一个更好的实现多维分析的方案,官方也有其与es的比较:http://druid.io/docs/latest/comparisons/druid-vs-elasticsearch.html。
消息队列
软件的组织结构,从开始的面向组件到SOA、SAAS是一个逐渐演变的过程。而到了今天微服务盛行的时代,你都不好意思说自己的系统只是单一的一个系统而没有解耦成一个个service。当然,小的系统的确没有拆分的必要性,但一个复杂的系统拆成一个个service做微服务架构确实是不得不做的事情。
那么问题就来了,service之间的通信如何来做呢?使用什么协议?通过什么方式调用?都是需要考虑的问题。
先抛开协议不谈,service之间的调用方式可以分为同步调用以及异步调用。同步调用的方式无需多说,那么异步调用是怎么进行的呢?一种很常见的方式就是使用消息队列,调用方把请求放到队列中即可返回,然后等待服务提供方去队列中去获取请求进行处理,然后把结果返回给调用方即可(可以通过回调)。
异步调用就是消息中间件一个非常常见的应用场景。此外,消息队列的应用场景还有以下:
- 解耦:一个事务,只关心核心的流程,需要依赖其他系统但不那么重要的事情,有通知即可,无须等待结果。
- 最终一致性:指的是两个系统的状态保持一致,要么都成功,要么都失败,可以有一定的延迟,只要最终达到一致性即可。
- 广播:这是消息队列最基本的功能。生产者只需要发布消息,无须关心有哪些订阅者来消费消息。
- 错峰与流控:当上下游系统处理能力不同的时候就需要类似消息队列的方式做为缓冲区来隔开两个系统。
目前主流的消息队列软件,主要有以下几种:
- ActiveMQ:Java中最为简单的消息队列,是对JMS的实现,没有规定消息的顺序、安全、重发等特性。
- RabbitMQ:是对AMQP协议的实现,对于消息的顺序性、安全、重发等都做了很好的支持。比较适合不允许数据丢失、有事务需求的业务场景下的消息传输。
- Kafka:是基于Log的消息队列,底层依赖于文件的顺序读取,是append-only的。适合对数据丢失不敏感、强调性能的一些海量日志传输场景中。是最近几年大数据领域很火的一个技术。
- ZeroMQ:是一个网络编程的Pattern库,将常见的网络请求形式(分组管理,链接管理,发布订阅等)模式化、组件化,简而言之socket之上、MQ之下。对于MQ来说,网络传输只是它的一部分,更多需要处理的是消息存储、路由、Broker服务发现和查找、事务、消费模式(ack、重投等)、集群服务等。
文件存储
不管是业务应用、依赖的后端服务还是其他的各种服务,最终还是要依赖于底层文件存储的。通常来说,文件存储需要满足的特性有:可靠性、容灾性、稳定性,即要保证存储的数据不会轻易丢失,即使发生故障也能够有回滚方案,也要保证高可用率。在底层可以采用传统的RAID作为解决方案,再上一层,目前hadoop的hdfs则是最为普遍的分布式文件存储方案,当然还有NFS、Samba这种共享文件系统也提供了简单的分布式存储的特性。
此外,如果文件存储确实成为了应用的瓶颈或者必须提高文件存储的性能从而提升整个系统的性能时,那么最为直接和简单的做法就是抛弃传统机械硬盘,用SSD硬盘替代。像现在很多公司在解决业务性能问题的时候,最终的关键点往往就是SSD。这也是用钱换取时间和人力成本最直接和最有效的方式。在数据库部分描述的SSDB就是对LevelDB封装之后,利用SSDB的特性的一种高性能KV数据库。
至于HDFS,如果要使用上面的数据,是需要通过hadoop的。类似xx on yarn的一些技术就是将非hadoop技术跑在hdfs上的解决方案(当然也是为了使用MR)。
统一认证中心
统一认证中心,主要是对app用户、内部用户、app等的认证服务,包括
- 用户的注册、登录验证、token鉴权
- 内部信息系统用户的管理和登录鉴权
- App的管理,包括app的secret生成,app信息的验证(如验证接口签名)等。
之所以需要统一认证中心,就是为了能够集中对这些所有app都会用到的信息进行管理,也给所有应用提供统一的认证服务。尤其是在有很多业务需要共享用户数据的时候,构建一个统一认证中心是非常必要的。此外,通过统一认证中心构建移动app的单点登录也是水到渠成的事情(模仿web的机制,将认证后的信息加密存储到本地磁盘中供多个app使用)。
单点登录系统
目前很多大的在线web网站都是有单点登录系统的,通俗的来说就是只需要一次用户登录,就能够进入多个业务应用(权限可以不相同),非常方便用户的操作。而在移动互联网公司中,内部的各种管理、信息系统同样也需要单点登录系统。目前,比较成熟的、用的最多的单点登录系统应该是耶鲁大学开源的CAS, 可以基于https://github.com/apereo/cas/tree/master/cas-server-webapp来定制开发的。此外,国人开源的kisso的这个也不错。基本上,单点登录的原理都类似下图所示:
统一配置中心
在Java后端应用中,一种读写配置比较通用的方式就是将配置文件写在propeties、yaml、HCON文件中,修改的时候只需要更新文件重新部署即可,可以做到不牵扯代码层面改动的目的。统一配置中心,则是基于这种方式之上的统一对所有业务或者基础后端服务的相关配置文件进行管理的统一服务, 具有以下特性:
- 能够在线动态修改配置文件并生效
- 配置文件可以区分环境(开发、测试、生产等)
- 使用方便: 在java中可以通过注解、xml配置的方式引入相关配置
disconf是可以在生产环境使用的一个方案,也可能根据自己的需求开发自己的配置中心(可以选择zookeeper作为配置存储)。
服务治理框架
对于外部API调用或者客户端对后端api的访问,可以使用http协议或者说restful(当然也可以直接通过最原始的socket来调用)。但对于内部服务间的调用,一般都是通过RPC机制来调用的。目前主流的RPC协议有:
- RMI
- Hessian
- Thrift
- Dubbo
这些RPC协议各有优劣点,需要针对业务需求做出相应的最好的选择。
这样,当你的系统服务在逐渐增多,RPC调用链越来越复杂,很多情况下,需要不停的更新文档来维护这些调用关系。一个对这些服务进行管理的框架可以大大节省因此带来的繁琐的人力工作。
传统的ESB(企业服务总线)本质就是一个服务治理方案,但esb作为一种proxy的角色存在于client和server之间,所有请求都需要经过esb,使得esb很容易成为性能瓶颈。因此,基于传统的esb,更好的一种设计如下图所示:
如图,以配置中心为枢纽,调用关系只存在于client和提供服务的server之间,就避免了传统esb的性能瓶颈问题。对于这种设计,esb应该支持的特性如下:
- 服务提供方的注册、管理
- 服务消费者的注册、管理
- 服务的版本管理、负载均衡、流量控制、服务降级等
- 服务的容错、熔断等
阿里开源的dubbo则对以上做了很好的实现,也是目前很多公司都在使用的方案。但由于某些原因,dubbo现已不再维护,推荐大家使用当当后来维护的dubbox。
统一调度中心
在很多业务中,定时调度是一个非常普遍的场景,比如定时去抓取数据、定时刷新订单的状态等。通常的做法就是针对各自的业务依赖Linux的cron机制或者java中的quartz。统一调度中心则是对所有的调度任务进行管理,这样能够统一对调度集群进行调优、扩展、任务管理等。azkaban和oozie是hadoop的流式工作管理引擎,也可以作为统一调度中心来使用。当然,你也可以使用cron或者quartz来实现自己的统一调度中心。
- 根据cron表达式调度任务
- 动态修改、停止、删除任务
- 支持任务工作流:比如一个任务完成之后再执行下一个任务
- 任务支持脚本、代码、url等多种形式
- 任务执行的日志记录、故障报警
对于Java的quartz这里需要说明一下:这个quartz需要和spring quartz区分,后者是spring对quartz框架的简单实现也是目前使用的最多的一种调度方式。但其并没有做高可用集群的支持。而quartz虽然有集群的支持,但是配置起来非常复杂。现在很多方案都是使用zookeeper来实现spring quartz集群的。这里有一个国人开源的uncode-shcedule对此实现的还不错,可以根据自己的业务需求做二次开发。此外,当当开源的elastic-job则在此之上又加入了弹性资源利用等更为强大的功能。
统一日志服务
日志是开发过程必不可少的东西。有时候,打印日志的时机、技巧是很能体现出工程师编码水平的。毕竟,日志是线上服务能够定位、排查异常最为直接的信息。
通常的,将日志分散在各个业务中非常不方便对问题的管理和排查。统一日志服务则使用单独的日志服务器记录日志,各个业务通过统一的日志框架将日志输出到日志服务器上。
可以通过实现log4j后者logback的appender来实现统一日志框架,然后通过RPC调用将日志打印到日志服务器上。
数据基础设施
数据是最近几年非常火的一个领域。从《精益数据分析》到《增长黑客》,都是在强调数据的非凡作用。很多公司也都在通过数据推动产品设计、市场运营、研发等。详细的可见之前的一篇《数据杂谈》,对数据相关的东西做过一些总结。这里需要说明的一点是,只有当你的数据规模真的到了单机无法处理的规模才应该上大数据相关技术,千万不要为了大数据而大数据。很多情况下使用单机程序+mysql就能解决的问题非得上hadoop即浪费时间又浪费人力。
这里需要补充一点的是,对于很多公司,尤其是离线业务并没有那么密集的公司,在很多情况下大数据集群的资源是被浪费的。因此诞生了xx on yarn一系列技术让非hadoop系的技术可以利用大数据集群的资源,能够大大提高资源的利用率,如Docker on yarn(Hulu的VoidBox)。
数据高速公路
接着上面讲的统一日志服务,其输出的日志最终是变成数据到数据高速公路上供后续的数据处理程序消费的。这中间的过程包括日志的收集、传输。
-
收集:统一日志服务将日志打印在日志服务上之后,需要日志收集机制将其集中起来。目前,常见的日志收集方案有:scribe、Chukwa、Kakfa和Flume。对比如下图所示:
-
传输:通过消息队列将数据传输到数据处理服务中。对于日志来说,通常选择kafka这种消息队列即可。
此外,这里还有一个关键的技术就是数据库和数据仓库间的数据同步问题,即将需要分析的数据从数据库中同步到诸如hive这种数据仓库时使用的方案。比较简单的、用的也比较多的可以使用sqoop进行基于时间戳的数据同步,此外,阿里开源的canal实现了基于binlog增量同步,更加适合通用的同步场景,但是基于canal你还是需要做不少的业务开发工作的。推荐另一款国人开源的MySQL-Binlog,原理和canal类似,默认提供了任务的后台管理功能,只需要实现接收到binlog后的处理逻辑即可。
离线数据分析
离线数据分析是可以有延迟的,一般针对是非实时需求的数据分析工作,产生的也是T-1的报表。目前最常用的离线数据分析技术除了hadoop还有spark。相比hadoop,spark性能上有很大优势,当然对硬件资源要求也高。
对于hadoop,传统的MR编写很复杂,也不利于维护,可以选择使用hive来用sql替代编写mr,但是前提务必要对hive的原理做到了解。可以参见美团的这篇博文来学习:Hive SQL的编译过程。而对于spark,也有类似hive的spark sql。
此外,对于离线数据分析,还有一个很关键的就是数据倾斜问题。所谓数据倾斜指的是region数据分布不均,造成有的结点负载很低,而有些却负载很高,从而影响整体的性能。因此,处理好数据倾斜问题对于数据处理是很关键的。对于hive的数据倾斜,可见:hive大数据倾斜总结。对于spark的倾斜问题,可见:Spark性能优化指南——高级篇。
实时数据分析
相对于离线数据分析,实时数据分析也叫在线数据分析,针对的是对数据有实时要求的业务场景,如广告结算、订单结算等。目前,比较成熟的实时技术有storm和spark streaming。相比起storm,spark streaming其实本质上还是基于批量计算的。如果是对延迟很敏感的场景,还是应该使用storm。
对于实时数据分析,需要注意的就是实时数据处理结果写入存储的时候,要考虑并发的问题,虽然对于storm的bolt程序来说不会有并发的问题,但是写入的存储介质是会面临多任务同时读写的。通常采用的方案就是采用时间窗口的方式对数据做缓冲后批量写入。
此外,实时数据处理一般情况下都是基于增量处理的,相对于离线来说并非可靠的,一旦出现故障(如集群崩溃)或者数据处理失败,是很难对数据恢复或者修复异常数据的。因此结合离线+实时是目前最普遍采用的数据处理方案。Lambda架构就是一个结合离线和实时数据处理的架构方案。
数据即席分析
离线和实时数据分析产生的一些报表是给数据分析师、产品经理参考使用的,但是很多情况下,线上的程序并不能满足这些需求方的需求。这时候就需要需求方自己对数据仓库进行查询统计。针对这些需求方,SQL上手容易、易描述等特点决定了其可能是一个最为合适的方式。因此提供一个SQL的即席查询工具能够大大提高数据分析师、产品经理的工作效率。Presto、Impala、Hive都是这种工具。如果想进一步提供给需求方更加直观的ui操作界面,可以搭建内部的Hue。
故障监控
对于面向用户的线上服务,发生故障是一件很严重的事情。因此,做好线上服务的故障检测告警是一件非常重要的事情。可以将故障监控分为以下两个层面的监控:
- 系统监控:主要指的对主机的带宽、cpu、内存、硬盘、io等硬件资源的监控。这可以使用开源的nagios、cacti等开源软件进行监控。目前,市面上也有很多第三方服务能够提供对于主机资源的监控,如监控宝等。对于分布式服务集群(如hadoop、storm、kafka、flume等集群)的监控则可以使用ganglia。此外,小米开源的OpenFalcon也很不错,涵盖了系统监控、JVM监控等,也支持自定义的监控机制。
- 业务监控:是在主机资源层面以上的监控,比如app的pv、uv数据异常、交易失败等。需要业务中加入相关的监控代码,比如在异常抛出的地方,加一段日志记录。
监控还有一个关键的步骤就是告警。告警的方式有很多种:邮件、im、短信等。考虑到故障的重要性不同、告警的合理性、便于定位问题等因素,有以下建议:
- 告警日志要记录发生故障的机器id,尤其是在集群服务中,如果没有记录机器id,那么对于后续的问题定位会很困难。
- 要对告警做聚合,不要每一个故障都单独进行告警,这样会对工程师造成极大的困扰。
- 要对告警做等级划分,不能对所有告警都做同样的优先级处理。
- 使用微信做为告警软件,能够在节省短信成本的情况下,保证告警的到达率。
故障告警之后,那么最最关键的就是应对了。对于创业公司来说,24小时待命是必备的素质,当遇到告警的时候,需要尽快对故障做出反应,找到问题所在,并能在可控时间内解决问题。对于故障问题的排查,基本上都是依赖于日志的。只要日志打的合理,一般情况下是能够很快定位到问题所在的,但是如果是分布式服务,并且日志数据量特别大的情况下,如何定位日志就成为了难题。这里有几个方案:
- 建立ELK(Elastic+Logstash+Kibana)日志集中分析平台,便于快速搜索、定位日志。对于ELK的介绍,可以见:使用Elasticsearch + Logstash + Kibana搭建日志集中分析平台实践
- 建立分布式请求追踪系统(也可以叫全链路监测系统),对于分布式系统尤其是微服务架构,能够极大的方便在海量调用中快速定位并收集单个异常请求信息,也能快速定位一条请求链路的性能瓶颈。唯品会的Mercury、阿里的鹰眼、新浪的WatchMan、Twitter开源的Zipkin基本都是基于Google的Dapper论文而来。此外,腾讯的染色日志机制本质上也是在链路追踪之上根据响应信息做了染色机制。Apache正在孵化中的HTrace则是针对大的分布式系统诸如hdfs文件系统、hbase存储引擎而设计的分布式追踪方案。这里需要提到的一点是,如果你的微服务实现使用了Spring cloud,那么Spring Cloud Sleuth则是最佳的分布式跟踪实现方案。
扩展
一. NetFlix
近几年Netflix开源了其内部很多的服务:https://github.com/Netflix,包括大数据、构建交付工具、通用运行时服务类库、数据持久化、安全等。里面有一些对应了上面所说的基础设施:
-
这是Netflix所有后端服务最前端的一道门,也就是我们上面说的Api网关, 主要包含了以下功能:
- 认证授权和安全:识别合法的外部请求,拒绝非法的。
- 监控:跟踪记录所有有意义的数据以便于给我们一个精确的产品视图。
- 动态路由:根据需要动态把请求路由到合适的后端服务上。
- 压力测试:渐进式的增加对集群的压力直到最大值。
- 限流:对每一种类型的请求都限定流量,拒绝超出的请求。
- 静态响应控制:对于某些请求直接在边缘返回而不转发到后端集群。
- 多区域弹性:在aws的多个region中进行请求路由。
-
是Netflix的服务注册发现服务,类似于dubbo的功能。包括负载均衡和容错。
-
hystrix是一个类库。基于命令模式,实现依赖服务的容错、降级、隔离等。在依赖多个第三方服务的时候非常有用。此外,还可以通过自定义实现dubbo的filter来给dubbo添加hystrix的特性支持。
此外,Netflix的这些开源组件统称做Netflix oss,提供了一整套分布式系统解决方案,涵盖了做分布式微服务需要的服务发现、服务容错、负载均衡、权限控制等。当然,如果你直接选用docker的话,那么K8s本身也提供了这些东西。
二. Spring Cloud
Spring cloud给我们构建分布式系统提供了一整套开发工具和框架,基本上也涵盖了本文讲述的各个组件,其子项目Spring Cloud Netflix则能够集成Netflix的各个组件。现在很多公司和团队都是基于Spring cloud这一套东西在做微服务实现的。不过,spring cloud包含很多子项目,想要吃透这些得花不小的成本。
-
Spring Cloud Config
统一配置中心,类似于前文说过的disconf,不过其配置文件时存储在版本管理系统如git、svn上的。其配置的实时在线更新则需要依赖Spring Cloud Bus。
-
Spring Cloud Security
提供了oauth2客户端的负载均衡以及认证header等安全服务,可以做为Api网关的实现。
-
Spring Cloud Consul/Zookeepr
服务统一发现、注册、配置服务。类似于dubbo。
-
Spring Cloud Bus
提供了服务之间通信的分布式消息事件总线,主要用来在集群中传播状态改变(如配置改动)。
-
Spring Cloud Sleuth
分布式跟踪系统, 能够追踪单次请求的链路轨迹以及耗时等信息。