I have spring 3.0 app, which connects to WebService. The webservice requests are limited to 1 per second, and I need to fire ~1000 requests with 1 second delay between each.
I'm trying to do it using Spring TaskExecutor and I've found the example here
But how can I set the 1 second delay between each taskExecutor.execute call?
The code from example I'm using:
import org.springframework.core.task.TaskExecutor;
public class TaskExecutorExample {
private class MessagePrinterTask implements Runnable {
private String message;
public MessagePrinterTask(String message) {
this.message = message;
}
public void run() {
System.out.println(message);
}
}
private TaskExecutor taskExecutor;
public TaskExecutorExample(TaskExecutor taskExecutor) {
this.taskExecutor = taskExecutor;
}
public void printMessages() {
for(int i = 0; i < 25; i++) {
taskExecutor.execute(new MessagePrinterTask("Message" + i));
}
}
}
Its called fixedDelay or fixedRate, depending on whant you need exactly
<task:scheduled-tasks scheduler="myScheduler">
<task:scheduled ref="someObject" method="someMethod" fixed-delay="1000"/>
<task:scheduled ref="someObject" method="someOtherMethod" fixed-rate="1000"/>
</task:scheduled-tasks>
or
#Scheduled(fixedDelay=1000)
or
#Scheduled(fixedRate=1000)
It is well documented in the Spring Reference, where you have taken the example from
TaskExecutor is not the correct interface to use for this, it used for fire-and-forget "execute this whenever you can" operations. You should use TaskScheduler instead. This provides methods such as scheduleAtFixedDelay and scheduleAtFixedRate.
Check out the javadoc to read the descriptions of these methods - be careful, it's quite subtle.
Related
I've defined 3 jobs using fixedDelayString=300000 (5 minutes) and I did that 3 these jobs will be executed independently. For that reason, I created an Async implementation. At first, each job worked fine, but in the time they started to delay a lot.
Each execution is about 5seg, but the next execution started to run after 10minutes. And occasionally 15 or 18minutes.
An example could be:
#EnableScheduling
public class AppConfig {
#Async('threadPoolTaskExecutor')
#Scheduled(fixedDelayString=15000)
public void doSomething1() {
// something that should run periodically
}
#Async('threadPoolTaskExecutor')
#Scheduled(fixedDelayString=300000)
public void doSomething2() {
// something that should run periodically
}
#Async('threadPoolTaskExecutor')
#Scheduled(fixedDelayString=300000)
public void doSomething3() {
// this job begins to have interval larger in each execution
}
}
#Configuration
#EnableAsync
public class AsyncConf {
#Bean("threadPoolTaskExecutor")
public TaskExecutor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);
executor.setMaxPoolSize(1000);
executor.setThreadNamePrefix("Async-");
return executor;
}
}
´´´
To mention the fixed delay, it should be fixedDelay instead of fixedDelayString. Check below code:
#Async('threadPoolTaskExecutor')
#Scheduled(fixedDelay = 300000)
public void doSomething3() { ... }
Also you should write #EnableScheduling annotation on your configuration class.
Also note that fixedDelay specifies that job should run next after specified amount of time once execution is completed. If you want to run your jobs at fixed intervals, you should try fixedRate instead of fixedDelay. Check more about scheduling here - https://www.baeldung.com/spring-scheduled-tasks
I am scheduling the spring scheduler with SchedulingConfigurer as follows. However, new traceid is not getting created every time the "ProcessJob" method is getting called.
Even following method always logs with the same traceid.
log.info("Performing task");
What is the issue here? and how do i ensure new traceid everytime this job is triggered.
I have even tried wrapping "processJob" method call inside newSpan as follows: but no luck.
Fix 1: not working:
private void setSchedule() {
future =
taskScheduler.schedule(
() -> {
Span newSpan = tracer.nextSpan().name("newSpan").start();
try (SpanInScope ws = tracer.withSpanInScope(newSpan.start())) {
log.info("Performing task");
taskManager.processJob();
} finally {
newSpan.finish();
}
},
dynamicTrigger);
}
Original class that needs fix:
public class SchedulerConfig
implements SchedulingConfigurer, ApplicationListener<RefreshScopeRefreshedEvent> {
private final DynamicTrigger dynamicTrigger;
private final TaskManager taskManager;
private TaskScheduler taskScheduler;
private ScheduledFuture<?> future;
#Bean(destroyMethod = "shutdown")
public ExecutorService taskExecutor() {
return Executors.newScheduledThreadPool(1);
}
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
taskScheduler = taskRegistrar.getScheduler();
setSchedule();
}
private void setSchedule() {
future =
taskScheduler.schedule(
() -> {z
log.info("Performing task");
taskManager.processJob();
},
dynamicTrigger);
}
#Override
public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
log.info("Rescheduling due to change in cron expression");
future.cancel(false);
setSchedule();
}
The way you start the span is not how you suppose to do it (e.g.: you call start twice). Please check the docs to see how to do it properly: https://docs.spring.io/spring-cloud-sleuth/docs/current/reference/htmlsingle/#using-creating-and-ending-spans
The easiest way to start a new span is using #NewSpan on a method that belongs to a Spring Bean, please see the docs: https://docs.spring.io/spring-cloud-sleuth/docs/current/reference/htmlsingle/#using-annotations-new-spans
For scheduling, I think it is way simpler using #Scheduled, see the docs: https://docs.spring.io/spring-cloud-sleuth/docs/current/reference/htmlsingle/#sleuth-async-scheduled-integration
This is also instrumented out of the box by Sleuth so you don't need to do anything to start a new Span:
#Scheduled(fixedDelay = 1_000)
public void scheduled() {
log.info("Hey, look what I'm doing");
}
If you don't want to use #Scheduled, you can use a TraceableScheduledExecutorService as your ExecutorService, docs: https://docs.spring.io/spring-cloud-sleuth/docs/current/reference/htmlsingle/#sleuth-async-executor-service-integration
I have a question about a general design pattern in EJB. I hava Java EE application (EJBs and Web) and I need a kind of background process which is permanently scanning and processing specific data via JPA.
One solution, I think about, is to implement a #Singleton EJB. In a method annotated with #PostConstruct I can start my process.
#Singleton
#Startup
public class MyUpdateService {
#PostConstruct
void init() {
while(true) {
// scann for new data...
// do the job.....
}
}
}
But is this a recommended pattern? Or is there a better way to run such a class in an EJB Container?
In EJBs there are the other patterns like #TimerService and the new Java EE7 batch processing. But both concepts - I think - are used for finite Processes?
Using EJB TimerService in current project for tasks like periodic data pruning, or back-end data synchronization. It allows not only single time execution, but also interval timers and timers with calendar based schedule.
Smth like:
#Startup
#Singleton
public class SyncTimer {
private static final long HOUR = 60 * 60 * 1000L;
#Resource
private TimerService timerService;
private Timer timer;
#PostConstruct
public void init() {
TimerConfig config = new TimerConfig();
config.setPersistent(false);
timer = timerService.createIntervalTimer(HOUR, HOUR, config);
}
#Timeout
private synchronized void onTimer() {
// every hour action
}
}
As an alternative to the TimerSerivce mentioned by #devmind since Java EE 7 it is possible to use the ManagedScheduledExecutorService:
#Startup
#Singleton
public class Scheduler {
static final long INITIAL_DELAY = 0;
static final long PERIOD = 2;
#Resource
ManagedScheduledExecutorService scheduler;
#PostConstruct
public void init() {
this.scheduler.scheduleAtFixedRate(this::invokePeriodically,
INITIAL_DELAY, PERIOD,
TimeUnit.SECONDS);
}
public void invokePeriodically() {
System.out.println("Don't use sout in prod " + LocalTime.now());
}
}
In difference to the TimerSerivce the ExecutorService can be run in parallel in separate tasks. See also a blog post form Adam Bien.
This question already has answers here:
Scheduling a job with Spring programmatically (with fixedRate set dynamically)
(8 answers)
Closed 4 years ago.
How to change the value of cron dynamically after each execution of the method execute
#Component
#EnableScheduling
public class PendingOrderScheduler {
private final Logger logger;
private final OrderService orderService;
public PendingOrderScheduler(Logger logger, OrderService orderService) {
this.logger = logger;
this.orderService = orderService;
}
#Scheduled(cron = "*/1 * * * * *")
public void execute() {
logger.info(String.format("Executed at %s", new Date()));
this.orderService.updatePendingOrder();
}
}
I don't think you can use #Scheduled annotation if you want to configure the scheduling of job at runtime.You can use custome scheduler as described in spring documentation.
To change the configuration you need to cancel the current scheduling and create new one using Future object of task scheduler.
Unfortunately, changing cron inside cron is impossible.
You can use SchedulingConfigurer to achieve this, but #Scheduled annotation will be removed.
#Component
#EnableScheduling
public class PendingOrderScheduler implements SchedulingConfigurer{
private Map<String,CronTask> cronTasks = new HashMap<>();
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setTaskScheduler(taskScheduler());
this.taskRegistrar = taskRegistrar;
addCronTask();
}
private CronTask addCronTask(){
CronTask task = new CronTask(new Runnable() {
public void run() {
logger.info(String.format("Executed at %s", new Date()));
this.orderService.updatePendingOrder();
}
}, "*/1 * * * * *"); //read it from some variable
cronTasks.put("CRON_KEY", task);
return task;
}
public void changeCron(){ //call it when you whant to change chron
synchronized (PendingOrderScheduler.class) {
List<CronTask> crons = taskRegistrar.getCronTaskList();
taskRegistrar.destroy(); //important, cleanups current scheduled tasks
taskRegistrar.setCronTasksList(new ArrayList<CronTask>());
for (CronTask cron : crons) {
if (!cronTasks.containsValue(cron)) {
taskRegistrar.addCronTask(cron); //copy croned by #Scheduled tasks as is
}
}
addCronTask();
taskRegistrar.afterPropertiesSet(); //rebuild
}
}
}
Also, you can look at Quartz library, which supports cron change.
I have a class with the following function:
public class classA{
...
...
void function_to_be_scheduled(String param){
...
...
}
}
I want to schedule the function using the scheduled-tasks element of the task namespace.
<task:scheduled-tasks>
<task:scheduled ref="beanA" method="function_to_be_scheduled" cron="${cron}"/>
</task:scheduled-tasks>
How do i pass the parameter to the function which i want to schedule?
According to the docs you cant.
Notice that the methods to be scheduled must have void returns and
must not expect any arguments.
The Spring doc about scheduling says:
Notice that the methods to be scheduled must have void returns and must not expect any arguments
Since the parameter comes from the Spring config file you can declare a bean (es beanB which wraps beanA) in the spring file, inject the parameter you need in the bean and the schedule the execution of a method of the bean which knows the parameter (it could be a simple wrapper of your beanA)
You can use TaskScheduler and encapsule your logic with a parameter in Runnable:
#Autowired
private TaskScheduler scheduler;
public void scheduleRules() {
MyTask task = new MyTaskImpl(someParam);
// new CronTrigger
scheduler.scheduleAtFixedRate(task, Duration.ofMinutes(1));
}
I've found that the only way to do this is to have a façade method that is #Scheduled and knows the default value required. The useful side-effect of this is that you can also provide an API through a #Controller to provide manual triggering with a specific parameter - useful if you need to re-run an activity.
#Scheduled(cron = "${myChronSchedule}")
public void generateActivities() {
this.generateActivities(LocalDate.now());
}
public void generateActivities(LocalDate theDate) {
// do the work
...
}
If you don't need the façade to be public, there's no reason why it can't be private and no-one is the wiser.
The Task Scheduler did the trick for me
First create a configuration class called ThreadPoolTaskScheduler class. Find details Here!
Then create a class where the magic happens
#Component
public class ThreadPoolTaskSchedulerExample {
#Autowired
private ThreadPoolTaskScheduler taskScheduler;
class EmailWatch implements Runnable{
private String userEmail;
public EmailWatch(String userEmail){
this.userEmail = userEmail;
}
#Override
public void run() {
System.out.println("This is the email "+ userEmail);
}
}
public void watchEmail(String userEmail) {
//refresh watch every minute
CronTrigger cronTrigger = new CronTrigger("0 * * ? * *");
taskScheduler.schedule(new EmailWatch(userEmail));
}
}