Spiderman源码分析(二)调度和执行

    这节我们来看看Spiderman是如何调度和执行一个爬取任务的。用户可以自行设置爬取任务的调度策略,也可以只执行一次。Spiderman提供了完善的调度策略接口,下面我们先来看看都有哪些和调度相关的字段:

Spiderman:

         isShutdownNow:是否立刻停止每个site的爬取线程,true:表示主调度线程(Spiderman所在线程)已经等待完毕设定的scheduleTime,由主线程完成每个site爬取的停止,

                                      false则表明由定时器任务来停止site的爬取

         pool:所有site爬取调度线程池,这里的每个task是一个site的爬取主线程

         isSchedule:本次爬取是否为调度执行

         scheduleTime:每次调度允许所有site爬取的时间,到点后所有site爬取将被停止

         scheduleDelay:调度间隔,如果是调度执行,调度时间到达后,间隔这时间再进行下一轮爬取

         scheduleTimes:已经执行的调度次数

        maxScheduleTimes:用户设定的最大调度次数

Site:

       thread:每个site爬取的线程数目

       pool:每个site爬取的线程池

       waitQueue:每个site内部等待爬取的任务队列(这里的每个任务就是一个经过封装的URL)

下面我们从四个方面来分析spiderman的调度执行过程,总体调度转移流程,总体调度策略,每个site内部的调度策略,site内部具体的一个Task的执行。

总体调度转移流程

     当用户通过相关接口设定好调度参数后,由spiderman创建一个外部的线程池pool(线程池的corePoolSize和maxPoolSize是site的个数),然后将每个site封装成一个可运行的线程提交到该线程池,由该线程池负责管理每个site线程的运行,在每个site内部又有一个线程池来存放每个具体的爬取任务,这里的线程池corePoolSize和maxPoolSize为thread变量指定大小。spiderman里的调度在下面函数完成:

	private Spiderman _startup(){
		for (Site site : sites){
			pool.execute(new Spiderman._Executor(site));
			listener.onInfo(Thread.currentThread(), null, "spider tasks of site[" + site.getName() + "] start... ");
		}
		return this;
	}

上面的pool创建如下:

	private void initPool(){
		if (pool == null){
			int size = sites.size();
			if (size == 0)
				throw new RuntimeException("there is no website to fetch...");
			pool = new ThreadPoolExecutor(size, size,
                    60L, TimeUnit.SECONDS,
                    new LinkedBlockingQueue<Runnable>());
			
			listener.onInfo(Thread.currentThread(), null, "init thread pool size->"+size+" success ");
		}
	}

从pool的创建代码可以看出corePoolSize和maxPoolSize被设定为site个数,这样设置可以保证每个site的爬取平等执行。

site由于没有继承Thread类也没有实现Runnable接口,因此不能独立执行,其被封装在一个Spiderman的内部类_Executor中,该类实现了Runnable接口。下面来看看具体的调度策略:


总体调度策略

 先来看一个直观的调度执行用法:

		Spiderman.me()
			.listen(listener)//设置监听器
			.schedule("10s")//调度,爬虫运行10s
			.delay("2s")//每隔 10 + 2 秒后重启爬虫
			.times(3)//调度 3 次
			.startup()//启动
			.blocking();//阻塞直到所有调度完成


上面代码,如果最后不调用blocking,主线程可以继续做其它事情,否则就被block知道所有调度执行完毕.schedule,delay,times都是设定调度参数,具体的调度在startup中完成,下面结合startup代码来分析:

public Spiderman startup() {
		if (isSchedule) <span style="color:#3366ff;">{//为调度执行
          		final Spiderman _this = this;
			timer.schedule(new TimerTask() {//<span style="color:#3366ff;">创建TimerTask
    			  public void run() {
					//限制schedule的次数
 
					if (_this.maxScheduleTimes > 0 && _this.scheduleTimes >= _this.maxScheduleTimes){
						try {
							_this.cancel();
						} catch (Throwable e){
							e.printStackTrace();
							_this.listener.onError(Thread.currentThread(), null, e.toString(), e);
						}
						
						_this.listener.onInfo(Thread.currentThread(), null, "Spiderman has completed and cancel the schedule.");
						
						try {
							_this.listener.onAfterScheduleCancel();
						} catch (Throwable e) {
							e.printStackTrace();
							_this.listener.onError(Thread.currentThread(), null, e.toString(), e);
						}
						
						_this.isSchedule = false;
					} else {//<span style="color:#3366ff;">未达到最大调度次数</span></strong></span>

                
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值