源码地址:
http://git.oschina.net/tinyframework/tiny
http://git.oschina.net/tinyframework/tiny/tree/master/framework/org.tinygroup.pc
前面和果粉们说了,要在200果粉齐了的时候就开源分布式计算框架,在偶死皮赖脸的乞讨声中,终于够200了,没啥好说滴,开吧。
先说说我当时做分布式计算框架的背景故事,本来开始时我是需要一个分布式计算框架的,但是读了fourinone之后,我觉得我吃不下或者说搞不定fourinone,因为我使用一个开源框架的前提是要么我可以吃定它,要么有许多人都在用,我可以相信它。很明显,许多人都在用fourinone还没有得到验证,因此我选择看看是否可以吃定它,但是看了三天之后,我放弃了,因此我选择自己做一个大家都能搞定的。
由于看了fourinone,感觉它的一些概念还是设计得不错的,在编写TinyPC时借鉴了一些fourinone的概念,但是代码是一行也没有拿来主义的,因此,还算不得抄袭吧。
手工仓库:用于存放各种分布式数据时的输入与输出内容。
它本身继承一Context接口,同时可以放入一个子仓库,子仓库中的内容也可以被读取到。
工人:用于对工作进行实际的运算。
考虑到工人的工作自由,可以在某种情况下拒绝接受工作,此时可以在acceptWork方法返回false,当然最大多数情况,直接返回true是正确的选择。工头:用于对工作进行分解及合并,同时带带着一帮工人来完成任务。
当然,工头有多种类型的工头,有的工头是只要是随便找一个人干活,此时就不需要分解与合并器了,此时它主要相当于负载均衡。有的工头则是需要好多个工人来一起协作共同快速的完成任务,这个时候就需要有分解合并器了。
职介所:提供一个空间,供工头、工人来注册,同时也用于完成雇主提交的任务。
可以看到职业介绍所确实复杂多了,但是幸运的是不需要自己来编写,只要使用就好了。
工作:每一项要完成的任务都可以是一个工作了,它带着一个输入手工仓库,在处理完毕之后,会有一个输出手工仓库。工作的是否需要进行序列化表明工作在没有处理完之前职业介绍所停止时工作是否会丢失。指明需要序列化的工作永远不会丢失,除非它被成功处理。
由于是异布执行,无法直接返回处理结果,因此如果需要进行回调,可以采用构建一个新工作的方式来处理返回结果。
可以看到工作的方法也不少,但是幸运的是,工作也不需要自己写,只要使用就好了。
工作分解器:有时候一个任务处理时间太长,这个时候就需要许多台机器一起协作来执行,这个时候工头可以利用一个工作分解器把大的工作,分解成小的工作。
工作合并器:工作被分解后,被许多个工人完成之后,需要把子任务的结果合并成一个结果后再返回,此时需要一个工作合并器。
工作分解合并器:它继承了工作分解和工作合并器,一般情况下,这两个都是配套使用的,因此,也可以通过它只写一个实现类来完成。
职业介绍所有两种,一种是本地职业介绍所,一种是远程职业介绍所。顾名思义,本地职业介绍所就是在当前计算机上的,远程职业介绍所用于连接到远程职业介绍所的。
工人、工头都可以加入到职业介绍所,所以加到本地或远程种业介绍所都是可以的。
在同一个职业介绍所中,具有同样类型的工人、工头和工作都存在的时候,工作就可以被安排下去执行。当然,有两种安排方式,一种是即时响应式的,具体的来说就是调用职业介绍所的doWork方法,如果此时有同样类型的工人和工头,那么就去执行,如果没有对应类型的工人或工头,则抛出无法执行的异常。这一种是同步方式处理工作。
另外一种方式是把工作添加职业介绍所即可,不关心工作是什么时候执行的,执业介绍所会对工作进行撮合,撮合并成功执行之后就把工作从工作列表之间清除。这一种是异步方式处理工作。
下面是一计算PI的值的分布式计算示例:
从上图看上,一共只有3个类,一个是工作分解合并器,一个是工人,一个是测试入口类。
public class PiWorker extends AbstractWorker { private static final long serialVersionUID = -1455631976934664413L; public PiWorker() throws RemoteException { super("pi"); } protected Warehouse doWork(Work work) throws RemoteException { long m = (Long) work.getInputWarehouse().get("start"); long n = (Long) work.getInputWarehouse().get("end"); double pi = 0.0d; for (double i = m; i < n; i++) { pi += Math.pow(-1, i + 1) / (2 * i - 1); } work.getInputWarehouse().put("pi", 4 * pi); return work.getInputWarehouse(); } }
public class PiSplitterCombiner implements WorkSplitterCombiner { private static final long serialVersionUID = -5365903331944759368L; public List<Warehouse> split(Work work, List<Worker> workers) throws RemoteException { List<Warehouse> list = new ArrayList<Warehouse>(); LongRangeSpliter longRangeSpliter = new LongRangeSpliter(); List<Range<Long>> rangePairList = longRangeSpliter.split((Long) work.getInputWarehouse().get("start"), (Long) work.getInputWarehouse().get("end"), workers.size()); for (Range range : rangePairList) { Warehouse subInputWarehouse = new WarehouseDefault(); subInputWarehouse.put("start", range.getStart()); subInputWarehouse.put("end", range.getEnd()); list.add(subInputWarehouse); } return list; } public Warehouse combine(List<Warehouse> warehouseList) throws RemoteException { Warehouse outputWarehouse = new WarehouseDefault(); double pi = 0d; for (Warehouse w : warehouseList) { pi += (Double) w.get("pi"); } outputWarehouse.put("pi", pi); return outputWarehouse; } }
public class Test { public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException { JobCenter jobCenter = new JobCenterLocal(); for (int i = 0; i < 10; i++) { jobCenter.registerWorker(new PiWorker()); } Foreman helloForeman = new ForemanSelectAllWorker("pi", new PiSplitterCombiner()); jobCenter.registerForeman(helloForeman); Warehouse inputWarehouse = new WarehouseDefault(); inputWarehouse.put("start", 1l); inputWarehouse.put("end", 1000000001l); Work work = new WorkDefault("pi", inputWarehouse); Warehouse outputWarehouse = jobCenter.doWork(work); System.out.println(String.format("pi:%s", outputWarehouse.get("pi"))); jobCenter.stop(); } }
首先建立一个本地职业介绍所
JobCenter jobCenter = new JobCenterLocal();然后添加10个会算PI的工人
for (int i = 0; i < 10; i++) { jobCenter.registerWorker(new PiWorker()); }
然后添加一个会做算PI任务的工头,当然,他还需要一个对求PI进行任务分解与合并的处理器
Foreman helloForeman = new ForemanSelectAllWorker("pi", new PiSplitterCombiner()); jobCenter.registerForeman(helloForeman);
然后添加一个手工仓库,里面放了两值,一个开始一个结束,这两个值算PI工人会用到
Warehouse inputWarehouse = new WarehouseDefault(); inputWarehouse.put("start", 1l); inputWarehouse.put("end", 1000000001l);
然后创建一个求PI的工作,并把刚才的手工仓库放入工作,然后让职业介绍所做这项工作,然后输出工作的输入仓库中的pi变量
Work work = new WorkDefault("pi", inputWarehouse); Warehouse outputWarehouse = jobCenter.doWork(work); System.out.println(String.format("pi:%s", outputWarehouse.get("pi")));
最后关闭职业介绍所,结束程序
jobCenter.stop();
算PI的算法:http://fourinone.iteye.com/blog/1829349
-0 [main] DEBUG - 开始注册对象:WorkQueue -2 [Thread-2] DEBUG - 开始检测已注册对象的可用性 -96 [main] DEBUG - 结束注册对象:WorkQueue -124 [main] DEBUG - 开始注册对象:Worker|pi|3c29555265304dff8e7500e6d2ff444e -130 [main] DEBUG - 结束注册对象:Worker|pi|3c29555265304dff8e7500e6d2ff444e -130 [main] DEBUG - 开始注册对象:Worker|pi|11ea430b537f4721a5a4a61b3b887cca -130 [main] DEBUG - 结束注册对象:Worker|pi|11ea430b537f4721a5a4a61b3b887cca -130 [main] DEBUG - 开始注册对象:Worker|pi|39e9d1a14cf544c29794e300ac9cbb91 -131 [main] DEBUG - 结束注册对象:Worker|pi|39e9d1a14cf544c29794e300ac9cbb91 -131 [main] DEBUG - 开始注册对象:Worker|pi|dd9d0c0b47dd4e18bbf31d7778e3fac1 -131 [main] DEBUG - 结束注册对象:Worker|pi|dd9d0c0b47dd4e18bbf31d7778e3fac1 -131 [main] DEBUG - 开始注册对象:Worker|pi|ef68753086374341b80acc66c1b6877a -132 [main] DEBUG - 结束注册对象:Worker|pi|ef68753086374341b80acc66c1b6877a -132 [main] DEBUG - 开始注册对象:Worker|pi|35b8df60692d455a96a4dc6dc855fef7 -132 [main] DEBUG - 结束注册对象:Worker|pi|35b8df60692d455a96a4dc6dc855fef7 -132 [main] DEBUG - 开始注册对象:Worker|pi|348e96c03b434158bd0ef026d33b35fb -133 [main] DEBUG - 结束注册对象:Worker|pi|348e96c03b434158bd0ef026d33b35fb -133 [main] DEBUG - 开始注册对象:Worker|pi|08d17dd9b7a64eeeaf0a541836a44500 -134 [main] DEBUG - 结束注册对象:Worker|pi|08d17dd9b7a64eeeaf0a541836a44500 -134 [main] DEBUG - 开始注册对象:Worker|pi|c766db4f2ecd49df807458cb010c9fb8 -134 [main] DEBUG - 结束注册对象:Worker|pi|c766db4f2ecd49df807458cb010c9fb8 -135 [main] DEBUG - 开始注册对象:Worker|pi|2240ed47fc9d4bf1a8029920b2cd4edd -135 [main] DEBUG - 结束注册对象:Worker|pi|2240ed47fc9d4bf1a8029920b2cd4edd -167 [main] DEBUG - 开始注册对象:Foreman|pi|fdf013969d33499ca5f5213149fc2134 -183 [main] DEBUG - 结束注册对象:Foreman|pi|fdf013969d33499ca5f5213149fc2134 -348 [RMI TCP Connection(1)-192.168.211.1] INFO - 任务[type:pi,id:0a0dc7334de245109f17eafe064ef54a]被分割为10份 -369 [RMI TCP Connection(1)-192.168.211.1] DEBUG - 线程组<id:0a0dc7334de245109f17eafe064ef54a,type:pi>运行开始,线程数10... -369 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-35b8df60692d455a96a4dc6dc855fef7] DEBUG - 线程<id:0a0dc7334de245109f17eafe064ef54a,type:pi-35b8df60692d455a96a4dc6dc855fef7>运行开始... -371 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-35b8df60692d455a96a4dc6dc855fef7] DEBUG - worker:35b8df60692d455a96a4dc6dc855fef7开始执行 -411 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-39e9d1a14cf544c29794e300ac9cbb91] DEBUG - 线程<id:0a0dc7334de245109f17eafe064ef54a,type:pi-39e9d1a14cf544c29794e300ac9cbb91>运行开始... -469 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-ef68753086374341b80acc66c1b6877a] DEBUG - 线程<id:0a0dc7334de245109f17eafe064ef54a,type:pi-ef68753086374341b80acc66c1b6877a>运行开始... -513 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-3c29555265304dff8e7500e6d2ff444e] DEBUG - 线程<id:0a0dc7334de245109f17eafe064ef54a,type:pi-3c29555265304dff8e7500e6d2ff444e>运行开始... -514 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-348e96c03b434158bd0ef026d33b35fb] DEBUG - 线程<id:0a0dc7334de245109f17eafe064ef54a,type:pi-348e96c03b434158bd0ef026d33b35fb>运行开始... -514 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-11ea430b537f4721a5a4a61b3b887cca] DEBUG - 线程<id:0a0dc7334de245109f17eafe064ef54a,type:pi-11ea430b537f4721a5a4a61b3b887cca>运行开始... -514 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-08d17dd9b7a64eeeaf0a541836a44500] DEBUG - 线程<id:0a0dc7334de245109f17eafe064ef54a,type:pi-08d17dd9b7a64eeeaf0a541836a44500>运行开始... -514 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-c766db4f2ecd49df807458cb010c9fb8] DEBUG - 线程<id:0a0dc7334de245109f17eafe064ef54a,type:pi-c766db4f2ecd49df807458cb010c9fb8>运行开始... -514 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-2240ed47fc9d4bf1a8029920b2cd4edd] DEBUG - 线程<id:0a0dc7334de245109f17eafe064ef54a,type:pi-2240ed47fc9d4bf1a8029920b2cd4edd>运行开始... -515 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-39e9d1a14cf544c29794e300ac9cbb91] DEBUG - worker:39e9d1a14cf544c29794e300ac9cbb91开始执行 -515 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-dd9d0c0b47dd4e18bbf31d7778e3fac1] DEBUG - 线程<id:0a0dc7334de245109f17eafe064ef54a,type:pi-dd9d0c0b47dd4e18bbf31d7778e3fac1>运行开始... -515 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-ef68753086374341b80acc66c1b6877a] DEBUG - worker:ef68753086374341b80acc66c1b6877a开始执行 -527 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-3c29555265304dff8e7500e6d2ff444e] DEBUG - worker:3c29555265304dff8e7500e6d2ff444e开始执行 -527 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-348e96c03b434158bd0ef026d33b35fb] DEBUG - worker:348e96c03b434158bd0ef026d33b35fb开始执行 -590 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-2240ed47fc9d4bf1a8029920b2cd4edd] DEBUG - worker:2240ed47fc9d4bf1a8029920b2cd4edd开始执行 -591 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-c766db4f2ecd49df807458cb010c9fb8] DEBUG - worker:c766db4f2ecd49df807458cb010c9fb8开始执行 -644 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-11ea430b537f4721a5a4a61b3b887cca] DEBUG - worker:11ea430b537f4721a5a4a61b3b887cca开始执行 -702 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-08d17dd9b7a64eeeaf0a541836a44500] DEBUG - worker:08d17dd9b7a64eeeaf0a541836a44500开始执行 -702 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-dd9d0c0b47dd4e18bbf31d7778e3fac1] DEBUG - worker:dd9d0c0b47dd4e18bbf31d7778e3fac1开始执行 -18309 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-3c29555265304dff8e7500e6d2ff444e] DEBUG - worker:3c29555265304dff8e7500e6d2ff444e执行完成 -18309 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-3c29555265304dff8e7500e6d2ff444e] DEBUG - 线程<id:0a0dc7334de245109f17eafe064ef54a,type:pi-3c29555265304dff8e7500e6d2ff444e>运行结束 -18399 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-11ea430b537f4721a5a4a61b3b887cca] DEBUG - worker:11ea430b537f4721a5a4a61b3b887cca执行完成 -18399 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-11ea430b537f4721a5a4a61b3b887cca] DEBUG - 线程<id:0a0dc7334de245109f17eafe064ef54a,type:pi-11ea430b537f4721a5a4a61b3b887cca>运行结束 -18450 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-dd9d0c0b47dd4e18bbf31d7778e3fac1] DEBUG - worker:dd9d0c0b47dd4e18bbf31d7778e3fac1执行完成 -18450 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-dd9d0c0b47dd4e18bbf31d7778e3fac1] DEBUG - 线程<id:0a0dc7334de245109f17eafe064ef54a,type:pi-dd9d0c0b47dd4e18bbf31d7778e3fac1>运行结束 -18613 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-348e96c03b434158bd0ef026d33b35fb] DEBUG - worker:348e96c03b434158bd0ef026d33b35fb执行完成 -18614 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-348e96c03b434158bd0ef026d33b35fb] DEBUG - 线程<id:0a0dc7334de245109f17eafe064ef54a,type:pi-348e96c03b434158bd0ef026d33b35fb>运行结束 -21298 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-35b8df60692d455a96a4dc6dc855fef7] DEBUG - worker:35b8df60692d455a96a4dc6dc855fef7执行完成 -21298 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-35b8df60692d455a96a4dc6dc855fef7] DEBUG - 线程<id:0a0dc7334de245109f17eafe064ef54a,type:pi-35b8df60692d455a96a4dc6dc855fef7>运行结束 -21377 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-2240ed47fc9d4bf1a8029920b2cd4edd] DEBUG - worker:2240ed47fc9d4bf1a8029920b2cd4edd执行完成 -21377 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-2240ed47fc9d4bf1a8029920b2cd4edd] DEBUG - 线程<id:0a0dc7334de245109f17eafe064ef54a,type:pi-2240ed47fc9d4bf1a8029920b2cd4edd>运行结束 -22418 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-c766db4f2ecd49df807458cb010c9fb8] DEBUG - worker:c766db4f2ecd49df807458cb010c9fb8执行完成 -22418 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-c766db4f2ecd49df807458cb010c9fb8] DEBUG - 线程<id:0a0dc7334de245109f17eafe064ef54a,type:pi-c766db4f2ecd49df807458cb010c9fb8>运行结束 -22445 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-39e9d1a14cf544c29794e300ac9cbb91] DEBUG - worker:39e9d1a14cf544c29794e300ac9cbb91执行完成 -22446 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-39e9d1a14cf544c29794e300ac9cbb91] DEBUG - 线程<id:0a0dc7334de245109f17eafe064ef54a,type:pi-39e9d1a14cf544c29794e300ac9cbb91>运行结束 -22446 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-ef68753086374341b80acc66c1b6877a] DEBUG - worker:ef68753086374341b80acc66c1b6877a执行完成 -22446 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-ef68753086374341b80acc66c1b6877a] DEBUG - 线程<id:0a0dc7334de245109f17eafe064ef54a,type:pi-ef68753086374341b80acc66c1b6877a>运行结束 -22462 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-08d17dd9b7a64eeeaf0a541836a44500] DEBUG - worker:08d17dd9b7a64eeeaf0a541836a44500执行完成 -22463 [id:0a0dc7334de245109f17eafe064ef54a,type:pi-08d17dd9b7a64eeeaf0a541836a44500] DEBUG - 线程<id:0a0dc7334de245109f17eafe064ef54a,type:pi-08d17dd9b7a64eeeaf0a541836a44500>运行结束 -22463 [RMI TCP Connection(1)-192.168.211.1] DEBUG - 线程组<id:0a0dc7334de245109f17eafe064ef54a,type:pi>运行结束, 用时:22094ms pi:3.1415926793432933 -22464 [main] DEBUG - 开始注销对象:Worker|pi|08d17dd9b7a64eeeaf0a541836a44500 -22464 [main] DEBUG - 结束注销对象:Worker|pi|08d17dd9b7a64eeeaf0a541836a44500 -22464 [main] DEBUG - 开始注销对象:Worker|pi|11ea430b537f4721a5a4a61b3b887cca -22464 [main] DEBUG - 结束注销对象:Worker|pi|11ea430b537f4721a5a4a61b3b887cca -22464 [main] DEBUG - 开始注销对象:Worker|pi|35b8df60692d455a96a4dc6dc855fef7 -22464 [main] DEBUG - 结束注销对象:Worker|pi|35b8df60692d455a96a4dc6dc855fef7 -22465 [main] DEBUG - 开始注销对象:Worker|pi|348e96c03b434158bd0ef026d33b35fb -22465 [main] DEBUG - 结束注销对象:Worker|pi|348e96c03b434158bd0ef026d33b35fb -22465 [main] DEBUG - 开始注销对象:Worker|pi|dd9d0c0b47dd4e18bbf31d7778e3fac1 -22465 [main] DEBUG - 结束注销对象:Worker|pi|dd9d0c0b47dd4e18bbf31d7778e3fac1 -22465 [main] DEBUG - 开始注销对象:Worker|pi|ef68753086374341b80acc66c1b6877a -22465 [main] DEBUG - 结束注销对象:Worker|pi|ef68753086374341b80acc66c1b6877a -22465 [main] DEBUG - 开始注销对象:Worker|pi|39e9d1a14cf544c29794e300ac9cbb91 -22465 [main] DEBUG - 结束注销对象:Worker|pi|39e9d1a14cf544c29794e300ac9cbb91 -22465 [main] DEBUG - 开始注销对象:Worker|pi|c766db4f2ecd49df807458cb010c9fb8 -22465 [main] DEBUG - 结束注销对象:Worker|pi|c766db4f2ecd49df807458cb010c9fb8 -22465 [main] DEBUG - 开始注销对象:Worker|pi|2240ed47fc9d4bf1a8029920b2cd4edd -22465 [main] DEBUG - 结束注销对象:Worker|pi|2240ed47fc9d4bf1a8029920b2cd4edd -22465 [main] DEBUG - 开始注销对象:Worker|pi|3c29555265304dff8e7500e6d2ff444e -22466 [main] DEBUG - 结束注销对象:Worker|pi|3c29555265304dff8e7500e6d2ff444e -22466 [main] DEBUG - 开始注销对象:Foreman|pi|fdf013969d33499ca5f5213149fc2134 -22466 [main] DEBUG - 结束注销对象:Foreman|pi|fdf013969d33499ca5f5213149fc2134 -22466 [main] DEBUG - 开始注销对象:WorkQueue -22466 [main] DEBUG - 结束注销对象:WorkQueue -25287 [Thread-2] DEBUG - 检测已注册对象的可用性完成可以看到中间的计算结果是:
pi:3.1415926793432933
上面可以看到Tiny并行计算框架的总体设计思路及一个典型示例的计算。
也可以看复杂示例。
也可以查看使用介绍
框架本身封装了大部分的代码工作,程序员只需要编写自己的工人及任务分解合并器就可以完成分布式计算。
下面是Tiny并行计算框架的代码统计情况,如你所见,总共只有1150行,去掉120个import只有1030行代码。
不管你是Java老手还是Java新手,保证通俗易懂,马上掌握。
当然,关注这部分的同学,也可以观看、对比一下本期开源专访 彭渊大师的分布式开源框架:fourinone