原文:https://blog.csdn.net/monkey_d_meng/article/details/6419246
要做这个东东的想法由来以久了的说,一个月以前动手把代码实现了下来,今天觉得如果实在不写成博文记录下来的话,不知又要推到何年何月了。嗯,废话不多说,直入正题了~
在实际的开发过程中,我们经常会遇到这样的情况:海量的并发用户发送请求要求服务器处理,而服务器集群之间彼此也经常会传递请求,用于完成特定的任务。我们可以把处理每个请求的过程理解为是一种任务的执行,这样的话,就相当于有一堆的作业摆在那时需要执行。而我们所要关心的是作业的数据结构是如何描述的、作业是如何存放、如何取出的以及是如何执行的。
基于上述的思路,我们可以将任何的操作流程封装在一个Job里,并将其丢入到一个Job池中,Job池最好设置成阻塞模式,这样即可唤醒等待的多个处理器从Job池中取出Job并执行。这样一个框架的形成就牵涉到了宏观设计的问题:即Job、Job池及处理器这三种最重要的资源是如何定义和组织的。JobFlow整体的框架结果如下图所示:
为了便于简化开发过程,使用的开发语言为Java,但这种设计思路是基于面向对象的,具备普适意义,感兴趣的可以自己实现C++版本。代码的组织结构截图如下:
一、Job作业的设计
Job即是对处理某个任务/作业执行流程的一种抽象,Job接口提供一个最重要的方法:run(),我们可以定义具体的Job类,实现run()方法,用于填充必要的业务逻辑,在run()执行过程中可能会产生新的Job。按照这样的思路,我们可以在一个大Job的run()方法定义相关的执行流程,从而派生出若干个小Job,继而实现了对一个大Job的拆分过程,同时每小Job也还可以继续做拆分…
如下图所示,简单地举例:我们定义了加法Job、减法Job、乘法Job及除法Job,每种Job均实现了Job接口,对于run()方法做了对应的填充,分别实现了两个数的相加、相减、相乘和相除。
[java] view plain copy
package com.jobflow.framework;
import java.io.Serializable;
/**
* 抽象Job接口:提供Job的业务逻辑入口和Job相关的信息
*
* Email:530025983@qq.com
*
* @author MONKEY.D.MENG 2011-04-06
*
*/
public interface Job extends Runnable, Serializable
{
/**
* Job执行入口,填充必要的业务逻辑,执行过程中可能产生新的任务
*
*/
public void run();
/**
* 获知该Job相关的信息
*
* @return Job相关信息
*/
public String getJobInfo();
}
二、作业池JobPool的设计
既然生成了Job,那么这些Job就需要进行存放。JobPool作为Job作业存储的载体,最为关注的是Job的存入put(),和Job的取出get()。而Job池本身的存储介质可以是多种多样的:内存、数据库或者是文件系统;Job池的取出策略也可以是多种多样的:作业先进先出FIFO、作业先进后出FILO、大作业优先、小作业优先、响应比高者优先、随机等等。
由于JobPool的存储介质和取出策略两者是独立的部分,使用桥接模式代替类之间的继承关系,使这两个部分独立地变化,从而避免了派生类的膨胀。
为了便于说明问题,本文后面的代码里只关注了取出策略的变化,存储介质只是基于内存实现的,但这并不代表我不知道如何实现这个桥接模式。
[java] view plain copy
package com.jobflow.framework;
import java.io.Serializable;
import java.rmi.Remote;
import java.rmi.RemoteException;
/**
* 作业池JobPool接口:提供Job池最基本的操作,JobPool只关注业务逻辑接口,不关注Job具体的存储介质、存取方式等
*
* Email:530025983@qq.com
*
* @author MONKEY.D.MENG 2011-04-06
*
*/
public interface JobPool extends Remote, Serializable
{
/**
* 获取一个Job
*
* @return 一个Job
*/
public Job get() throws RemoteException;
/**
* 存放一个Job
*
* @param Job
* 要存放的Job
* @return 是否成功
*/
public boolean put(Job job) throws RemoteException;
/**
* 批量存放Job
*
* @param object
* 要存放的批量Job
* @return 是否成功
*/
public boolean putBatch(Job[] jobs) throws RemoteException;
/**
* 获取Job池中Job的数目
*
* @return Job池中Job数目
*/
public int getNum() throws RemoteException;
/**
* 判断Job池是否为空
*
* @return Job池是否为空
*/
public boolean isEmpty() throws RemoteException;
/**
* 判断Job池是否满载
*
* @return Job池是否满载
*/
public boolean isFull() throws RemoteException;
/**
* 清空Job池
*
* @return 是否清空Job池
*/
public boolean clear() throws RemoteException;
}
三、处理器Executer的设计
有了Job,也有了存储Job的JobPool,那么我们就需要定义一些计算资源从JobPool里出取一个Job,并执行Job.run()。我们将这种计算资源抽象为Executer,即处理器。Executer可以是一个进程或者是一个线程,可以是本地的计算资源,也可以考虑是远程的计算资源,这样我们可以灵活地将单机环境的系统扩展为一个分布式计算系统。为了简单处理,我们Executer的设计先考虑本地线程的情况。
[java] view plain copy
package com.jobflow.framework;
/**
* 抽象处理器接口:定义处理器的相关执行操作,处理器可以是单机环境的线程、进程,也可以是分布式环境的线程、进程
*
* Email:530025983@qq.com
*
* @author MONKEY.D.MENG 2011-04-06
*
*/
public interface Executer
{
/**
* 处理器执行Job的接口
*/
public abstract void run();
/**
* 处理器开始执行
*/
public abstract void start();
/**
* 处理器关闭接口
*/
public abstract void shutdown();
/**
* 获取处理器ID
*
* @return 处理器ID
*/
public int getExecuterId();
/**
* 设置处理器ID
*
* @param executerId
* 处理器ID
*/
public void setExecuterId(int executerId);
/**
* 获取该处理器作用的Job池
*
* @return Job池
*/
public JobPool getJobPool();
/**
* 为该处理器设置对应的Job池
*
* @param jobPool
* Job池
*/
public void setJobPool(JobPool jobPool);
/**
* 处理器是否正在执行
*
* @return 处理器是否正在执行
*/
public boolean isRunning();
/**
* 设置处理器的执行状态
*
* @param isRunning
* 处理器的执行状态
*/
public void setRunning(boolean isRunning);
/**
* 处理器是否已经关闭
*
* @return 处理器是否已经关闭
*/
public boolean isShutdown();
/**
* 设置处理器的关闭状态
*
* @param isShutdown
* 处理器的关闭状态
*/
public void setShutdown(boolean isShutdown);
}
我们在使用计算资源Executer时,并非只有一个Executer,利用ExecuterPool对Executer进行管理。
[java] view plain copy
package com.jobflow.framework;
/**
* 抽象处理器池接口ExecuterPool:简单地对处理器队列进行封装,提供必须的操作
*
* Email:530025983@qq.com
*
* @author MONKEY.D.MENG 2011-04-06
*
*/
public interface ExecuterPool
{
/**
* 从处理器池中获得处理器数组
*
* @return 处理器数组
*/
public Executer[] getExecuters();
/**
* 获取处理器池中最大能容纳的处理器数目
*
* @return 最大能容纳的处理器数目
*/
public int getMaxExecuterNum();
/**
* 设置处理器池最大能容纳的处理器数目
*
* @param maxExecuterNum
* 最大能容纳的处理器数目
*/
public void setMaxExecuterNum(int maxExecuterNum);
/**
* 获取处理器池所对应的Job池
*
* @return 对应的Job池
*/
public JobPool getJobPool();
/**
* 为处理器池设置对应的Job池
*
* @param jobPool
* 对应的Job池
*/
public void setJobPool(JobPool jobPool);
}
为了使计算资源也作业本身对接,我们创建一个作业池管理类JobPoolManager封装JobPool与ExecuterPool,用于取出Job并执行。
[java] view plain copy
package com.jobflow.framework;
import java.rmi.RemoteException;
/**
* 管理作业池接口JobPoolManager:用于提供管理Job池及处理器的接口
*
* Email:530025983@qq.com
*
* @author MONKEY.D.MENG 2011-04-06
*
*/
public interface JobPoolManager
{
/**
* 启动工作线程,开始处理任务
*/
public void startup();
/**
* 向Job池中放入新Job,托管交由Job池处理
*
* @param newJob
* 新Job
* @throws RemoteException
*/
public void put(Job newJob) throws RemoteException;
/**
* 批量增加新Job,托管交由Job池处理
*
* @param jobs
* 批量Job
* @throws RemoteException
*/
public void putBatch(Job[] jobs) throws RemoteException;
/**
* 获知Job池的相关信息
*
* @return Job池的相关信息
* @throws RemoteException
*/
public String getJobPoolInfo() throws RemoteException;
/**
* 停止工作线程,工作线程不一定立即停止,只有在线程处于运行状态时会立即停止
*
* @throws RemoteException
*/
public void shutdown() throws RemoteException;
/**
* 获取所要管理的Job池
*
* @return Job池
*/
public JobPool getJobPool();
/**
* 设置要管理的Job池
*
* @param jobPool
* 所要管理的Job池
*/
public void setJobPool(JobPool jobPool);
/**
* 设置所要管理的处理器池
*
* @return 所要管理的处理器池
*/
public ExecuterPool getExecuters();
/**
* 设置所要管理的处理器池
*
* @param executers
* 所要管理的处理器池
*/
public void setExecuters(ExecuterPool executers);
}
四、基本的实现BaseXXXX.java
有了上述这些框架基础的接口之后,我们需要对这些接口做一些最初步的实现,称之为BaseXXXX.java,对应源代码分别如下:
[java] view plain copy
package com.jobflow.base;
import com.jobflow.framework.impl.JobImpl;
/**
* 基础工作类BaseJob:提供基本Job操作
*
* Email:530025983@qq.com
*
* @author MONKEY.D.MENG 2011-04-06
*
*/
public abstract class BaseJob extends JobImpl
{
}
package com.jobflow.base;
import java.rmi.RemoteException;
import com.jobflow.framework.impl.JobPoolImpl;
/**
* 基础的Job池BaseJobPool类:提供最基本Job池的方法
*
* Email:530025983@qq.com
*
* @author MONKEY.D.MENG 2011-04-06
*
*/
public abstract class BaseJobPool extends JobPoolImpl
{
/**
* 默认构造函数
*/
public BaseJobPool() throws RemoteException
{
this(MAX_JOB_NUM);
}
/**
* 构造函数,初始化相关参数
*
* @param maxJobNum
* 实际Job池中所能存储的最大Job数目
*/
public BaseJobPool(int maxJobNum) throws RemoteException
{
this.maxJobNum = maxJobNum;
}
}
package com.jobflow.base;
import com.jobflow.framework.JobPool;
import com.jobflow.framework.impl.ExecuterImpl;
/**
* 基础的处理器BaseExecuter类:提供最基本处理Job的方法
*
* Email:530025983@qq.com
*
* @author MONKEY.D.MENG 2011-04-06
*
*/
public class BaseExecuter extends ExecuterImpl
{
/**
* 构造函数
*
* @param executerId
* 处理器ID
* @param jobPool
* 所要处理的Job池
*/
public BaseExecuter(int executerId, JobPool jobPool)
{
super(executerId, jobPool);
}
}
package com.jobflow.base;
import com.jobflow.framework.JobPool;
import com.jobflow.framework.impl.ExecuterPoolImpl;
/**
* 基础的处理器池类BaseExecuterPool:基本的封装处理器队列
*
* Email:530025983@qq.com
*
* @author MONKEY.D.MENG 2011-04-06
*
*/
public class BaseExecuterPool extends ExecuterPoolImpl
{
/**
* 构造函数
*
* @param jobPool
* 设置处理器池所对应的Job池
*/
public BaseExecuterPool(JobPool jobPool)
{
super(jobPool);
}
/**
* 构造函数
*
* @param maxExecuterNum
* 能容纳的最大处理器数目
* @param jobPool
* 要处理的Job池
*/
public BaseExecuterPool(int maxExecuterNum, JobPool jobPool)
{
super(maxExecuterNum, jobPool);
}
}
package com.jobflow.base;
import com.jobflow.framework.JobPool;
import com.jobflow.framework.impl.ExecuterPoolImpl;
import com.jobflow.framework.impl.JobPoolManagerImpl;
/**
* 基本的管理Job池的类BaseJobPoolMananger:用于管理Job池资源对象,并分配相应的处理器执行Job
*
* Email:530025983@qq.com
*
* @author MONKEY.D.MENG 2011-04-06
*
*/
public class BaseJobPoolManager extends JobPoolManagerImpl
{
/**
* 构造函数
*
* @param jobPool
* 所要处理的Job池
* @param executerPool
* 处理器池,封装了处理器队列
*/
public BaseJobPoolManager(JobPool jobPool, ExecuterPoolImpl executerPool)
{
super(jobPool, executerPool);
}
}
五、具体的实现
基于BaseXXXX.java的基本实现,我们需要定义能够在实际运行环境下工作的类,源代码如下:
[java] view plain copy
package com.jobflow.framework.impl;
import java.util.Date;
import com.jobflow.framework.Job;
/**
* 抽象工作类BaseJob:提供基本Job操作
*
* Email:530025983@qq.com
*
* @author MONKEY.D.MENG 2011-04-06
*
*/
public abstract class JobImpl implements Job
{
/* Job的ID */
protected long id;
/* Job创建时间 */
protected Date createTime = null;
/* Job提交执行时间 */
protected Date submitTime = null;
/* Job开始执行时间 */
protected Date beginTime = null;
/* JOb执行完成时间 */
protected Date finishTime = null;
/**
* 默认构造函数初始化Job的创建时间
*/
public JobImpl()
{
this.createTime = new Date();
}
/**
* Job执行入口,填充必要的业务逻辑,执行过程中可能产生新的任务
*
*/
public abstract void run();
/**
* 获知该Job相关的信息
*
* @return Job相关信息
*/
public String getJobInfo()
{
return "Job:【ID】=" + this.id + "/t【生成时间】:" + this.createTime
+ "/t【开始执行时间】:" + this.beginTime + "/t【执行完成时间】:"
+ this.finishTime;
}
/**
* 获取Job的ID
*
* @return
*/
public long getId()
{
return id;
}
/**
* 设置Job的ID
*
* @param id
* Job的ID
*/
public void setId(long id)
{
this.id = id;
}
/**
* 获取Job的创建时间
*
* @return Job的创建时间
*/
public Date getCreateTime()
{
return createTime;
}
/**
* 设置Job的创建时间
*
* @param createTime
* Job的创建时间
*/
public void setCreateTime(Date createTime)
{
this.createTime = createTime;
}
/**
* 获取Job的提交时间
*
* @return Job的提交时间
*/
public Date getSubmitTime()
{
return submitTime;
}
/**
* 设置Job的提交时间
*
* @param submitTime
* Job的提交时间
*/
public void setSubmitTime(Date submitTime)
{
this.submitTime = submitTime;
}
/**
* 获取Job开始执行的时间
*
* @return Job开始执行的时间
*/
public Date getBeginTime()
{
return beginTime;
}
/**
* 设置Job开始执行的时间
*
* @param beginTime
* Job开始执行的时间
*/
public void setBeginTime(Date beginTime)
{
this.beginTime = beginTime;
}
/**
* 获取Job执行结束的时间
*
* @return Job执行结束的时间
*/
public Date getFinishTime()
{
return finishTime;
}
/**
* 设置Job执行结束的时间
*
* @param finishTime
* Job执行结束的时间
*/
public void setFinishTime(Date finishTime)
{
this.finishTime = finishTime;
}
}
package com.jobflow.framework.impl;
import java.io.Serializable;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import com.jobflow.framework.Job;
import com.jobflow.framework.JobPool;
/**
* 抽象Job池BaseJobPool类:提供最基本Job池的方法
*
* Email:530025983@qq.com
*
* @author MONKEY.D.MENG 2011-04-06
*
*/
public abstract class JobPoolImpl extends UnicastRemoteObject implements
JobPool, Serializable
{
/* 默认Job池中所能存储的最大Job数目 */
protected static final int MAX_JOB_NUM = 100;
/* 实际Job池中所能存储的最大Job数目 */
protected int maxJobNum = -1;
/**
* 默认构造函数
*/
public JobPoolImpl() throws RemoteException
{
this(MAX_JOB_NUM);
}
/**
* 构造函数,初始化相关参数
*
* @param maxJobNum
* 实际Job池中所能存储的最大Job数目
*/
public JobPoolImpl(int maxJobNum) throws RemoteException
{
this.maxJobNum = maxJobNum;
}
/**
* 批量存放Job
*
* @param object
* 要存放的批量Job
* @return 是否成功
*/
public boolean putBatch(Job[] jobs) throws RemoteException
{
return false;
}
/**
* 获取Job池中Job的数目
*
* @return Job池中Job数目
*/
public int getNum() throws RemoteException
{
return -1;
}
/**
* 同步地获取最大能容纳的Job数目
*
* @return 最大能容纳的Job数目
*/
public synchronized int getMaxJobNum() throws RemoteException
{
return maxJobNum;
}
/**
* 设置最大能容纳的Job数目
*
* @param maxJobNum
* 最大能容纳的Job数目
*/
public void setMaxJobNum(int maxJobNum) throws RemoteException
{
this.maxJobNum = maxJobNum;
}
/**
* 获取默认的最大能容纳的Job数目MAX_JOB_NUM
*
* @return 默认的最大能容纳的Job数目MAX_JOB_NUM
*/
public static int getMAX_JOB_NUM() throws RemoteException
{
return MAX_JOB_NUM;
}
}
package com.jobflow.framework.impl;
import java.rmi.RemoteException;
import java.util.Date;
import com.jobflow.framework.Executer;
import com.jobflow.framework.JobPool;
/**
* 处理器抽象类ExecuterImpl:以线程作为处理器的载体,并实现了处理器Executer抽象接口
*
* Email:530025983@qq.com
*
* @author MONKEY.D.MENG 2011-04-06
*
*/
public class ExecuterImpl extends Thread implements Executer
{
/* 处理器的ID */
protected int executerId;
/* 所作用的作业池 */
protected JobPool jobPool = null;
/* 该工作线程是否正在执行 */
protected boolean isRunning = false;
/* 该工作线程是否已经关闭 */
protected boolean isShutdown = false;
/**
* 设置处理器的ID和所要处理的Job池
*
* @param executerId
* 处理器ID
* @param jobPool
* 所要处理的Job池
*/
public ExecuterImpl(int executerId, JobPool jobPool)
{
this.executerId = executerId;
this.jobPool = jobPool;
}
/**
* 关键代码:处理Job池中的Job
*
*/
public void run()
{
while (!this.isShutdown)
{
JobImpl job = null;
/* 同步对象为JobPool */
synchronized (jobPool)
{
try
{
while (jobPool.isEmpty())
{
try
{
/* Job池为空则进入等待状态 */
jobPool.wait();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
/* 从Job池中取出一个Job */
job = (JobImpl) jobPool.get();
}
catch (RemoteException e)
{
e.printStackTrace();
}
}
if (null != job)
{
/* 设置忙碌状态 */
this.isRunning = true;
/* 设置Job开始执行时间 */
job.setBeginTime(new Date());
/* 执行Job */
new Thread(job).start();
/* 设置Job执行完毕的时间 */
job.setFinishTime(new Date());
/* 处理器重新处于等待状态 */
this.isRunning = false;
/* 打印出Job的相关信息 */
System.out.println(job.getJobInfo());
/* 清除处理后的Job */
job = null;
}
}
}
/**
* 设置处理器为关闭状态
*/
public void shutdown()
{
this.setShutdown(true);
}
/**
* 获取处理器ID
*
* @return 处理器ID
*/
public int getExecuterId()
{
return executerId;
}
/**
* 设置处理器ID
*
* @param executerId
* 处理器ID
*/
public void setExecuterId(int executerId)
{
this.executerId = executerId;
}
/**
* 获取该处理器作用的Job池
*
* @return Job池
*/
public JobPool getJobPool()
{
return jobPool;
}
/**
* 为该处理器设置对应的Job池
*
* @param jobPool
* Job池
*/
public void setJobPool(JobPool jobPool)
{
this.jobPool = jobPool;
}
/**
* 处理器是否正在执行
*
* @return 处理器是否正在执行
*/
public boolean isRunning()
{
return isRunning;
}
/**
* 设置处理器的执行状态
*
* @param isRunning
* 处理器的执行状态
*/
public void setRunning(boolean isRunning)
{
this.isRunning = isRunning;
}
/**
* 处理器是否已经关闭
*
* @return 处理器是否已经关闭
*/
public boolean isShutdown()
{
return isShutdown;
}
/**
* 设置处理器的关闭状态
*
* @param isShutdown
* 处理器的关闭状态
*/
public void setShutdown(boolean isShutdown)
{
this.isShutdown = isShutdown;
}
}
package com.jobflow.framework.impl;
import com.jobflow.framework.Executer;
import com.jobflow.framework.ExecuterPool;
import com.jobflow.framework.JobPool;
/**
* 抽象处理器池类ExecuterPoolImpl:简单地对处理器队列进行封装
*
* Email:530025983@qq.com
*
* @author MONKEY.D.MENG 2011-04-06
*
*/
public class ExecuterPoolImpl implements ExecuterPool
{
/* 默认处理器池中所能存储的最大处理器数目 */
protected static final int MAX_EXECUTER_NUM = 10;
/* 实际处理器池中所能存储的最大处理器数目 */
protected int maxExecuterNum = 0;
/* 处理器所处理的Job池 */
protected JobPool jobPool = null;
/* 处理器队列 */
protected Executer[] executers = null;
/**
* 构造函数
*
* @param jobPool
* 所要处理的Job池
*/
public ExecuterPoolImpl(JobPool jobPool)
{
this(MAX_EXECUTER_NUM, jobPool);
}
/**
* 构造函数,初始化相关参数
*
* @param maxExecuterNum
* 实际Job池中所能存储的最大Job数目
* @param jobPool
* 所要处理的Job池
*/
public ExecuterPoolImpl(int maxExecuterNum, JobPool jobPool)
{
this.maxExecuterNum = maxExecuterNum;
this.jobPool = jobPool;
this.init();
}
/**
* 初始化处理器
*/
public void init()
{
/* 必须先为数组申请存储空间,否则会出现空指针异常 */
executers = new Executer[maxExecuterNum];
/* 遍历初始化处理器 */
for (int index = 0; index < maxExecuterNum; ++index)
{
this.executers[index] = new ExecuterImpl(index + 1, jobPool);
}
}
/**
* 从处理器池中获得处理器数组
*
* @return 处理器数组
*/
public Executer[] getExecuters()
{
/* 如果处理器队列为空,则调用init()函数初始化处理器队列 */
if (null == executers)
{
this.init();
}
return this.executers;
}
/**
* 获取处理器池中最大能容纳的处理器数目
*
* @return 最大能容纳的处理器数目
*/
public int getMaxExecuterNum()
{
return maxExecuterNum;
}
/**
* 设置处理器池最大能容纳的处理器数目
*
* @param maxExecuterNum
* 最大能容纳的处理器数目
*/
public void setMaxExecuterNum(int maxExecuterNum)
{
this.maxExecuterNum = maxExecuterNum;
}
/**
* 默认的处理器池所容纳最大数目MAX_EXECUTER_NUM
*
* @return 默认的处理器池所容纳最大数目MAX_EXECUTER_NUM
*/
public static int getMAX_EXECUTER_NUM()
{
return MAX_EXECUTER_NUM;
}
/**
* 获取处理器池所对应的Job池
*
* @return 对应的Job池
*/
public JobPool getJobPool()
{
return jobPool;
}
/**
* 为处理器池设置对应的Job池
*
* @param jobPool
* 对应的Job池
*/
public void setJobPool(JobPool jobPool)
{
this.jobPool = jobPool;
}
/**
* 设置处理器数组
*
* @param executers
* 处理器数组
*/
public void setExecuters(Executer[] executers)
{
this.executers = executers;
}
}
package com.jobflow.framework.impl;
import java.rmi.RemoteException;
import com.jobflow.framework.Executer;
import com.jobflow.framework.ExecuterPool;
import com.jobflow.framework.Job;
import com.jobflow.framework.JobPool;
import com.jobflow.framework.JobPoolManager;
/**
* 管理作业池抽象类JobPoolManagerImpl:用于管理Job池及处理器
*
* Email:530025983@qq.com
*
* @author MONKEY.D.MENG 2011-04-06
*
*/
public class JobPoolManagerImpl implements JobPoolManager
{
/* Job池的存储载体 */
protected JobPool jobPool = null;
/* 处理器队列 */
protected ExecuterPool executers = null;
/**
* 构造函数,指定对应的Job池和处理器队列
*
* @param jobPool
* 要处理的Job池
* @param executers
* 用于执行的处理器队列
*/
public JobPoolManagerImpl(JobPool jobPool, ExecuterPool executers)
{
this.jobPool = jobPool;
this.executers = executers;
/* 启动处理器 */
// this.startup();
}
/**
* 启动工作线程,开始处理任务
*/
public void startup()
{
System.out.println("正在启动处理器...");
/* 启动处理器工作 */
for (Executer executer : executers.getExecuters())
{
executer.start();
}
}
/**
* 向Job池中放入新Job,托管交由Job池处理
*
* @param newJob
* 新Job
* @throws RemoteException
*/
public void put(Job newJob) throws RemoteException
{
System.out.println("往Job池中放入一个新Job...");
jobPool.put(newJob);
}
/**
* 批量增加新Job,托管交由Job池处理
*
* @param jobs
* 批量Job
* @throws RemoteException
*/
public void putBatch(Job[] jobs) throws RemoteException
{
System.out.println("往Job池中放入批量放入新Job...");
jobPool.putBatch(jobs);
}
/**
* 获知Job池的相关信息
*
* @return Job池的相关信息
* @throws RemoteException
*/
public String getJobPoolInfo() throws RemoteException
{
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("Job池中尚未处理的Job数目:" + jobPool.getNum() + "/n");
for (Executer executer : executers.getExecuters())
{
stringBuffer.append("处理器【" + executer.getExecuterId() + "】正在"
+ ((executer.isRunning()) ? "运行..." : "等待.../n"));
}
return stringBuffer.toString();
}
/**
* 停止工作线程,工作线程不一定立即停止,只有在线程处于运行状态时会立即停止
*
* @throws RemoteException
*/
public synchronized void shutdown() throws RemoteException
{
System.out.println("正在停止处理器...");
/* 关闭处理器 */
for (Executer executer : executers.getExecuters())
{
executer.shutdown();
executer = null;
}
/* 清空Job池 */
jobPool.clear();
System.out.println("处理器已经全部停止,Job池已经清空...");
}
/**
* 获取所要管理的Job池
*
* @return Job池
*/
public JobPool getJobPool()
{
return jobPool;
}
/**
* 设置要管理的Job池
*
* @param jobPool
* 所要管理的Job池
*/
public void setJobPool(JobPool jobPool)
{
this.jobPool = jobPool;
}
/**
* 设置所要管理的处理器池
*
* @return 所要管理的处理器池
*/
public ExecuterPool getExecuters()
{
return executers;
}
/**
* 设置所要管理的处理器池
*
* @param executers
* 所要管理的处理器池
*/
public void setExecuters(ExecuterPool executers)
{
this.executers = executers;
}
}
六、基于队列的JobPool实现
我们现在可以自己定义具体的Job存取方式了,如以List队列形式存放Job会遵循FIFO的取出规则:
[java] view plain copy
package com.jobflow.impl;
import java.rmi.RemoteException;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import com.jobflow.base.BaseJob;
import com.jobflow.base.BaseJobPool;
import com.jobflow.framework.Job;
/**
* List形式的Job池类ListJobPool:以List形式为载体的Job池实现
*
* Email:530025983@qq.com
*
* @author MONKEY.D.MENG 2011-04-06
*
*/
public class ListJobPool extends BaseJobPool
{
/* Job池的序列号ID号 */
private static final long serialVersionUID = -6236080833979961860L;
/* 用于标识Job的ID */
private long jobCounter = 0;
/* Job池存储的载体 */
private List<Job> jobList = Collections
.synchronizedList(new LinkedList<Job>());
/**
* 默认构造函数
*/
public ListJobPool() throws RemoteException
{
super();
}
/**
* 构造函数,初始化相关参数
*
* @param maxJobNum
* 实际Job池中所能存储的最大Job数目
*/
public ListJobPool(int maxJobNum) throws RemoteException
{
super(maxJobNum);
}
/**
* 从Job池中取出一个Job
*
* @param 一个Job
*/
public Job get() throws RemoteException
{
synchronized (this)
{
return jobList.remove(0);
}
}
/**
* 向Job池中放入新Job
*
* @param newJob
* 新Job
*/
public boolean put(Job newJob) throws RemoteException
{
synchronized (this)
{
/* 强制使用BaseJob类型,设置Job的相关属性 */
((BaseJob) newJob).setId(++jobCounter);
((BaseJob) newJob).setSubmitTime(new Date());
/* 放入Job池 */
jobList.add(newJob);
/* 唤醒Job池,开始执行Job */
jobList.notifyAll();
return true;
}
}
/**
* 批量增加新Job
*
* @param jobs
* 批量Job
*/
public boolean putBatch(Job[] jobs) throws RemoteException
{
/* 作业队列为空则返回 */
if (null == jobs || jobs.length == 0)
{
return false;
}
/* 同步对象为封装了Job队列的ListJobPool */
synchronized (this)
{
/* 遍历将Job依次放入Job池中 */
for (int i = 0; i < jobs.length; ++i)
{
if (null == jobs[i])
{
continue;
}
/* 设置Job相关属性信息 */
((BaseJob) jobs[i]).setId(++jobCounter);
((BaseJob) jobs[i]).setSubmitTime(new Date());
/* 将该Job放入Job池中 */
jobList.add(jobs[i]);
}
/* 唤醒Job池,开始执行 */
this.notifyAll();
return true;
}
}
/**
* 获取Job池中Job的数目
*
* @return Job池中Job数目
*/
public int getNum() throws RemoteException
{
synchronized (this)
{
return jobList.size();
}
}
/**
* Job池是否为空
*
* @return Job池是否为空
*/
public boolean isEmpty() throws RemoteException
{
synchronized (this)
{
return jobList.isEmpty();
}
}
/**
* Job池是否已满
*
* @return Job池是否已满
*/
public boolean isFull() throws RemoteException
{
synchronized (this)
{
return jobList.size() >= this.getMaxJobNum();
}
}
/**
* Job池是已清空
*
* @return Job池是已清空
*/
public boolean clear() throws RemoteException
{
synchronized (this)
{
jobList.clear();
return true;
}
}
/**
* 获取Job的总数目
*
* @return Job的总数目
*/
public synchronized long getJobCounter() throws RemoteException
{
return jobCounter;
}
/**
* 设置Job的总数目
*
* @param jobCounter
* Job的总数目
*/
public synchronized void setJobCounter(long jobCounter)
throws RemoteException
{
this.jobCounter = jobCounter;
}
/**
* 获取Job的队列
*
* @return Job队列
*/
public synchronized List<Job> getJobList() throws RemoteException
{
return jobList;
}
/**
* 设置Job队列
*
* @param jobList
*/
public synchronized void setJobList(List<Job> jobList)
throws RemoteException
{
this.jobList = jobList;
}
}
七、单机环境测试
为了对框架本身进行简单的测试,我们编写了4个简单的Job作业,分别做加减乘除运算,并编写了一个Test类用于框架的执行:
[java] view plain copy
package com.jobflow.test;
import com.jobflow.base.BaseJob;
/**
* 简单的工作类JobA:实现两个随机数的+运算
*
* Email:530025983@qq.com
*
* @author MONKEY.D.MENG 2011-04-06
*
*/
public class JobA extends BaseJob
{
/* 序列化的ID号 */
private static final long serialVersionUID = -8497864829928543297L;
/* 操作数1 */
private int num1 = (int) (Math.random() * 100);
/* 操作数2 */
private int num2 = (int) (Math.random() * 100);
/**
* Job执行入口
*/
public void run()
{
int result = num1 + num2;
System.out.println(num1 + "+" + num2 + "=" + result);
}
/**
* 基本set、get方法
*
* @return
*/
public int getNum1()
{
return num1;
}
public void setNum1(int num1)
{
this.num1 = num1;
}
public int getNum2()
{
return num2;
}
public void setNum2(int num2)
{
this.num2 = num2;
}
}
package com.jobflow.test;
import com.jobflow.base.BaseJob;
/**
* 简单的工作类JobB:实现两个随机数的-运算
*
* Email:530025983@qq.com
*
* @author MONKEY.D.MENG 2011-04-06
*
*/
public class JobB extends BaseJob
{
/* 序列化的ID号 */
private static final long serialVersionUID = -344356077313636718L;
/* 操作数1 */
private int num1 = (int) (Math.random() * 100);
/* 操作数2 */
private int num2 = (int) (Math.random() * 100);
/**
* Job执行入口
*/
public void run()
{
int result = num1 + num2;
System.out.println(num1 + "-" + num2 + "=" + result);
}
/**
* 基本set、get方法
*
* @return
*/
public int getNum1()
{
return num1;
}
public void setNum1(int num1)
{
this.num1 = num1;
}
public int getNum2()
{
return num2;
}
public void setNum2(int num2)
{
this.num2 = num2;
}
}
package com.jobflow.test;
import com.jobflow.base.BaseJob;
/**
* 简单的工作类JobC:实现两个随机数的*运算
*
* Email:530025983@qq.com
*
* @author MONKEY.D.MENG 2011-04-06
*
*/
public class JobC extends BaseJob
{
/* 序列化的ID号 */
private static final long serialVersionUID = 4680511842453574631L;
/* 操作数1 */
private int num1 = (int) (Math.random() * 100);
/* 操作数2 */
private int num2 = (int) (Math.random() * 100);
/**
* Job执行入口
*/
public void run()
{
int result = num1 + num2;
System.out.println(num1 + "*" + num2 + "=" + result);
}
/**
* 基本set、get方法
*
* @return
*/
public int getNum1()
{
return num1;
}
public void setNum1(int num1)
{
this.num1 = num1;
}
public int getNum2()
{
return num2;
}
public void setNum2(int num2)
{
this.num2 = num2;
}
}
package com.jobflow.test;
import com.jobflow.base.BaseJob;
/**
* 简单的工作类JobD:实现两个随机数的/运算
*
* Email:530025983@qq.com
*
* @author MONKEY.D.MENG 2011-04-06
*
*/
public class JobD extends BaseJob
{
/* 序列化的ID号 */
private static final long serialVersionUID = -5806851570931840024L;
/* 操作数1 */
private int num1 = (int) (Math.random() * 100);
/* 操作数2 */
private int num2 = (int) (Math.random() * 100);
/**
* Job执行入口
*/
public void run()
{
int result = num1 + num2;
System.out.println(num1 + "/" + num2 + "=" + result);
}
/**
* 基本set、get方法
*
* @return
*/
public int getNum1()
{
return num1;
}
public void setNum1(int num1)
{
this.num1 = num1;
}
public int getNum2()
{
return num2;
}
public void setNum2(int num2)
{
this.num2 = num2;
}
}
package com.jobflow.test;
import java.rmi.RemoteException;
import com.jobflow.base.BaseExecuterPool;
import com.jobflow.base.BaseJobPoolManager;
import com.jobflow.framework.Job;
import com.jobflow.framework.JobPool;
import com.jobflow.framework.impl.ExecuterPoolImpl;
import com.jobflow.framework.impl.JobPoolManagerImpl;
import com.jobflow.impl.ListJobPool;
/**
* 异步工作流测试类Test:对异步工作流的实现进行测试
*
* Email:530025983@qq.com
*
* @author MONKEY.D.MENG 2011-04-06
*
*/
public class Test
{
/**
* 测试主函数入口
*
* @param args
*/
public static void main(String[] args) throws RemoteException
{
/* Job数目 */
final int JOB_NUM = 40;
/* 批量Job */
Job[] jobs = new Job[JOB_NUM];
/* 初始化Job队列 */
for (int index = 0; index < JOB_NUM; index += 4)
{
jobs[index] = new JobA();
jobs[index + 1] = new JobB();
jobs[index + 2] = new JobC();
jobs[index + 3] = new JobD();
}
/* 生成Job池,并将Job批量放入Job池中 */
JobPool jobPool = new ListJobPool();
/* 初始化处理器池 */
ExecuterPoolImpl executers = new BaseExecuterPool(5, jobPool);
/* 生成Job池管理者,分配处理器执行Job */
JobPoolManagerImpl jobPoolManager = new BaseJobPoolManager(jobPool,
executers);
/* 通过管理者向Job池中添加Job */
jobPoolManager.putBatch(jobs);
/* 启动处理器 */
jobPoolManager.startup();
try
{
Thread.sleep(5000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
/* 关闭Job池 */
jobPoolManager.shutdown();
}
}
往Job池中放入批量放入新Job...
正在启动处理器...
72+28=100
83*90=173
82/70=152
83-14=97
90+64=154
Job:【ID】=1 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
Job:【ID】=6 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
Job:【ID】=7 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
Job:【ID】=8 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
Job:【ID】=4 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
Job:【ID】=2 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
87-20=107
36/36=72
Job:【ID】=5 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
82*60=142
Job:【ID】=9 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
18+71=89
Job:【ID】=3 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
Job:【ID】=11 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
Job:【ID】=13 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
Job:【ID】=12 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
Job:【ID】=10 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
57/17=74
87*61=148
96+26=122
Job:【ID】=15 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
7*15=22
Job:【ID】=14 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
Job:【ID】=16 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
Job:【ID】=17 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
23-93=116
62-5=67
82/48=130
56+11=67
Job:【ID】=18 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
Job:【ID】=20 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
Job:【ID】=21 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
Job:【ID】=24 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
Job:【ID】=23 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
Job:【ID】=25 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
15-85=100
75/37=112
Job:【ID】=26 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
74+3=77
3/67=70
35-75=110
Job:【ID】=19 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
39*98=137
91-43=134
15*71=86
16+72=88
Job:【ID】=22 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
Job:【ID】=27 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
43/47=90
35*60=95
Job:【ID】=28 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
Job:【ID】=30 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
Job:【ID】=29 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
44-85=129
Job:【ID】=31 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
Job:【ID】=32 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
23*38=61
1/90=91
52+84=136
65+92=157
Job:【ID】=34 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
Job:【ID】=33 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
34-18=52
98*61=159
Job:【ID】=35 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
Job:【ID】=36 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
44-36=80
3/48=51
Job:【ID】=38 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
Job:【ID】=37 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
30*70=100
85+82=167
91/58=149
Job:【ID】=40 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
Job:【ID】=39 【生成时间】:Sat May 14 08:55:34 CST 2011 【开始执行时间】:Sat May 14 08:55:38 CST 2011 【执行完成时间】:Sat May 14 08:55:38 CST 2011
正在停止处理器...
处理器已经全部停止,Job池已经清空...
八、将JobFlow的应用扩展到分布式环境
目前而言,我们已经初步实现了单机环境下的异步工作流执行,我们更希望是将其扩展到一个分布式的环境中,利用多物理处理机异步地执行整个工作流序列。设计的思想是将JobPool所封装的Job队列设置为分布式环境下共享的阻塞队列,如果JobPool为空,则所有处理器均处于阻塞状态,如果有Job存入JobPool,则唤醒等待的处理器进行异步地执行。在编码实现上,利用Java的RMI技术实现JobPool的远程调用。
[java] view plain copy
package com.jobflow.rmi;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import com.jobflow.framework.JobPool;
import com.jobflow.impl.ListJobPool;
/**
* 启动远程Job池对象类RmiJobPool:在服务器端部署远程的Job池对象
*
* Email:530025983@qq.com
*
* @author MONKEY.D.MENG 2011-04-06
*
*/
public class RmiJobPoolInit
{
/**
* 远程Job池对象注册函数入口
*
* @param args
*/
public static void main(String[] args)
{
try
{
/* 注册一个端口号 */
LocateRegistry.createRegistry(8808);
/* 启动一个处理器 */
JobPool jobPool = new ListJobPool();
/* 绑定服务的URL */
Naming.rebind("//localhost:8808/JOB-POOL", jobPool);
System.out.println("Job池已经初始化完成,等待Job的填入与取出...");
}
catch (RemoteException e)
{
e.printStackTrace();
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
}
}
package com.jobflow.rmi;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import com.jobflow.framework.Job;
import com.jobflow.framework.JobPool;
import com.jobflow.test.JobA;
import com.jobflow.test.JobB;
import com.jobflow.test.JobC;
import com.jobflow.test.JobD;
/**
* 生产Job类RmiJobProducer:不断地生产出Job,并填入远程的Job池中
*
* Email:530025983@qq.com
*
* @author MONKEY.D.MENG 2011-04-06
*
*/
public class RmiJobProducer
{
/**
* 生产Job主函数入口
*
* @param args
*/
public static void main(String[] args) throws RemoteException
{
/* Job数目 */
final int JOB_NUM = 40;
/* 批量Job */
Job[] jobs = new Job[JOB_NUM];
/* 初始化Job队列 */
for (int index = 0; index < JOB_NUM; index += 4)
{
jobs[index] = new JobA();
jobs[index + 1] = new JobB();
jobs[index + 2] = new JobC();
jobs[index + 3] = new JobD();
}
try
{
/* 利用RMI获取远程Job池对象,并将Job批量放入Job池中 */
JobPool jobPool = (JobPool) Naming
.lookup("//localhost:8808/JOB-POOL");
/* 通过管理者向Job池中添加Job */
jobPool.putBatch(jobs);
System.out.println("批量作业已经填入远程Job池...");
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
catch (NotBoundException e)
{
e.printStackTrace();
}
}
}
package com.jobflow.rmi;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import com.jobflow.base.BaseExecuterPool;
import com.jobflow.base.BaseJobPoolManager;
import com.jobflow.framework.JobPool;
import com.jobflow.framework.impl.ExecuterPoolImpl;
import com.jobflow.framework.impl.JobPoolManagerImpl;
/**
* 执行Job的处理器类RmiExecuterServer:不断从远程的Job池中取出Job并执行
*
* Email:530025983@qq.com
*
* @author MONKEY.D.MENG 2011-04-06
*
*/
public class RmiExecuterServer
{
/**
* 处理器类入口函数
*
* @param args
*/
public static void main(String[] args) throws RemoteException
{
/* 利用RMI获取远程Job池对象,并将Job批量放入Job池中 */
JobPool jobPool;
try
{
jobPool = (JobPool) Naming.lookup("//localhost:8808/JOB-POOL");
/* 初始化处理器池 */
ExecuterPoolImpl executers = new BaseExecuterPool(5, jobPool);
/* 生成Job池管理者,分配处理器执行Job */
JobPoolManagerImpl jobPoolManager = new BaseJobPoolManager(jobPool,
executers);
/* 启动执行Job的处理器 */
jobPoolManager.startup();
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
catch (NotBoundException e)
{
e.printStackTrace();
}
}
}
Job池已经初始化完成,等待Job的填入与取出...
批量作业已经填入远程Job池...
正在启动处理器...
69+88=157
4-94=98
Job:【ID】=2 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
Job:【ID】=1 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
Job:【ID】=3 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
55*0=55
12/41=53
Job:【ID】=4 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
Job:【ID】=5 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
73+52=125
Job:【ID】=6 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
35-60=95
Job:【ID】=7 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
27*47=74
Job:【ID】=8 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
57/88=145
Job:【ID】=9 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
92+87=179
Job:【ID】=10 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
4-70=74
Job:【ID】=11 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
36*59=95
Job:【ID】=12 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
82/90=172
Job:【ID】=13 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
44+50=94
Job:【ID】=14 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
20-48=68
Job:【ID】=15 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
43*41=84
Job:【ID】=16 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
38/21=59
Job:【ID】=17 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
13+82=95
Job:【ID】=18 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
49-76=125
Job:【ID】=19 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
35*50=85
Job:【ID】=21 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
Job:【ID】=22 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
Job:【ID】=23 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
Job:【ID】=20 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
16+33=49
67*96=163
29/91=120
90-41=131
Job:【ID】=25 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
Job:【ID】=26 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
Job:【ID】=27 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
50+64=114
38*21=59
Job:【ID】=24 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
47-65=112
30/3=33
11/36=47
5-69=74
75*52=127
Job:【ID】=31 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
64+62=126
Job:【ID】=28 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
Job:【ID】=30 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
Job:【ID】=29 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
Job:【ID】=32 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
5/49=54
Job:【ID】=33 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
70+64=134
Job:【ID】=35 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
Job:【ID】=36 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
Job:【ID】=34 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
16*1=17
52/27=79
14-94=108
Job:【ID】=37 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
2+4=6
Job:【ID】=38 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
38-76=114
Job:【ID】=39 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
54*56=110
Job:【ID】=40 【生成时间】:Sat May 14 09:11:05 CST 2011 【开始执行时间】:Sat May 14 09:11:12 CST 2011 【执行完成时间】:Sat May 14 09:11:12 CST 2011
2/12=14
运行的方式:先运行RmiJobPoolInit使JobPool远程对象化,从而适用于分布式环境下并发的访问,再在同一主控节点上运行RmiJobProducer产生若干Job并将这些Job放入到JobPool中。随后,在其他从属节点上运行RmiExecuterServer用于从JobPool中取出Job并执行,从而实现了一个简单的分布式计算框架。
然而,这里会存在一个问题,即如果你在从属节点上先开启RmiExecuterServer,而主控节点后开启RmiJobProducer,则从属节点还是一直处于阻塞状态,并不会从JobPool中取Job执行,很奇怪呢。这里Job的同步的控制并不完备,神马原因呢,暂时还没想通,以后我得找时间好好去调调~
九、总结
很明显可以看出,整个JobFlow的框架的思想精髓的实质即是我们之前所学习的操作系统的两个经典问题:生产者/消费者问题、作业调度问题。JobFlow适用于很多的场合,比如C/S模式的网络收发数据包:网络数据都需要经过打包、发包、解包、执行这四个流程,我们就可以将它们封装成一个一个的Job,丢到一个很大的Job池里面。Client端有多个打包线程不断地将数据打包后丢到Job池中,又有多个发送线程去Job池里取包向Server发送数据;Server端有多个接包线程在接收到Client所传输的数据包后,丢到自己Job池中,再由多个解析线程取Job进行数据包的解析,最后将解析后的Job分类,丢给相应的执行Job池中,再分别由多个执行线程从对应的执行Job池中取Job进行执行。
这里只是举例说明,具体的JobFlow的实现还是会遇到很多技术难点,要考虑各种Executer的同步、内存泄露问题、线程安全问题、各种池化对象的维护与管理。而对于一个框架而言,可扩展性、可复用性和灵活可配置性都是要考虑的问题。
好了,JobFlow的原理和代码实现大概就是如此了。为了验证想法,这个版本JobFlow的代码实现的还是过于简单了些,在最初设计上的考虑,在本版本的代码里并未。更为完善的版本会在后续的时间里更新,请大家持续关注本博客~
---------------------
作者:monkey_d_meng
来源:CSDN
原文:https://blog.csdn.net/monkey_d_meng/article/details/6419246
版权声明:本文为博主原创文章,转载请附上博文链接!