**高并发、微服务 、性能调优实战案例100讲，所有案例均源于个人工作实战，均配合代码落地**

加我微信：itsoku，所有案例均提供在线答疑。



# 第11节 使用SpringBoot实现动态Job实战

又是非常实用硬核的一节课，大家看完后感觉不错的，帮忙点个赞，多个各位兄弟。

## 功能

- 会创建一个job表
- 可以通过接口，对这些job表进行增删改查
- 支持动态增加、删除、修改、启动、停止Job等
- 系统均无需重启，会自动监控job表的变化，实现job的动态执行

## 用到的技术

- SpringBoot
- MyBatis-Plus
- MySql
- cron表达式（job执行的一种表达式，如`* * * * * *`表示每秒执行一次 ）



## 先看效果

### 系统启动后会自动执行下面的脚本

> 创建job表，插入2条记录

```sql
-- 创建job表
create table if not exists t_job
(
    id          varchar(50) primary key comment 'id，主键',
    name        varchar(100) not null comment 'job名称，可以定义一个有意义的名称',
    cron        varchar(50)  not null comment 'job的执行周期，cron表达式',
    bean_name   varchar(100) not null comment 'job需要执行那个bean，对应spring中bean的名称',
    bean_method varchar(100) not null comment 'job执行的bean的方法',
    status      smallint     not null default 0 comment 'job的状态,0：停止，1：执行中'
);

-- 为了方便测试，清理数据
delete from t_job;
-- 创建2个测试用的job，job1每1秒执行1次，job2每2秒执行一次
insert ignore into t_job values ('1', '第1个测试job', '* * * * * *', 'job1', 'execute', 1);
insert ignore into t_job values ('2', '第2个测试job', '*/2 * * * * *', 'job2', 'execute', 1);
```

### 原理

应用启动后，会自动去job表拉取需要执行的job，进行执行。

并每隔5秒对这个 job 表进行一次监控，监控其有没有发生变化，比如这个表中的 job 发生变化（有新增、删除、修改、暂停、启动等操作）都会被自动监控到，然后便实现了 job 的动态执行。

### 系统中定义了3个测试用的job

```java
com.itsoku.lesson011.test.Job1
com.itsoku.lesson011.test.Job2
com.itsoku.lesson011.test.Job3
```

### 启动应用

```java
com.itsoku.lesson011.Lesson011Application
```

### 可以看到job1和job2在执行了

```
[main] 18:02:17 ：Tomcat started on port(s): 8080 (http) with context path ''
[main] 18:02:17 ：Started Lesson011Application in 4.123 seconds (JVM running for 5.989)
[main] 18:02:17 ：启动 job 成功:{"id":"1","name":"第1个测试job","cron":"* * * * * *","beanName":"job1","beanMethod":"execute"}
[main] 18:02:17 ：启动 job 成功:{"id":"2","name":"第2个测试job","cron":"*/2 * * * * *","beanName":"job2","beanMethod":"execute"}
[taskExecutor-2] 18:02:18 ：job1
[taskExecutor-3] 18:02:18 ：job2
[taskExecutor-4] 18:02:19 ：job1
[taskExecutor-1] 18:02:20 ：job2
[taskExecutor-1] 18:02:20 ：job1
[taskExecutor-3] 18:02:21 ：job1
[taskExecutor-6] 18:02:22 ：job1
```

### 测试各种场景

下面提供了5个测试案例，一个个运行下，给大家看下效果。

```http
### 停止job1
POST http://localhost:8080/jobStop?id=1
Accept: application/json

### 启动job1
POST http://localhost:8080/jobStart?id=1
Accept: application/json

### 删除job1
POST http://localhost:8080/jobDelete?id=1
Accept: application/json

### 变更job2，将其执行周期改为每5秒一次
POST http://localhost:8080/jobUpdate
Accept: application/json
Content-Type: application/json

{
  "id": "2",
  "name": "第2个job",
  "cron": "*/5 * * * * *",
  "beanName": "job2",
  "beanMethod": "execute",
  "status": 1
}


### 添加一个job3，每秒执行一次
POST http://localhost:8080/jobCreate
Content-Type: application/json
Accept: application/json

{
  "name": "第3个job",
  "cron": "* * * * * *",
  "beanName": "job3",
  "beanMethod": "execute",
  "status": 1
}
```



## 源码解析

### JobController：job对外暴露的接口

提供对job表的增删改查、job的暂停、启动等接口



### ThreadPoolTaskScheduler：Spring 框架中用于任务调度的组件

系统中最终job的执行，就是调用`ThreadPoolTaskScheduler`这个类的一些方法去执行的，稍后带大家看下源码。

```java
@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
    ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
    //线程池大小
    threadPoolTaskScheduler.setPoolSize(100);
    //线程名称前缀
    threadPoolTaskScheduler.setThreadNamePrefix("taskExecutor-");
    //等待时长
    threadPoolTaskScheduler.setAwaitTerminationSeconds(60);
    //关闭任务线程时是否等待当前被调度的任务完成
    threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true);
    return threadPoolTaskScheduler;
}
```



### SpringJobRunManager：动态job执行的的核心代码

这个类是自己写的，内部会监控job表变化，然后动态执行job，目前系统默认是5秒监控一次。



## 扩展

### 如何让job支持集群？

目前这个job是单机版本的，可能有朋友会问，线上会是集群的方式执行，那怎么办？这里给2个方案

1. 可以在配置文件中加个开关，用开关配置，是否需要这台机器执行，这个方案可能不太友好
2. 对每个job加分布式锁，加锁成功才去执行

### 让job支持其他方式调用

案例中job是通过beanName、beanMethod结合反射去调用的，不够灵活。

大家可以对这个进行改造，比如支持http接口的方式去调用job或者其他更多方式，大家自由扩展，这样你开发出来的这个job就是比较通用的了，可以直接当成公司的一个job平台去用，会让领导对你刮目相看，同时也提升了技术，双赢。



##源码

在高并发系列实战案例的 lesson011 模块中，如果找不到的，留言获取，感谢大家。



# 高并发 & 微服务 & 性能调优实战案例100讲

## 已更新 11 节课

<span style="font-weight:bold; color:red">目前整个课程59块钱，一杯咖啡的价格，还没下手的朋友，赶紧了，马上要涨价了</span>。

```java
1. 分片上传实战
2. 通用并发处理工具类实战
3. 实现一个好用接口性能压测工具类
4. 超卖问题的4种解决方案，也是防止并发修改数据出错的通用方案
5. Semaphore实现接口限流实战
6. 并行查询，优化接口响应速度实战
7. 接口性能优化之大事务优化
8. 通用的Excel动态导出功能实战
9. 手写线程池管理器，管理&监控所有线程池
10. 动态线程池
11. 使用SpringBoot实现动态Job实战
```



## 课程部分大纲，连载中。。。。

以下课程均来源于个人多年的实战，均提供原理讲解 && 源码落地

<span style="font-weight:bold; color:red">目前整个课程59块钱，一杯咖啡的价格，还没下手的朋友，赶紧了，马上要涨价了</span>。

```java
1. 分片上传实战
2. 通用并发处理工具类实战
3. 实现一个好用接口性能压测工具类
4. 超卖问题的4种解决方案，也是防止并发修改数据出错的通用方案
5. Semaphore实现接口限流实战
6. 并行查询，优化接口响应速度实战
7. 接口性能优化之大事务优化
8. 通用的Excel动态导出功能实战
9. 手写线程池管理器，管理&监控所有线程池
10. 动态线程池
11. 使用SpringBoot实现动态Job实战
12. 通用的幂等性工具类实战
13. 接口返回值通用设计
14. 接口太多，各种dto、vo不计其数，如何命名？
15. 一个业务太复杂了，方法太多，如何传参？
16. 如何统计接口耗时？
17. AOP实战接口日志打印功能
18. AOP实现业务操作日志记录功能
19. AOP实现MyBatis分页功能
20. SpringBoot读写分离实战
21. MQ专题：事务消息实战（防止消息丢失）
22. MQ专题：消息幂等消费通用方案实战
23. MQ专题：延迟消息通用方案实战
24. MQ专题：顺序消息通用方案实战
25. MQ专题：消息积压问题
26. 分布式事务：使用事务消息实现事务最终一致性
27. 分布式事务：通用的TCC分布式事务生产级代码落地实战
28. 分布式锁案例实战
29. 微服务中如何传递上下文？实战
30. 微服务链路日志追踪实战（原理&代码落地）
31. SpringBoot实现租户数据隔离
32. MyBatis进阶：封装MyBatis，实现通用的无SQL版CRUD功能，架构师必备
33. MyBatis进阶：自己实现通用分表功能，架构师必备
34. MyBatis进阶：实现多租户隔离ORM框架
35. SpringBoot中实现自动监听PO的变化，自动生成表结构
36. 分布式专题：其他实战课程等
37. 性能调优：如何排查死锁？
38. 性能调优：如何排查内存溢出？
39. 性能调优：CPU被打满，如何排查？
40. 性能调优：生产代码没生效，如何定位？
41. 性能调优：接口太慢，如何定位？
42. 性能调优：如何查看生产上接口的入参和返回值？
43. 性能调优：远程debug
44. 生产上出现了各种故障，如何定位？
45. 其他等各种实战案例。。。
。。。
```

