任培花,李健浩
(山西大同大学 计算机与网络工程学院,山西 大同 037009)
车辆稽查[1]是指,通过道路执法系统对过往车辆信息的实时采集,并将拍摄日志实时传回交警指挥中心的服务器进行存储,然后对海量日志数据进行处理,从中找出嫌疑车辆。
传统的车辆稽查方式应对日益上涨的交通执法压力显得力不从心,尤其面对海量信息,采用人工或半人工的稽查处理方式不仅费时费力,而且滞后严重,这些情况说明传统的车辆稽查方式显然是不合理的。因此,在海量车辆信息和日志信息的大背景下,如何降低车辆违法行为的稽查压力,是当前急需解决的问题。
关于车辆稽查系统的研究国内起步较晚,是随着汽车保有量的增长,车辆各种违法行为(违法行驶、遮挡拍照、套牌等)增多的现状推动下,对车辆稽查系统问题的研究才逐渐开始的。很多学者都是借助物联网进行采集,然后直接将数据保存、后期分析的方法开展研究。文中减少了对物联网技术的依赖,以稽查数据研究为重点,借助大数据技术[2],提出一种交通运行数据模拟方案。在具体技术实现方面,在前人研究地基础上,进行了一系列创新。如江日念等提出将Maven应用到Java项目中[3],文中借鉴该方法将Maven引入仿真系统进行横向和纵向拆分,提高代码的鲁棒性及团队开发下的易管理性;邱祝文从系统应用角度出发,提出基于Redis的分布式缓存系统架构[4],在该研究的基础上合理使用Redis集群,完成摄像头拍摄模拟模块的设计,并结合Flume框架[5-7]保证分布式日志稳定可靠收集的需求;薛亚鹏提出“基于实时日志系统的海量日志服务平台设计与实现”[8],魏彬提出“基于分布式日志系统的数据云服务平台设计与实现”[9],均为日志收集提供思路,具体实现过程选用稳定可靠的Hadoop[10-14]框架完成日志收集。
根据高并发模拟[15-16]下计算机性能充分利用原则,以功能对计算机资源消耗程度为度量对项目进行横向和纵向的拆分,将系统划为4个模块:交通元素模拟模块、车辆模拟运行模块、摄像头拍摄模拟模块和日志收集模块,通过不同模块在不同服务器中的部署运行完成各自的功能。以下为系统各功能模块分析说明。
城市交通元素模拟模块是该系统的基础模块,将从需求分析、输入和输出进行阐述。
(1)需求分析。
该模块对城市交通运行中的“城市”、“交通卡口”、“摄像头”、“机动车”四个交通元素的模拟进行需求分析。表1是对该模块的需求划分。
表1 城市交通元素模拟模块
(2)输入和输出。
系统出于性能考虑对项目进行横向和纵向的拆分,拆分后该模块主要负责对系统中实体类的抽象,没有具体的数据输入和输出。
车辆模拟运行模块是系统的核心模块之一,将从需求分析、输入和输出两个角度展开阐述。
(1)需求分析。
车辆模拟运行即对现实生活中城市道路上20万辆机动车同时运行过程的模拟。系统中,该模块需要对现实城市中道路中的车辆对象进行模拟,合理地将20万辆车分布到所有街道中,并对车辆在道路中的实时随机行驶及进过卡口这一事件进行模拟,同时保证20万级别的超高并发度,实现对现实生活中20万辆车在城市道路中并发行驶的模拟。表2为车辆模拟运行模块的需求划分。
表2 车辆模拟运行模块需求划分
(2)输入和输出
该模块以20万辆机动车基本信息、2 000个卡口基本信息为模块输入值,以携带有车辆悬挂牌照、车辆运行方向、经过卡口的位置等信息的海量级车辆行驶日志为输出值。
摄像头拍摄模拟模块是系统的核心模块之一,将从需求分析、输入和输出两个角度展开阐述。
(1)需求分析。
摄像头拍摄模拟即对现实生活中监控摄像头拍摄经过车辆的信息过程的模拟。在系统中,该模块负责对上层模块发来的短时间海量的车辆行驶日志进行处理,从车辆行驶日志中获取车辆所经过的卡口位置信息,最终获取所拍摄的摄像头信息并追加到车辆行驶日志中,成为摄像头拍摄日志,完成对现实生活中摄像头抓拍经过卡口的车辆牌照、抓拍时间、车辆行驶速度等拍摄过程的模拟。表3为摄像头拍摄模拟模块的需求划分。
表3 摄像头拍摄模拟模块需求划分
(2)输入和输出。
该模块以上层模块传来的携带有车辆悬挂牌照、车辆运行方向、经过卡口的位置等信息的海量级车辆行驶日志及Redis中相应的摄像头对象JSON数组为输入值,以携带有车辆悬挂牌照、车辆运行方向、拍摄地点、拍摄时间、卡口id、卡口位置信息、摄像头id、摄像头基本信息等要素的摄像头拍摄日志为输出值。
日志收集模块是系统的核心模块之一,将从需求分析、输入和输出两个角度展开阐述。
(1)需求分析。
该模块负责对上层模块(分布式集群)发来的海量级的日志数据进行高效、可靠的收集及在分布式文件系统中的合理可靠存储。表4为日志收集模块的需求划分。
表4 日志收集模拟需求划分
(2)输入和输出。
该模块以上层模块(分布式集群)传来的日志数据为输入值,以分布式文件系统中日志文件为输出值。
结合系统需求分析及项目架构设计对系统整体进行网络架构设计。系统网络架构如图1所示。
图1 车辆模拟运行及日志收集子系统网络架构
根据各个Maven子项目和各个模块的特点,将车辆模拟运行项目独立部署在一台服务器,使服务器性能得到最大发挥。由于摄像头拍摄模拟模块要抗击上层车辆模拟运行模块发来的海量级Http压力的特点,将摄像头拍摄模拟项目分别部署在三台服务器上,结合Ngnix服务器的使用,实现负载均衡的效果,抗击上层模块超高并发的http请求,保证摄像头拍摄模拟的性能。根据摄像头拍摄模拟模块高频率进行数据库IO操作的特点,使用Redis(内存级数据库)并结合哨兵及分片机制来保障摄像头拍摄模拟服务器集群高频率的数据库IO操作。Redis集群架构如图2所示。
图2 Redis集群架构
考虑到摄像头拍摄模拟模块分布式集群的特点,选用Flume并采取多级流动的结构保证摄像头拍摄模拟模块分布式日志稳定可靠收集的需求。在存储方面,考虑到该系统分布式集群输出海量级别的日志的特点,选用稳定可靠的Hadoop[8-12]集群(3台),并将日志文件存储在分布式文件系统上。Flume[14-16]及Hadoop架构设计如图3所示。
图3 Flume及Hadoop架构设计
针对该系统的关键功能模块给出具体的实现方式。
车辆模拟运行功能即对20万辆车在道路中实时运行进行模拟。涉及的类包括RunController类、CarTask类、HttpClientThread类、HttpThreadFactory类、SystemConfig类、Car类、HttpClientService类、Position类等。
(1)RunController类。
负责车辆模拟运行的准备工作,包括Runnable任务创建、线程池创建等。由于车辆模拟运行功能的性能完全取决于线程并发性能及合理的调度,针对高并发的要求,对线程、线程池、对象复用做了专门的设计。由于篇幅原因,这里只对高并发需求下的线程相关设计的实现进行描述。
(2)CarTask类。
实现自Runnable接口,为每一辆车创建一个CarTask对象,在run()方法中,执行对应的Car类对象的move方法,模拟车辆行驶一步。同时,根据move()方法返回的信息判断车辆当前步骤是否经过了卡口,如经过卡口,则使用当前线程中的HttpClient对象,向下层模块发送车辆行驶日志。日志格式如下:
①内容。
车辆id|真实车牌|悬挂车牌|当前拍摄时的坐标_X|当前拍摄时的坐标_Y|车辆上一步坐标_X|车辆上一步坐标_Y|拍摄时运行方向|车辆被拍摄的坐标_X|车辆被拍摄的坐标_Y|拍摄时间(long)|拍摄的摄像头id
②编码。
id|registId|hangId|x|y|prex|prey|dir|passedPortPosition_X|passedPortPosition_X|timestamp|sensorId
HttpClientThread类是为解决20万级的数量下HttpClient对象的复用而设计的自定义线程类。将HttpClient对象从CarTask中抽出,封装在了每个线程中,大大提高了对象的复用及项目稳定性。
ScheduledExecutorService属性(RunController类属性)是为了针对车辆需要循环执行行驶步骤的要求而选用Excutors框架中的定时调度线程池类对象。
addTasksToPoll()方法是将200 000个Runnable任务(CarTask)提交至线程池,线程池会定时将任务队列中的队列提交给线程执行,保证每个任务执行的间隔相同。
至此,每个车辆以一定的时间间隔执行一步模拟行驶,如果经过卡口,则向下层模块发送车里行驶日志。车辆运行模块涉及所有功能均已实现。
摄像头拍摄模拟模块具体包括卡口模拟、摄像头模拟、摄像头拍摄模拟三个功能。以下将从这三个功能展开介绍。
(1)卡口及摄像头模拟。
由于摄像头拍摄模拟模块采用分布式集群架构,摄像头拍摄模拟模块需要高效的根据上层模块传来行驶中车辆经过的卡口位置信息高效获取到对应的摄像头信息,因此选用内存级数据库Redis,搭配哨兵及分片机制搭建本功能。同时将100 000台摄像头中位置相同的摄像头合并为一个集合,以位置信息为键,集合为值的形式存储在内存级数据库Redis集群中。
(2)摄像头拍摄模拟。
摄像头拍摄模拟功能即摄像头拍摄模拟模块接收上层发送的车辆行驶日志,根据日志中的位置信息从Redis中获取相应的摄像头信息追加到日志中,成为拍摄日志,发送给下层模块。拍摄模拟功能时序如图4所示。
MessageHandlerController.handle(String carRunInfo)方法负责接收到来自上层模块传来的车辆行驶日志,提取出车辆经过的卡口位置信息,内部调用MessageHandlerServiceImpl类对象的getSensorIDByPocation(Position pos)方法根据位置信息获取拍摄的摄像头id。
MessageHandlerServiceImpl.getSensorIDByPocation(Position pos)负责摄像头拍摄模拟的具体实现,内部通过JedisCluster实现类与Redis进行相应的通信。
图4 拍摄模拟功能时序
jedisCluster.smembers()方法负责和Redis集群进行交互,借助本方法根据位置信息获取所有该位置上所有的摄像头的JSON格式字符串。从获取到的集合中随机选中一个摄像头对象JSON字符串,并提取摄像头id返回。
日志收集模块即对分布式的摄像头拍摄模拟集群产生的海量日志信息进行稳定可靠的收集,具体分为分布式日志收集和海量日志文件分布式存储两部分。分布式日志收集部分采用现阶段成熟的Flume分布式日志收集框架进行功能的搭建,采用多级流动的形式提高上层拍摄模块高并发情况下的收集性能。海量日志分布式存储部分采用成熟的Hadoop生态系统中HDFS分布式文件系统来实现。由于篇幅原因,对Hadoop集群搭建过程不做描述。
借助大数据技术,该系统的研究和实现,不仅实现了城市级别的车辆模拟运行和日志收集,可以为城市交通提供完整、相对贴合实际的运行场景,而且以此模拟系统为依据,可以方便车辆稽查,甚至调整道路交通运行方案。该系统为数据量较大的行业提供了一种解决问题的思路,用同样的方法可以为城市旅游、城市产业分布等进行模拟,进而调整旅游服务、产业设置等方案。