Myślę, że Spring Batch to przesada i nie jest to naprawdę to, czego szukasz. Jest to bardziej przydatne w przypadku przetwarzania wsadowego, np. Zapisywanie wszystkich zamówień do pliku, a następnie przetwarzanie wszystkich naraz, podczas gdy wydaje się, że jest to bardziej asynchroniczne przetwarzanie, w którym chcesz po prostu "stać w kolejce" pracy i przetwarzać ją w ten sposób.
Jeśli tak jest w rzeczywistości, zajrzę do używania modelu pub/sub przy użyciu JMS. Istnieje kilka dostawców JMS, na przykład Apache ActiveMQ lub Pivotal RabitMQ. Zasadniczo Twój OrderService
podzieliłby CSV na jednostki pracy, wepchnął je do kolejki JMS, a ty miałbyś wielu Konsumentów skonfigurowanych do odczytu z Kolejki i wykonania zadania roboczego. Istnieje wiele sposobów na skonfigurowanie tego, ale chciałbym po prostu utworzyć klasę do przechowywania wątków roboczych i uczynić liczbę wątków konfigurowalną. Inne dodatkowe korzyści to:
- Możesz uzewnętrznić kod klienta, a nawet go uruchomić na zupełnie innym sprzęcie, jeśli chcesz.
- MQ jest dość dobrze znanym procesem, a istnieje wiele komercyjnych ofert. Oznacza to, że możesz łatwo napisać swój system przetwarzania zamówień w języku C# za pomocą MQ, aby przenieść wiadomości, lub nawet użyć Spring Batch, jeśli chcesz. Heck, istnieje nawet seria MQ dla hosta, więc możesz zlecić przetwarzanie zamówienia na komputerze mainframe w języku COBOL, jeśli pasuje ci to do gustu.
- To głupio po prostu dodać więcej konsumentów lub producentów. Po prostu zasubskrybuj kolejkę i odejdą!
- W zależności od używanego produktu kolejka zachowuje stan, aby wiadomości nie zostały "utracone". Jeśli wszyscy konsumenci przejdą do trybu offline, kolejka będzie po prostu tworzyć kopie zapasowe wiadomości i przechowywać je do momentu powrotu klientów.
- Kolejki są zazwyczaj bardziej niezawodne. Producent może zejść na dół, a konsumenci nawet nie wzdrygają się. Konsumenci mogą zejść na dół, a producent nawet nie musi tego wiedzieć.
Istnieje jednak kilka wad. Masz teraz dodatkowy punkt awarii.Prawdopodobnie będziesz chciał monitorować głębokości kolejki i będziesz musiał zapewnić wystarczającą ilość miejsca do przechowywania wiadomości podczas buforowania wiadomości. Ponadto, jeśli czas przetwarzania może być problemem, być może trzeba będzie monitorować, jak szybko przetwarzane są rzeczy w kolejce, aby upewnić się, że nie tworzy się zbyt duża kopia zapasowa lub nie łamie żadnych umów SLA, które mogą obowiązywać.
Edycja: Dodawanie przykład ... Gdybym miał gwintowaną klasę, na przykład tak:
public class MyWorkerThread implements Runnable {
private boolean run = true;
public void run() {
while (run) {
// Do work here...
}
// Do any thread cooldown procedures here, like stop listening to the Queue.
}
public void setRunning(boolean runState) {
run = runState;
}
}
Wtedy zacznę wątki użyciu klasy tak:
@Service("MyThreadManagerService")
public class MyThreadManagerServiceImpl implements MyThreadManagerService {
private Thread[] workers;
private int workerPoolSize = 5;
/**
* This gets ran after any constructors and setters, but before anything else
*/
@PostConstruct
private void init() {
workers = new Thread[workerPoolSize];
for (int i=0; i < workerPoolSize; i++) {
workers[i] = new Thread(new MyWorkerThread()); // however you build your worker threads
workers[i].start();
}
}
/**
* This gets ran just before the class is destroyed. You could use this to
* shut down the threads
*/
@PreDestroy
public void dismantle() {
// Tell each worker to stop
for (Thread worker : workers) {
worker.setRunning(false);
}
// Now join with each thread to make sure we give them time to stop gracefully
for (Thread worker : workers) {
worker.join(); // May want to use the one that allows a millis for a timeout
}
}
/**
* Sets the size of the worker pool.
*/
public void setWorkerPoolSize(int newSize) {
workerPoolSize = newSize;
}
}
Teraz masz miłą klasę usług, do której możesz dodać metody monitorowania, restartowania, zatrzymywania itp. Wszystkich wątków roboczych. Zrobiłem to jako @Service
, ponieważ czułem się bardziej odpowiedni niż zwykły @Component
, ale technicznie może to być wszystko tak długo, jak Spring wie, aby odebrać go podczas autowiring. Metoda init()
w klasie usług jest tym, co uruchamia wątki, a dismantle()
jest używany do ich zatrzymania i oczekiwania na zakończenie. Używają adnotacji @PostConstruct
i @PreDestroy
, dzięki czemu możesz nadawać im dowolne nazwy. Prawdopodobnie masz konstruktora na swoim MyWorkerThread
, aby skonfigurować Kolejki i takie. Ponadto, jako zastrzeżenie, wszystko to zostało zapisane z pamięci, więc mogą występować niewielkie problemy z kompilacją lub nazwy metod.
Możliwe, że są już dostępne klasy do tego typu rzeczy, ale ja sam nigdy ich nie widziałem. Czy ktoś wie o lepszym sposobie korzystania z gotowych części, które chciałbym lepiej wykształcić?
Uwaga boczna: adnotacja '@ Service' jest tutaj nadmiarowa. – sp00m
Co się stanie, jeśli proces w tle zakończy się niepowodzeniem? Aka, wysyłam plik i import z jakiegoś powodu nie powiedzie się. Co się wtedy stanie? Jeśli chcesz mieć możliwość ponownego uruchomienia procesu importu, Spring Batch może mieć sens. W przeciwnym razie istnieje wiele innych opcji. –