本文旨在通过对某省高速公路联网收费运营管理平台的架构设计过程进行案例分析,描述架构设计的决策过程。
1.业务背景
某省的高速公路分为近百个路段,不同的路段归属不同的公司建设与运营,造成了车辆在跨越不同路段时,需要停经收费站缴费换卡,降低了高速公路的车辆通行效率。
随着信息化技术的发展,将全省的高速公路联网收费的条件成熟,改造后车辆在高速公路上行驶,在出发地上高速时领卡,到目的地出高速时全程只需缴费一次。随着信息化推进,未来车辆在全国范围内高速公路通行,均只需缴费一次。
为了适应全省联网收费系统改造,迫切需要能集中对所有路段内的收费交易流水,高清卡口流水与图像进行检索,对交易流水进行重新拆分,对全省高速上车辆进行视频监控与防逃费稽查。与以前最大的改进是数据由分散在各个路段系统,集中到省中心,并提供信息共享查询与统计分析报表。
2.关键需求
根据业务需求,各个层级系统间数据流向如下图所示:
1) 全省路网约有100个路段中心;每个路段中心对应几个到几十个不等的收费站,每个收费站平均有4个入口车道,2个出口车道,合计全省约有2000个入口车道,1000个出口车道。
2) 车辆驶入收费站上高速时,产生交易入口流水和高清卡口信息(包含图像);车辆从收费站出口缴费时产生交易出口流水信息。各个车道中设备的状态信息,定时发送到路段中心。
3) 各路段中心的数据需要及时汇总到省运营管理平台。车辆从收费站出口缴费时,收费站系统需要到省运营平台查询该车辆对应的入口流水数据,以及高清卡扣流水数据,必要时还需要调取高清卡扣图像进行肉眼比对。
4) 由省收费结算中心根据路段中心提供的交易流水进行费用拆分,并将拆分结果下发给省运营平台。省运营平台再将拆分结果下发给各个路段中心。
5) 省运营平台下发基础费率、设备控制指令、运营参数信息到路段中心。路段中心再逐级下发到收费站。
对业务需求作进一步了解分析,发现系统具有如下特点:
1) 绝大多数场景对实时性要求不高。只有查询入口交易流水需要在1秒内响应。
2) 系统的并发访问量小。普通用户数量小于2000。应用接口查询并发数小于500。(在不考虑移动APP公众应用的场景下)
3)复杂的业务逻辑少,系统需求集中于对数据进行查询分析与信息管理。只有交易流水拆分规则较为复杂。
4) 查询入口交易流水与查询高清卡口流水要求7 * 24高可用性,否则收费站程序可能会无法对车辆进行收费计算处理,进行人工干预会大大降低收费效率,易造成交通堵塞。
5)交易流水与高清卡口流水数据量大,日均在3千万和2千万以上。业务管理上要求对交易流水存档一年时间以上,高清卡口流水3个月以上。每张高清卡口图片大小为200K,集中存储在省平台将是巨大的容量。
3.重大决策
在系统设计过程中作重大决策,必须在分析清楚主要功能需求与质量性需求的基础上,结合已有系统现状,可支出经济成本,技术团队素质现状,网络环境,业务管理约束,工期要求等因素作出平衡性取舍。
在该系统的实施过程中,最大的现实约束是工期赶,牵涉到的工程实施方多,开发测试时间被压缩到2个月内,必须把握好进度不能影响其它单位联调测试。在架构设计时必须要结合技术团队现有的知识技能储备,新技术预研需要成本,同时还会引入风险,必须谨慎。
3.1高性能方面
3.1.1按业务划分不同数据库,对大表分区
由于整个系统的业务功能非常多,划分为15个以上的子系统。对公共的用户、组织机构、道路、收费站、基础字典等信息放在一个数据库(Schema)中,对其它业务系统不能公用的分别划分一个数据库。对于重点业务功能专门划分一个数据库。为每一个业务系统分配一个独立的数据库用户,便于使用与管理。
充分利用Oracle数据库其分区表机制,对于入口流水,出口流水,高清卡口流水等大表按照日期(例如按照3天,10天)进行分区,确保每个分区内数据量在1亿条以内。这样在同一个分区内单表查询时,只要数据库索引建立合理,服务器资源充足,Oracle能保障在1秒内响应。同时在对历史记录做清理时可直接删除分区文件,大数据量删除性能得到很大提升。
3.1.2使用内存数据库提升查询性能
虽然已经对数据库作了优化,但由于车辆在收费出口缴费时,收费软件均要查询入口流水以及高清流水。如果并发查询数上升,数据库在处理并发查询时就不一定能保证在1秒内响应了。该功能的响应性能,是用户的痛点,直接决定了该项目被不被认可。
因此必须考虑引入NoSQL数据库,将最近3天的流水数据加载到内存中,处理来自收费站的并发查询请求,确保查询在10~100毫秒内完成。
在我们的业务场景中,数据已经持久化在Oracle中,引入分布式缓存或NoSQL产品只是为了直接从内存检索数据提升性能。于是,对几款开源的分布式缓存和内存数据库产品做了大致了解。
Memcached适合做分布式缓存,但它本身不提供检索查询功能。它将数据全部放在内存中,不实现文件持久化机制。同时在集群部署时,节点之间是不作通信的,一旦某个节点宕机则该节点上数据全部丢失。要实现查询检索功能,应用开发的工作量会比较多,不适合我们的场景。
Redis与MongoDB都提供了持久化到文件的机制,Redis的查询性能优于MongoDB。但MongoDB在查询API上实现了很多类似SQL语句查询的便利功能,同时在集群部署时它能自动实现数据分片,灾难转移,应用开发的工作量也要小一些。两相比较,个人倾向于引入MongoDB的。但是苦于工期短,没有人力与时间做深入的预研,如果研究的不够深入,又会增加风险。
后来Vmware公司推荐一款叫GemFire的内存数据库产品,称在12306网站应用取得了不错的效果。于是他们的售前就过来给我们洗脑,演示了一些成功案例,让我们对这个产品增加了很大信心。让我印象最深刻的是这个产品定位为内存计算平台解决方案,可以直接在数据节点进行计算(类似于在数据库中预先定义好函数,在应用中进行调用),再将计算的结果合并后返回给应用,这就是分布式计算的意思了。
由于商业产品可以提供可靠性保障,又能节约我们的人力投入,项目预算也足够,何乐而不为。后面的实施情况证明,这个产品性能与稳定性还是不错的,能将查询时间降低到几十毫秒级,当然也比较耗费内存与CPU(3台服务器,每台10G内存起)。
3.1.3采购高配置硬件网络设备
互联网应用因为服务器规模庞大,为了节约成本必须去IOE化,大量采用廉价服务器与开源软件。企业应用因为规模小,相比之下,服务器资源没有那么大的规模,高配置的硬件成本企业还能负担。该项目中采用IBM服务器,EMC存储,思科的交换机,F5硬负载均衡器。
这种通过硬件来提升性能的方式,在成本允许的条件下,是可以节省力气的。采用了专用存储,其自身已经做了冗余机制,提供了灾备的解决方案。应用层面就不用再去考虑数据灾备的问题。硬盘坏了,就换个新硬盘插上去就好了,因为做了RAID10/5已经保证了数据不丢失。若等研究完几个月的HDFS,终于敢用到项目里面,项目工期已经结束了。
3.2可靠性与高可用方面
3.2.1采用商用中间件提高可靠性
采用Oracle数据库RAC集群,提供了数据库层面的高可用性。
采用IBM Websphere来部署重要业务系统,例如接口查询子系统。它自身提供了软负载均衡和集群功能,可通过Web界面来对应用服务器集中管理与监控。这样应用层的高可用也得到了解决。(在我们的重要业务场景中,接口查询是无状态的,会话不需要进行复制,也就是采用粘连性会话即可)。
3.2.2采用消息中间件进行通信
业务场景中,省运营平台还需要下发控制指令,运营参数等信息到各个路段。同时路段上传的大量流水数据,如果先缓冲到消息服务器中,则拆分子系统可以直接从消息服务器读取数据,多线程处理或者单线程部署多份,部署模式非常灵活。同时引入消息中间件还可以降低各个子系统的耦合度。
开源的有ActiveMQ、RabbitMQ,商用的也了解了下IBM MQ。比较诧异的是IBM MQ的并发性能测试指标非常差,比前两者都弱。
最终我们采用了开源的RabbitMQ,看重的是它的并发性能和对于高可用以及灾难转移的支持。目前将服务端部署在省运营平台,路段只使用客户端进行连接。这样也是考虑到部署与运维成本,路段的各合作方技术水平有限,分散部署不利于故障诊断。
3.3扩展性方面
3.3.1划分为多个子系统解耦
一般大点的系统都有必要划分成多个子系统,易于团队并行开发,部署升级也快。核心业务系统,我们可以集群多部署一些实例,多给点硬件资源。不重要的业务系统,配置管理类的,甚至单实例部署,直接部署到Tomcat上。同时有些后台服务,本身就是一个Java进程,与数据库相关的,设计时就限定了只能部署一个实例。
子系统和子系统之间,可以通过消息服务器来解耦,一个子系统宕机,不会影响另外的子系统运行。
在整个系统的实施过程中,我们引入了Portal门户、统一用户管理、单点登录来进行不同Web之间页面级的集成。
3.4运维方面
3.4.1基础设施采用虚拟化技术便于运维管理
借鉴公司私有云建设的成功经验(基于Vmware的产品组件),我们认为可以通过对服务器进行虚拟化来充分利用资源,降低成本,也便于集中进行运维管理。
在实施过程中,我们将3台豪华配置服务器虚拟出近百台服务器,同时为Oracle做集群留下了2台较高配置的服务器。
如果不进行虚拟化,可能需要更多的服务器,同时多个应用部署在一台服务器上,运维监控起来非常麻烦。虚拟化后,可以根据业务场景动态对服务器资源进行分配,这也是云计算机的特点。
值得注意的是,在虚拟机上部署集群应用时,要确保把节点分布到不同的物理机器上,这样才能真正保证高可用性。
3.5综合考量方面
3.5.1高清图片不集中存储到省平台
前文提到,高清图片数据量大,若集中存储到省平台耗费存储容量。用专用存储来存这些图片,有些浪费。若将图像从路段传输到省平台,也将浪费大量的通信带宽。
实际业务场景中,收费站只在某些特定场景才调用图像查询接口,访问量并不大。也就是说只有非常少的图片真正需要被读取。于是我们定义了HTTP接口规范,由路段提供服务端实现,省平台负责调用。因为HTTP接口简单易于实现,给各路段增加的工作量很小。
也就是说,这里我们牺牲了图像的查询性能,节约了带宽与存储。
3.5.2直接从路段中间数据库抽取数据
已有系统的接口方式都是通过中间数据库来实现的,考虑到引入新的接口方式,路段太多,对合作公司的技术能力与集成工作量都将大大增加。于是我们遵循了以前的方式,定义了中间表规范,由各个路段负责提供一套中间数据库,往其中写数据。
考虑到现有系统各种类型的数据库都存在,于是我们采用了开源数据抽取工具Kettle来进行数据迁移。同时数据库抽取的数据量较大,各种不同的业务表结构,如果开发接口程序去实现,则工作量会很大,稳定性也是难以保证。
为了保证数据抽取的效率,每10个路段分为一组,部署一套Kettle抽取程序去做搬迁。
也就是说,这里我们考虑到现实技术条件,用最简单适用的方式去实现业务功能。实际实施下来,这种数据抽取的方式为项目节省了很多时间。
3.5.3子系统各自缓存用户信息
用户与组织机构信息是存储在公用数据库中的。但是用户的角色、可访问的资源、以及资源URL等信息在各个子系统中是不一样的。为了提升性能,各个子系统将用户和组织机构信息缓存到内存中,对于授权与访问控制这块的功能,通过代码级别(class文件或者Jar包)的重用实现复用。
实际上,如果子系统数量越来越多,是可以把用户认证与授权以及查询用户信息这块的功能服务化,包装成HTTP REST服务或RPC调用服务。考虑到项目的工期,以及开发的成本,我们并没有这样为了技术而技术,直接通过数据库访问作接口和缓存用户信息来提升性能。
注意这里对用户信息作缓存时一定要设置时效性,例如5分钟。
4.整体架构
下章节表述的是系统的整体架构,用来说明各个子系统及业务组件的划分与所处的层次,还未细化到足以指导开发工作的程度。
4.1逻辑架构
1) 基础设施层为服务器、交换机、路由器、防火墙、安全防护、磁带备份等硬件网络设备。还包括虚拟化软件与操作系统。
2)中间件层为商用的数据库、应用服务器、内存数据库以及开源的消息中间件、数据抽取软件。
3) 服务组件层为可复用的业务组件,二次开发框架。
4)业务应用层为各个业务子系统。
4.2组件依赖
1) Kettle将抽取到的流水数据存入数据库,同时写入到消息服务器RabbitMQ。
2)GemFire从流水数据库装载最近几天的流水数据到内存。
3)接口查询服务调用GemFire的客户端API查询入口流水。
4)交易流水拆分服务从消息服务器读取数据进行处理,将拆分结果写入业务数据库。
5) 各个Web子业务系统需要依赖CAS服务实现单点登录。
6)由统一用户管理系统集中管理用户与组织机构信息。
7)单点登录服务从用户数据库查询用户信息以实现集中认证。
8)Portal门户将各个Web子业务系统集成起来。
4.3物理部署
4.4子系统设计
由于子系统设计会涉及到公司具体的业务,这里不做进一步描述。设计时只要按照通用功能抽取,不同业务功能划分不同子系统原则即可。
通常子系统设计中需要描述清楚具体的功能模块设计,业务处理流程,开发包划分,领域模型,关键的类图,内部接口及外部接口的规范。
5.结语
玩过即时战略类电子游戏的朋友,都知道什么时候采矿,几个农民采一堆最大效率,什么时候造什么兵种来克制对方,什么时候侦查与扩张,作战时候多兵种如何摆阵配合才能发挥最佳优势。这里不妨作一下类比,架构设计就是实施战略的过程,具体的技术是兵种武器,模式理论就是各种战术。