Docker-Compose部署xxl-job-admin
最近在探索微服务想做一个分布式任务调度中心,发现用之前.net的Quartz不太行
Quartz作为开源作业调度中的佼佼者,是作业调度的首选。但是集群环境中Quartz采用API的方式对任务进行管理,从而可以避免上述问题,但是同样存在以下问题:
问题一:调用API的的方式操作任务,不人性化;
问题二:需要持久化业务QuartzJobBean到底层数据表中,系统侵入性相当严重。
问题三:调度逻辑和QuartzJobBean耦合在同一个项目中,这将导致一个问题,在调度任务数量逐渐增多,同时调度任务逻辑逐渐加重的情况下,此时调度系统的性能将大大受限于业务;
问题四:quartz底层以“抢占式”获取DB锁并由抢占成功节点负责运行任务,会导致节点负载悬殊非常大;而XXL-JOB通过执行器实现“协同分配式”运行任务,充分发挥集群优势,负载各节点均衡。
于是就查询了一下目前主流的分布式调度中心
于是我便选择使用.net注入 xxl-job 的方式进入任务调度
先使用docker-compose搭建服务器环境
version: "3" services: xxl-job-admin: restart: always # docker 镜像 image: xuxueli/xxl-job-admin:2.2.0 # 容器名称 container_name: xxl-job-admin volumes: # 日志目录映射到主机目录 - ./data/logs:/data/applogs ports: # 端口映射 - "8800:8800" environment: # 设置启动参数 PARAMS: ' --server.port=8800 --server.servlet.context-path=/xxl-job-admin --spring.datasource.url=jdbc:mysql://xxx.xxx.xxx.xx:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai --spring.datasource.username=root --spring.datasource.password=hzs888 --xxl.job.accessToken=onelovehzs888' #代码里面需要指定的token
在执行之前因为xxl-job需要做数据持久化,所以这边需要先创建一个对应的数据库并进行初始化
sql文件 自带数据库创建脚本
#cat sss.sql # # XXL-JOB v2.2.0 # Copyright (c) 2015-present, xuxueli. CREATE database if NOT EXISTS `xxl_job` default character set utf8mb4 collate utf8mb4_unicode_ci; use `xxl_job`; SET NAMES utf8mb4; CREATE TABLE `xxl_job_info` ( `id` int(11) NOT NULL AUTO_INCREMENT, `job_group` int(11) NOT NULL COMMENT '执行器主键ID', `job_cron` varchar(128) NOT NULL COMMENT '任务执行CRON', `job_desc` varchar(255) NOT NULL, `add_time` datetime DEFAULT NULL, `update_time` datetime DEFAULT NULL, `author` varchar(64) DEFAULT NULL COMMENT '作者', `alarm_email` varchar(255) DEFAULT NULL COMMENT '报警邮件', `executor_route_strategy` varchar(50) DEFAULT NULL COMMENT '执行器路由策略', `executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler', `executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数', `executor_block_strategy` varchar(50) DEFAULT NULL COMMENT '阻塞处理策略', `executor_timeout` int(11) NOT NULL DEFAULT '0' COMMENT '任务执行超时时间,单位秒', `executor_fail_retry_count` int(11) NOT NULL DEFAULT '0' COMMENT '失败重试次数', `glue_type` varchar(50) NOT NULL COMMENT 'GLUE类型', `glue_source` mediumtext COMMENT 'GLUE源代码', `glue_remark` varchar(128) DEFAULT NULL COMMENT 'GLUE备注', `glue_updatetime` datetime DEFAULT NULL COMMENT 'GLUE更新时间', `child_jobid` varchar(255) DEFAULT NULL COMMENT '子任务ID,多个逗号分隔', `trigger_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '调度状态:0-停止,1-运行', `trigger_last_time` bigint(13) NOT NULL DEFAULT '0' COMMENT '上次调度时间', `trigger_next_time` bigint(13) NOT NULL DEFAULT '0' COMMENT '下次调度时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `xxl_job_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `job_group` int(11) NOT NULL COMMENT '执行器主键ID', `job_id` int(11) NOT NULL COMMENT '任务,主键ID', `executor_address` varchar(255) DEFAULT NULL COMMENT '执行器地址,本次执行的地址', `executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler', `executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数', `executor_sharding_param` varchar(20) DEFAULT NULL COMMENT '执行器任务分片参数,格式如 1/2', `executor_fail_retry_count` int(11) NOT NULL DEFAULT '0' COMMENT '失败重试次数', `trigger_time` datetime DEFAULT NULL COMMENT '调度-时间', `trigger_code` int(11) NOT NULL COMMENT '调度-结果', `trigger_msg` text COMMENT '调度-日志', `handle_time` datetime DEFAULT NULL COMMENT '执行-时间', `handle_code` int(11) NOT NULL COMMENT '执行-状态', `handle_msg` text COMMENT '执行-日志', `alarm_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '告警状态:0-默认、1-无需告警、2-告警成功、3-告警失败', PRIMARY KEY (`id`), KEY `I_trigger_time` (`trigger_time`), KEY `I_handle_code` (`handle_code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `xxl_job_log_report` ( `id` int(11) NOT NULL AUTO_INCREMENT, `trigger_day` datetime DEFAULT NULL COMMENT '调度-时间', `running_count` int(11) NOT NULL DEFAULT '0' COMMENT '运行中-日志数量', `suc_count` int(11) NOT NULL DEFAULT '0' COMMENT '执行成功-日志数量', `fail_count` int(11) NOT NULL DEFAULT '0' COMMENT '执行失败-日志数量', PRIMARY KEY (`id`), UNIQUE KEY `i_trigger_day` (`trigger_day`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `xxl_job_logglue` ( `id` int(11) NOT NULL AUTO_INCREMENT, `job_id` int(11) NOT NULL COMMENT '任务,主键ID', `glue_type` varchar(50) DEFAULT NULL COMMENT 'GLUE类型', `glue_source` mediumtext COMMENT 'GLUE源代码', `glue_remark` varchar(128) NOT NULL COMMENT 'GLUE备注', `add_time` datetime DEFAULT NULL, `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `xxl_job_registry` ( `id` int(11) NOT NULL AUTO_INCREMENT, `registry_group` varchar(50) NOT NULL, `registry_key` varchar(255) NOT NULL, `registry_value` varchar(255) NOT NULL, `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), KEY `i_g_k_v` (`registry_group`,`registry_key`,`registry_value`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `xxl_job_group` ( `id` int(11) NOT NULL AUTO_INCREMENT, `app_name` varchar(64) NOT NULL COMMENT '执行器AppName', `title` varchar(12) NOT NULL COMMENT '执行器名称', `address_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '执行器地址类型:0=自动注册、1=手动录入', `address_list` varchar(512) DEFAULT NULL COMMENT '执行器地址列表,多地址逗号分隔', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `xxl_job_user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL COMMENT '账号', `password` varchar(50) NOT NULL COMMENT '密码', `role` tinyint(4) NOT NULL COMMENT '角色:0-普通用户、1-管理员', `permission` varchar(255) DEFAULT NULL COMMENT '权限:执行器ID列表,多个逗号分割', PRIMARY KEY (`id`), UNIQUE KEY `i_username` (`username`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `xxl_job_lock` ( `lock_name` varchar(50) NOT NULL COMMENT '锁名称', PRIMARY KEY (`lock_name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; INSERT INTO `xxl_job_group`(`id`, `app_name`, `title`, `address_type`, `address_list`) VALUES (1, 'xxl-job-executor-sample', '示例执行器', 0, NULL); INSERT INTO `xxl_job_info`(`id`, `job_group`, `job_cron`, `job_desc`, `add_time`, `update_time`, `author`, `alarm_email`, `executor_route_strategy`, `executor_handler`, `executor_param`, `executor_block_strategy`, `executor_timeout`, `executor_fail_retry_count`, `glue_type`, `glue_source`, `glue_remark`, `glue_updatetime`, `child_jobid`) VALUES (1, 1, '0 0 0 * * ? *', '测试任务1', '2018-11-03 22:21:31', '2018-11-03 22:21:31', 'XXL', '', 'FIRST', 'demoJobHandler', '', 'SERIAL_EXECUTION', 0, 0, 'BEAN', '', 'GLUE代码初始化', '2018-11-03 22:21:31', ''); INSERT INTO `xxl_job_user`(`id`, `username`, `password`, `role`, `permission`) VALUES (1, 'admin', 'e10adc3949ba59abbe56e057f20f883e', 1, NULL); INSERT INTO `xxl_job_lock` ( `lock_name`) VALUES ( 'schedule_lock'); commit;
程序正常运行之后 输入
http://ip:8800/xxl-job-admin/
进行访问 admin/123456 初始账户密码
然后就会得到这么一个页面
接下来使用.netcore把api注入到xxl-job上然后经行配置执行计划
1.先安装DotXxlJob.Core
"XxlJob": { "AccessToken": "", "AdminAddresses": "http://xxx.xx.xx.xx:8800/xxl-job-admin", "AppName": "xxljob-VipUser", "AutoRegistry": true, //"CallBackInterval =, //"LogPath =, "SpecialBindUrl": "xxx.xx.xx.xx", "LogRetentionDays": 30, "Port": 5001, "SpecialBindAddress": "xxx.xxx.xxx.xx:5001" }
然后依赖注入一下
#region xxl-job builder.Services.Configure<XxlJobExecutorOptions>(builder.Configuration.GetSection("XxlJob")); builder.Services.AddXxlJobExecutor(builder.Configuration); builder.Services.AddSingleton<IJobHandler, DemoJobHandler>(); // 添加自定义的jobHandler builder.Services.AddAutoRegistry();// 自动注册\ #endregion
var app = builder.Build(); //执行调用cousul注册到服务 app.Services.GetService<IConsulRegister>()!.ConsulRegistAsync(); //consul的心跳检查 app.UseHealthCheckMiddleware(); //启用XxlExecutor app.UseXxlJobExecutor();
然后实现 UseXxlJobExecutor
namespace OneLove.VipUser.Interface.Middleware { public static class ApplicationBuilderExtensions { public static IApplicationBuilder UseXxlJobExecutor(this IApplicationBuilder @this) { return @this.UseMiddleware<XxlJobExecutorMiddleware>(); } } }
实现XxlJobExecutorMiddleware
using DotXxlJob.Core; namespace OneLove.VipUser.Interface.Middleware { public class XxlJobExecutorMiddleware { private readonly IServiceProvider _provider; private readonly RequestDelegate _next; private readonly XxlRestfulServiceHandler _rpcService; public XxlJobExecutorMiddleware(IServiceProvider provider, RequestDelegate next) { this._provider = provider; this._next = next; this._rpcService = _provider.GetRequiredService<XxlRestfulServiceHandler>(); } public async Task Invoke(HttpContext context) { string contentType = context.Request.ContentType; if ("POST".Equals(context.Request.Method, StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(contentType) && contentType.ToLower().StartsWith("application/json")) { await _rpcService.HandlerAsync(context.Request, context.Response); return; } await _next.Invoke(context); } } }
然后实现DemoJobHandler
using DotXxlJob.Core; using DotXxlJob.Core.Model; using OneLove.Logs.Extension.IService; namespace OneLove.VipUser.Interface.Expand { [JobHandler("demoJobHandler")] public class DemoJobHandler : AbstractJobHandler { private readonly ILogUtility _logUtility1; public DemoJobHandler(ILogUtility logUtility) { _logUtility1 = logUtility; } public override Task<ReturnT> Execute(JobExecuteContext context) { context.JobLogger.Log("receive demo job handler,parameter:{0}", context.JobParameter); _logUtility1.Info(Guid.NewGuid(), Thread.CurrentThread.ManagedThreadId.ToString(), Logs.Extension.Enum.StatusCode.OK, "Execute", "测试定时运行任务"); return Task.FromResult(ReturnT.SUCCESS); } } }
ok 现在开始演示
appname要与配置文件的appname一致
然后目前使用自动注入的方式还有点问题,先放放,先手动录入ip
然后添加任务管理
然后先执行一次 看一下是否正常
然后查看日志 正常执行