background
It’s another day carrying a brick.
In the process of data relocation, it is found that some data in the original table has foreign key dependence. But the current migration project is based on multi-threaded, multi-task concurrent operations.
At the same time, each task is also based on polymorphic design considerations
Based on the current project architecture, when a project runs, all tasks run simultaneously (using schedule). And it is a single application (there are no multiple nodes running). Therefore, the listener in Spring is used for improvement, and the whole project is not completely transformed in accordance with the mode of responsibility chain (the cost of transformation is still a little high, I think), but handled based on the observer mode.
Introduction to responsibility chain
To avoid coupling request senders with multiple request handlers, all request handlers are linked in a chain by remembering the reference of the previous object to the next object; When a request occurs, it can be passed along the chain until an object processes it.
The figure above can simply describe the whole process of the responsibility chain, one by one (it can be understood as the series connection in the circuit).
transform
Because there are corresponding data dependencies in the database, we need to transform the current project from parallel to serial.
Transformation process
- There will be partial task data dependent interface implementations
ApplicationListener
Interfaces and definitions are the sameApplicationEvent
Event entity.
eg:
@Component
public class BRunner implements IRunner.ApplicationListener<NoticeEvent> {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@Override
public void run(a) {
System.out.println("this is BRunner"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
try {
Thread.sleep(10 _000);
} catch (InterruptedException e) {
e.printStackTrace();
}
NoticeEvent noticeEvent = new NoticeEvent(this."C");
applicationEventPublisher.publishEvent(noticeEvent);
}
@Override
@Async
public void onApplicationEvent(NoticeEvent noticeEvent) {
if ("B".equalsIgnoreCase(noticeEvent.getMessage())){
this.run(); }}}Copy the code
public class NoticeEvent extends ApplicationEvent {
private String message;
public NoticeEvent(Object source, String message) {
super(source);
this.message = message;
}
public NoticeEvent(Object source) {
super(source);
}
public String getMessage(a) {
returnmessage; }}Copy the code
- Publish message transformation as data source body.
eg:
@Component
public class ARunner implements IRunner {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@Override
public void run(a) {
System.out.println("this is ARunner"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
try {
Thread.sleep(10 _000);
} catch (InterruptedException e) {
e.printStackTrace();
}
NoticeEvent noticeEvent = new NoticeEvent(this."B"); applicationEventPublisher.publishEvent(noticeEvent); }}Copy the code
- Because the project started with the use of
schedule
Perform multiple executionstask
Mission. We need to put"Consumer"
Part of it is removed from the startup (there are several ways to remove it, but I will briefly mention the simplest one here).- Maintain a static collection to store what needs to be culled
task
The name that is loaded when the container is initialized and checked at execution time to see if it is in the collection. - using
instanceof
To determine whether the current task belongsApplicationListener
, belongs to the elimination of the execution plan.
- Maintain a static collection to store what needs to be culled
improved
Through the above process, we can achieve what we need (but it doesn’t work right, it’s really serial, there are parallel tasks). Finding that all the tasks really became a chain of responsibility and did not achieve what I wanted, I went into the source code to find the problem and fix it.
Improvement in
- Start class increment
@EnableAsync
. onApplicationEvent
Methodologically add@Async
.
why
Discover that the listener receives the message and decides whether to execute asynchronously
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType ! =null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
for(ApplicationListener<? > listener : getApplicationListeners(event, type)) {if(executor ! =null) {
executor.execute(() -> invokeListener(listener, event));
}
else{ invokeListener(listener, event); }}}Copy the code
** Because there was no asynchronous execution, the first version was executed in serial mode. Details about the code by org. Springframework. Context. Event. SimpleApplicationEventMulticaster view.
At the end
These are the least expensive changes I can make to get what I need done. Of course, the whole project can be completed based on the chain of responsibility, but it also needs many changes. At least for now, it’s the least expensive. Big guys have other good ideas to share 😝.