2013-05-23 4 views
5

Мне нужен совет по следующим вопросам:вручную вызвать метод @Scheduled

У меня есть метод @Scheduled сервиса, который имеет fixedDelay пару секунд, в котором он делает сканирование очереди работ и обработку apropriate работы если он найдет. В той же службе у меня есть метод, который помещает работу в рабочую очередь, и я хотел бы, чтобы этот метод немедленно запускал проверку очереди после ее завершения (поскольку я уверен, что теперь будет некоторая работа для сканера) во избежание задержки до запланированных пинков (поскольку это могут быть секунды, а время несколько критично).

Функция «триггер теперь» в подсистеме «Выполнение задачи и подзарядки» была бы идеальной, которая также могла бы сбрасывать fixedDelay после того, как выполнение было инициировано вручную (так как я не хочу, чтобы мое ручное исполнение сталкивалось с запланированным). Примечание: работа в очереди может поступать из внешнего источника, поэтому требуется периодическое сканирование.

Любые советы приветствуются

Edit: Очередь хранится в документе на основе БД, так локальных решений на основе очередей не подходят.

Решение Я не вполне доволен (не очень нравится использование сырьевых потоков) бы что-то вроде этого:

@Service 
public class MyProcessingService implements ProcessingService { 

    Thread worker; 

    @PostCreate 
    public void init() { 
     worker = new Thread() { 
      boolean ready = false; 

      private boolean sleep() { 
       synchronized(this) { 
        if (ready) { 
         ready = false; 
        } else { 
         try { 
          wait(2000); 
         } catch(InterruptedException) { 
          return false; 
         } 
        } 
       } 

       return true; 
      } 

      public void tickle() { 
       synchronized(this) { 
        ready = true; 
        notify(); 
       } 
      } 

      public void run() { 
       while(!interrupted()) { 
        if(!sleep()) continue; 

        scan(); 
       } 
      } 
     } 

     worker.start(); 
    } 

    @PreDestroy 
    public void uninit() { 
     worker.interrup(); 
    } 

    public void addWork(Work work) { 
     db.store(work); 

     worker.tickle(); 
    } 

    public void scan() { 
     List<Work> work = db.getMyWork(); 

     for (Work w : work) { 
      process(); 
     } 
    } 

    public void process(Work work) { 
     // work processing here 
    } 

} 
+0

Пожалуйста, голосование за эту проблему: https://jira.spring.io/browse/SPR-14562, чтобы создать простой способ сделать это. – crm86

ответ

6

Поскольку метод @Scheduled не будет иметь никакой работы делать, если в рабочей очереди нет элементов, то есть если никто не помещает какую-либо работу в очередь между циклами выполнения. В том же примечании, если какой-либо рабочий элемент был вставлен в рабочую очередь (возможно, внешним источником) сразу же после завершения запланированного выполнения, работа не будет отображаться до следующего выполнения.

В этом случае вам нужен потребитель-производитель queue. A queue, в котором один или несколько производителей помещаются в рабочие элементы, а потребитель берет предметы с queue и обрабатывает их. Здесь вы найдете BlockingQueue. Они могут использоваться для решения проблемы потребителя-производителя поточно-безопасным способом.

У вас может быть один Runnable, который выполняет задачи, выполняемые вашим текущим методом @Scheduled.

public class SomeClass { 
     private final BlockingQueue<Work> workQueue = new LinkedBlockingQueue<Work>(); 

    public BlockingQueue<Work> getWorkQueue() { 
     return workQueue; 
    } 

    private final class WorkExecutor implements Runnable { 

     @Override 
     public void run() { 
      while (true) { 
       try { 
        // The call to take() retrieves and removes the head of this 
        // queue, 
        // waiting if necessary until an element becomes available. 
        Work work = workQueue.take(); 
        // do processing 
       } catch (InterruptedException e) { 
        continue; 
       } 
      } 
     } 
    } 

    // The work-producer may be anything, even a @Scheduled method 
    @Scheduled 
    public void createWork() { 
     Work work = new Work(); 
     workQueue.offer(work); 
    } 

} 

И некоторые другие Runnable или другой класс может поставить в графах следующим образом:

public class WorkCreator { 

    @Autowired 
    private SomeClass workerClass; 

    @Override 
    public void run() { 
     // produce work 
     Work work = new Work(); 
     workerClass.getWorkQueue().offer(work); 
    } 

} 

Я думаю, что это правильный путь, чтобы решить эту проблему, вы должны под рукой. Есть несколько вариантов/конфигураций, которые вы можете иметь, просто посмотрите на пакет java.util.concurrent.

Update после вопроса отредактирован

Даже если внешний источник является децибел, он по-прежнему является проблемой производитель-потребитель. Вероятно, вы можете вызвать метод scan() всякий раз, когда вы храните данные в db, а метод scan() может поместить полученные данные из db в BlockingQueue.

Чтобы решить реальную вещь о сбросе fixedDelay

То есть на самом деле не возможно, засыхают с Java, или с Spring, если не обрабатывать диспетчеризации, часть сами. Функциональности trigger-now нет. Если у вас есть доступ к Runnable, который выполняет эту задачу, вы, вероятно, можете сами вызвать метод run(). Но это будет то же самое, что вызывать метод обработки самостоятельно из любого места, и вам действительно не нужен Runnable.

Другой возможный обходной

private Lock queueLock = new ReentrantLock(); 


@Scheduled 
public void findNewWorkAndProcess() { 
    if(!queueLock.tryLock()) { 
     return; 
    } 
    try { 
     doWork(); 
    } finally { 
     queueLock.unlock(); 
    } 
} 

void doWork() { 
    List<Work> work = getWorkFromDb(); 
    // process work 
} 

// To be called when new data is inserted into the db. 
public void newDataInserted() { 
    queueLock.lock(); 
    try { 
     doWork(); 
    } finally { 
     queueLock.unlock(); 
    } 
} 

newDataInserted() вызывается при вставке новых данных. Если запланированное выполнение выполняется, оно будет ждать, пока оно не будет завершено, а затем выполните работу. Вызов lock() здесь блокируется, так как мы знаем, что в базе данных есть некоторая работа, и запланированный вызов мог быть вызван до того, как была вставлена ​​работа. Вызов для фиксации блокировки в findNewWorkAndProcess() в неблокирующем качестве, если блокировка была получена методом newDataInserted, это означает, что запланированный метод не должен выполняться.

Ну, вы можете настроить мелодию по своему усмотрению.

+0

Я отредактировал вопрос, чтобы сделать проблему более понятной. Ваше решение было бы правильным, если бы это была локальная очередь, но очередь была разделена и сохранялась через db. Я хочу избежать как можно большего количества локальных очередей. – m1h4

+0

Я обновил ответ, надеюсь, что это поможет. –

+0

метод newDataInserted будет вызываться из метода обработчика запроса, поэтому было бы плохо, если бы он блокировался до обработки работы. Метод запроса должен хранить запрос на работу в очереди db и возвращать результат отслеживания вызывающему. Я мог бы пометить метод с @Async, но это позволит произвольно выделять произвольные потоки для каждого входящего запроса. – m1h4

Смежные вопросы