- 测试环境:物理机2台,每台内存128G,CPU32核。机器1直接安装Centos6,机器2安装EXsi,再虚拟出4个虚拟机。机器之间千兆带宽。
- 测试场景:持续流数据到Ignite客户端,数据先缓存,再周期(1分钟)形成计算任务发布到Ignite平台进行业务处理,并将结果写入Ignite分布式缓存。分别创建2、4、8节点数的Ignite集群以测试水平扩展能力。
- 先把测试结果列一下,有数据才真实。
-
运行程序 平均耗时(s) 耗时比例
(和原程序相比)平均吞吐量(条/s) 吞吐量比例
(和原程序相比)原程序(1机器) 44.75 100.00% 7490.903759 100.00% Ignite并行计算,2实例(2机器) 26.63 59.50% 13974.03663 186.55% Ignite并行计算,4实例(4机器) 18.50 41.34% 18342.28861 244.86% Ignite并行计算,8实例(8机器) 10.63 23.74% 33336.30975 445.02% 现在看测试结果,水平计算扩展的效果已经展现,但不是非常显著,这里应该还有优化空间。------------------------------------------------------------干货分割线---------------------------------------------------
-
测试场景是有真实项目对应的,该项目使用java开发的单进程应用,并且结合Redis存储大规模内存数据用于计算,目前并没有达到性能瓶颈。没有远虑必有近忧,这不希望寻找分布式解决方案,且能够较容易进行架构迁移。 Ignite既支持计算网格(分布式计算),又支持数据网格(分布式缓存)。没有理由不试一试。
-
第一个方案,直接把原程序的全局类数据结构转换为Ignite的Cache,使用把原程序的计算模块整体抽取放到Ignite集群进行分布式计算:
compute.run(new MatchingJob(m_mapReadyDataPara));
这个架构的转换非常容易,很快就从单进程应用迁移到Ignite分布式框架下了,但是,执行的效果非常糟糕,即使多台机器组成的Ignite集群,执行效率也不及原单进程应用。尝试了调整Ignite的线程池: publicThreadPoolSize 、 systemThreadPoolSize ,也没有起太大作用。 卡在这个方案很久,才重新深入理解业务和它的数据模型,又进行了一系列的方案改进:
- 批量处理输入数据,可配置每个job处理GPS点数量。 结果:较方案1,处理效率提升明显。 但是水平扩展的效果依然不好。
- 在发现resetData方法严重耗时后,重构数据reset方法(即,清理过期数据),使用广播消息机制,本地化处理过期数据。结果:resetData部分效率提升显著。原因:并行化,并避免异地数据迁移。
compute.broadcast( new IgniteRunnable() { @Override public void run() { Iterator<Cache.Entry<String, Map<Long, List<baselink>>>> it = mapMatchingData.localEntries(CachePeekMode.ALL).iterator(); ... }} )
-
匹配计算完成后写计算结果耗时巨大,进行重构,通过计算和数据并置,让写结果的数据写本地,避免网络传输。 按车辆进行并置,一个car的数据作为1个job计算。 结果:较之前方案,效率提升显著, 但水平扩展效果不佳。原因:job粒度过细。
-
先根据carID的并置关系,预先分类在同一Ignite实例上计算的car数据,再根据实例CPU核数启动job数。 结果:不论和原程序比较,还是水平扩展,效果均达预期。
UUID nodeID = ignite.affinity("MapMatchingData").mapKeyToNode(car_key).id();
总结: Ignite这样一个功能全面的内存框架,由java语言开发,部署非常简单,可以说开箱即用。尤其对于java开发人员,简直没有学习成本,借用官方的说法,Spark用于大数据处理,而Ignite则用于快数据处理。后面,还会写一写Ignite在支持SQL方面的表现,敬请关注。
转载于:https://my.oschina.net/u/2269414/blog/778725