当前位置: 首页 > news >正文

分布式系统中的定时任务全解(二)

为什么80%的码农都做不了架构师?>>>   hot3.png

概述

上一篇分布式系统中的定时任务全解(一)中对定时任务和定时任务的基础使用方式进行了说明。这一小节,把分布式场景下的定时任务进行一个大致的讲解。

什么是分布式场景呢,当单台服务器服务能力不够的时候,就需要更多的服务器进行水平向的扩展,由多台服务器分工(任务量上的水平划分,而非业务线上的垂直划分)的方式来增强服务能力,提供更强的并发请求处理,更短的时间响应。

像第一节说到的定时任务使用场景,大多是一次任务执行仅能有一个服务器在执行,如果是所有服务器都在执行相同的任务,一个是会造成错误,就算不会造成错误,很多服务器在做重复的工作也是极大的浪费。

所以,分布式场景下定时任务要做的一个基本难点就是:怎么让某一个定时任务,在一个触发时点上仅有一台服务器在执行。

更进一步,如果,你的定时任务涉及到很多同类型的数据要处理,比如说要处理100个输入文件,处理方式相同;再比如说你的数据库已经做了分库处理,业务数据被写入到了10个数据库实例中,处理方式相同。那么此时,可以让更多台服务器执行定时任务,每台执行其中的一部分,比如10个输入文件;再比如1个数据库实例中的业务数据。

以上两种场景怎么办呢?第一种很简单,后续会提供三种方式去做:1.设置某一台为任务执行服务器,其他服务器不执行;2.使用quartz的集群功能,实现某一台执行;3.使用当当开源的elastic-job,实现某一台执行。第二种场景只有第一种场景中的第3中方式可以做到。

接下来逐个看一下。

实现分布式的方式

设置某一台为任务执行服务器

这种方式可以采用环境变量的方式来实现,定时任务运行时检查本机的环境变量值是否为可执行,如果是则执行定时任务,如果不是则直接返回。

@Value("${ISTIMERRUNNER}")
private String isTimerRunner;

@Scheduled(cron="0 0 0 * * ? ")
public void task(){
    try {
        if("true".equals(isTimerRunner)){
                //do something.....
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

这里有一个需要注意的事项,如果集群环境下,你使用了脚本部署的方式,而且是类似于作者的方式。也就是先把文件拷贝到一台服务器,启动好后,调用脚本(脚本参见:http://www.voidcn.com/blog/BuquTianya/article/p-5760349.html),逐个部署到其他服务器。那么,你就需要注意一下了。

远程ssh调用startup.sh时,tomcat取不到环境变量,这里需要把startup.sh的顶部进行修改:

#!/bin/sh --login

当然这里还有另外一种方式,就是在tomcat的bin目录中添加一个setenv.sh文件,startup.sh执行时会加载其中的内容。

export ISTIMERRUNNER=true

这种方式有十分明显的缺陷:1.单点,当任务执行节点出现问题时,整个定时任务全部over;2.资源分配不均衡,随着定时任务的增多,任务执行服务器的资源占用压力会越来越大。

当然了,这是在技术能力不够的时候,最简单有效的实现方式。

使用quartz的集群功能

quartz这个老牌的定时任务执行工具,在集群方面也提供了很好的支持。quartz的集群是借助数据库来实现的,所有的服务器实例共享一套数据库表中存储的任务、触发器和调度器信息,实现一个时间点,同一个任务仅有一台服务器在执行。而且提供了负载均衡和failover失败转移功能。

quartz的负载均衡大概是这样的策略:对于需要调度很多任务的调度器,会近似随机的选择服务器执行;对于调度的任务数比较少的(1个或者2个)的触发器,会尽量的在同一台服务器上执行。(可以参见这里:http://www.quartz-scheduler.org/documentation/quartz-2.2.x/tutorials/tutorial-lesson-11.html)

quartz的集群使用也不复杂,接下来一起看一下:

1.导入数据库表

quartz的集群是基于数据库实现的,所以首先要把数据库表结构创建好。创建脚本在quartz的完整下载包里可以找到(官网下载地址:http://www.quartz-scheduler.org/downloads/)。

解压之后,在docs/dbTable目录下可以找到你想要的所有常见数据库类型的创建脚本。

输入图片说明

我使用的是sql数据库,所以使用了tables_mysql_innodb.sql。

这里导入的时候遇到了一个问题,就是创建索引时索引字段过长。这里我采用的方法是把所有的scheduler的长度变成了50,修改之后也就是会限制所有的scheduler的名字在50个字节以内。

2.创建支持集群的scheduler

集群和非集群的配置不同,关键就在于scheduler,集群时需要给scheduler配置数据源、将org.quartz.jobStore.isClustered设置为true、以及配置quartz.properties属性文件。详细的实现方式可以参见:http://sundoctor.iteye.com/blog/486055

使用elastic-job

elastic-job是基于quartz实现的,最大的不同点是elastic-job把做为共享中心的数据库换成了zookeeper。所以要使用elastic-job首先要安装zookeeper。

安装好zookeeper之后,使用elastic-job也不难,它做了很好的spring集成支持,只需要配置注册中心和执行任务的job即可。

以下是一个配置文件的示例:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:reg="http://www.dangdang.com/schema/ddframe/reg" 
    xmlns:job="http://www.dangdang.com/schema/ddframe/job" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.dangdang.com/schema/ddframe/reg http://www.dangdang.com/schema/ddframe/reg/reg.xsd http://www.dangdang.com/schema/ddframe/job http://www.dangdang.com/schema/ddframe/job/job.xsd ">
    <context:component-scan base-package="com.dangdang.example.elasticjob" />
    <context:property-placeholder location="classpath:conf/*.properties" />

    <reg:zookeeper id="regCenter" server-lists="${serverLists}" namespace="${namespace}" base-sleep-time-milliseconds="${baseSleepTimeMilliseconds}" max-sleep-time-milliseconds="${maxSleepTimeMilliseconds}" max-retries="${maxRetries}" nested-port="${nestedPort}" nested-data-dir="${nestedDataDir}" />

    <job:simple id="simpleElasticJob" class="com.dangdang.example.elasticjob.spring.job.SimpleJobDemo" registry-center-ref="regCenter" sharding-total-count="${simpleJob.shardingTotalCount}" cron="${simpleJob.cron}" sharding-item-parameters="${simpleJob.shardingItemParameters}" monitor-execution="${simpleJob.monitorExecution}" monitor-port="${simpleJob.monitorPort}" failover="${simpleJob.failover}" description="${simpleJob.description}" disabled="${simpleJob.disabled}" overwrite="${simpleJob.overwrite}" />
</beans>

更详细的示例可以到github下载:https://github.com/ZhangShufan15/elastic-job

这里简单的说一下elastic-job相对于quartz的优势:

1.使用zookeeper做为协调,更加轻量级,这一点对于使用者来说也是一个困难项,因为无论再小的服务也有一个数据库,所以定时任务也使用数据库,那么用起来省事一点。但使用zookeeper一方面速度快,另一方面是不占用现有数据库的连接和计算资源。

2.支持任务的分片,quartz同一时点,同一任务只能在一台机器上运行,但是elastic-job可以在多台机器上运行,并且能够指定每台服务器上运行的输入分片。比如业务数据在10个数据库,这里总共有5台服务器,那么每台服务器在同一个时点,仅处理其中的2个数据库。做到将可纵向切分的任务,切分给不同的服务器,充分利用资源,加快计算速度。

elastic-job怎么做到的分片,在不同的场景下我们该怎么使用elastic-job,接下来的一节将从源代码的角度进行讲解。

转载于:https://my.oschina.net/dabird/blog/830138

相关文章:

  • 基于Vue2全家桶的移动端AppDEMO实现
  • 曾经的曾经的多么多么的爱一个人。。。
  • 手把手教你写Kconfig---基于tiny4412开发板
  • jquery点击回到页面顶部方法
  • Python 爬虫-下载图片
  • 中文转拼音without CJK
  • Python爬虫之多进程爬取(以58同城二手市场为例)
  • webpack+react项目初体验——记录我的webpack环境配置
  • js-权威指南学习笔记19.2
  • Swift与OC混编
  • JavaWeb之文件上传、下载
  • HttpServletRequest和HttpServletResponse详解
  • shell面试难题
  • restlet入门搭建hello world
  • Vue初探
  • 【402天】跃迁之路——程序员高效学习方法论探索系列(实验阶段159-2018.03.14)...
  • 【node学习】协程
  • Apache的基本使用
  • el-input获取焦点 input输入框为空时高亮 el-input值非法时
  • GDB 调试 Mysql 实战(三)优先队列排序算法中的行记录长度统计是怎么来的(上)...
  • JavaScript服务器推送技术之 WebSocket
  • java第三方包学习之lombok
  • Mysql数据库的条件查询语句
  • php的插入排序,通过双层for循环
  • puppeteer stop redirect 的正确姿势及 net::ERR_FAILED 的解决
  • 初探 Vue 生命周期和钩子函数
  • 从地狱到天堂,Node 回调向 async/await 转变
  • 工程优化暨babel升级小记
  • 欢迎参加第二届中国游戏开发者大会
  • 如何用Ubuntu和Xen来设置Kubernetes?
  • 验证码识别技术——15分钟带你突破各种复杂不定长验证码
  • 用Node EJS写一个爬虫脚本每天定时给心爱的她发一封暖心邮件
  • 由插件封装引出的一丢丢思考
  • 云栖大讲堂Java基础入门(三)- 阿里巴巴Java开发手册介绍
  • 支付宝花15年解决的这个问题,顶得上做出十个支付宝 ...
  • (C#)Windows Shell 外壳编程系列9 - QueryInfo 扩展提示
  • (笔记)Kotlin——Android封装ViewBinding之二 优化
  • (分布式缓存)Redis分片集群
  • (附源码)apringboot计算机专业大学生就业指南 毕业设计061355
  • (附源码)springboot“微印象”在线打印预约系统 毕业设计 061642
  • (七)c52学习之旅-中断
  • (十七)Flask之大型项目目录结构示例【二扣蓝图】
  • (一)认识微服务
  • * 论文笔记 【Wide Deep Learning for Recommender Systems】
  • .NET CLR基本术语
  • .NET Reactor简单使用教程
  • .net 桌面开发 运行一阵子就自动关闭_聊城旋转门家用价格大约是多少,全自动旋转门,期待合作...
  • .NET 自定义中间件 判断是否存在 AllowAnonymousAttribute 特性 来判断是否需要身份验证
  • .Net+SQL Server企业应用性能优化笔记4——精确查找瓶颈
  • .net反混淆脱壳工具de4dot的使用
  • .project文件
  • /dev/sda2 is mounted; will not make a filesystem here!
  • @Import注解详解
  • [ C++ ] STL priority_queue(优先级队列)使用及其底层模拟实现,容器适配器,deque(双端队列)原理了解
  • [.net 面向对象程序设计进阶] (19) 异步(Asynchronous) 使用异步创建快速响应和可伸缩性的应用程序...