本文已参与「新人创作礼」活动,一起开启掘金创作之路。
背景
项目中需要处理定时任务,我们的应用会发布到多台服务器上运行。为了不会并发的处理导致脏数据,通常我们会引入Elastic-Job或者xxl-Job等分布式调度系统来处理。但是这样需要搭建新系统,如果只是简单实现分布式定时任务,我是这样思考实践的。
项目的分布式组件是Nacos+Dubbo。项目启动后会把Dubbo的provider都注册到Nacos中,正好我们也可以注册自己的定时任务服务到Nacos中。
都注册之后就带来了每个task都会运行的窘境,接下来我们可以通过注册中心负载均衡的思维让每次只有一个服务实例会生效,在该服务下线后其他服务实例的定时任务生效。
1. 初始化Nacos命名服务
Maven依赖
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>${nacos.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo-registry-nacos</artifactId>
<version>${dubbo.version}</version>
</dependency>
定义分布式任务服务
- 获取项目配置
- 注册服务到Nacos注册中心
@Component
@Slf4j
public class DistributedTask implements ApplicationContextAware {
/**
* 应用运行端口
*/
@Value("${dubbo.protocol.port}")
private Integer serverPort;
/**
* nacos注册中心地址
*/
@Value("${nacos.server-address}")
private String serverAddress;
/**
* nacos注册中心端口
*/
@Value("${nacos.port}")
private String nacosPort;
/**
* 分布式task服务名
*/
private static final String SERVICENAME="distributedTask";
/**
* Nacos命名服务,此处为静态对象
*/
public static NamingService naming;
/**
* 应用程序上下文,随容器启动
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
try {
if (naming==null) {
//初始化Nacos命名服务
naming = NamingFactory.createNamingService(serverAddress+":"+nacosPort);
//设置服务实例
Instance instance=new Instance();
//获取IP,NetUtils是Dubbo源码里获取IP的
instance.setIp(NetUtils.getLocalHost());
instance.setPort(serverPort);
//负载核心思想,记录当前运行时间戳,写入到服务元数据中
instance.setMetadata(MapUtil.of("timestamp",System.currentTimeMillis()+""));
//注册实例
naming.registerInstance(DistributedTask.SERVICENAME,instance);
}
} catch (NacosException e) {
e.printStackTrace();
System.exit(-1);
}
}
}
接下来我们看看具体定时任务怎么利用Nacos服务的呢?