This is the sixth day of my participation in the August More text Challenge. For details, see: August More Text Challenge

preface

This function point needs to solve the problem of using WebSocket to complete the batch generation page progress bar loading and display.

The difficulties of this time are:

  • 1. Multithreading is used to optimize the batch generation under big data
  • 2. Push the progress bar to the front end through WebSocket
  • 3. The front end subscribes and displays the progress bar

This page is a simple version, the follow-up will continue to optimize, but the function has been basically implemented.

1. Concurrent data processing

This is where asynchronous processing is required when the back-end interface is invoked. The response is returned to the front end and the business logic is processed asynchronously. The next progress bar task is left to webSocket to listen for processing.

For asynchronous processing, we need to use the thread pool to process the task, but there is a problem, we need to wait for all threads to finish the execution of the final action, to update the rest of the list to the database. This requires determining whether all threads are finished. Since the main thread executes from top to bottom, it doesn’t have to wait for the result of your asynchronous execution to go down, so this one has to wait.

1.1 the thread pool

Based on the above analysis, the code logic looks like this, which is handled through a thread pool

New Thread(() -> {for (DeviceInfo DeviceInfo: DeviceInfos) {pool.execute(new Runnable() {@override public void run() {// 2, execute business logic}).start(); / / 3 and returns the response return ResponseData. SuccessResponse ();Copy the code

1.2 How to calculate the percentage

At this point, as a rule of thumb, when dealing with a number concurrently, we need to use the atomic class,

You can define an atomic class IC outside of the loop, and then align it inside the loop to increment and divide the total

DecimalFormat df = new DecimalFormat(); df.setMaximumFractionDigits(2); / / set to retain a few decimal places df. SetMinimumFractionDigits (2); AtomicInteger ai = new AtomicInteger(0); String result = df.format(ai.incrementandget () * 100.00 / amout);Copy the code

1.3 How Do I Wait until All Threads Are Executed

After all threads finish executing, we need to update the remaining data to the database, and the common methods of waiting for the end of the thread execution are as follows:

1.3.1 isTerminated() through the pool method

Take the following example:

public class Test { public static void main(String args[]) throws InterruptedException { ExecutorService exe = Executors.newFixedThreadPool(50); for (int i = 1; i <= 5; i++) { exe.execute(new SubThread(i)); } exe.shutdown(); While (true) {if (exe.isterminated ()) {system.out.println (" It's over! "); ); break; } Thread.sleep(200); }}Copy the code

When the exe.shutdown() method is called, the thread pool does not receive any new tasks. The thread pool does not exit immediately until all the tasks in the thread pool have been processed. While (true) listens to the isTerminated method to see if all threads in the pool have finished executing, and then executes the final logic.

In theory, I could use this to implement this function, but after a wave of testing, I found that it could not handle my business, so I didn’t use it.

CyclicBarrier

It was only after use that it was discovered that it was literally a recyclable barrier. All it has to do is get a group of threads to block when they reach a barrier, until the last thread reaches the barrier, then do the next thing, then clear zero, and continue to the next barrier. Its internal counters can be re-executed.

Therefore, it is streamable, entering the library every time a thread executes to a barrier. Theoretically possible, but not to overwhelm the database.

1.3.3 CountDownLatch atresia

It is a synchronization utility class that coordinates synchronization between multiple threads. The function of this method is that when some operation is completed, the operation of the current thread can only continue until all other threads have completed their operations

In fact, it is a counter, each thread comes once to subtract the total value by one, and at the end of the execution of the final logic at 0.

Since our total value is available, we can create a counter of the same size with the following code:

CountDownLatch = new CountDownLatch(amout); for (DeviceInfo deviceInfo : DeviceInfos) {pool.execute(new Runnable() {@override public void run() {countdownlate.countdown (); }}); } try {// 3, wait until the counter is 0, execute the subsequent code countdownlatch.await (); } catch (InterruptedException e) { e.printStackTrace(); } // 4, insert remaining list if (list.size()! = 0) { deviceInfoMapper.updateByBatch(list); list.clear(); }Copy the code

2. Websocket usage

This method is called directly by the WebSocket block, and is locally encapsulated using SimpMessagingTemplate. Interested users can learn how to integrate.

    @PostMapping("/sendMsg")
    public Boolean sendMsg(@RequestBody SocketDTO socketDTO) {
        if (checkCertificate(socketDTO.getPassword())) {
            log.error("socket account or password Incorrect -> password = {}", socketDTO.getPassword());
            return false;
        }
        simpMessagingTemplate.convertAndSend(socketDTO.getTopic(), socketDTO.getData());
        log.debug("socket send success -> topic = {} , data = {}", socketDTO.getTopic(), JSONObject.toJSONString(socketDTO.getData()));
        return true;
    }
Copy the code

Instead, all we need to do is call webSocket to send the percentage of messages to the front end

   socketApi.sendMsg(SocketDTO
        .builder()
        .password(configComponent.getPassword())
        .topic(ElectricityStationConst.DEVICE_BATCH_PRODUCE + sysId)
        .data(result)
        .build()
        );
Copy the code

Third, front-end subscription display percentage

<el-progress :text-inside="true" :stroke-width="26" :percentage="processvalue" v-show="processshow" ></el-progress> mounted() { this.init() this.stomp() }, methods:{ stomp(){ this.$bus.on(TopicConst.DEVICE_BATCH_PRODUCE+StoreService.getStationId(),data =>{ this.$data.processshow=true this.$data.processvalue=parseInt(data) if(this.$data.processvalue==100){ This.$message. Success (" batch printing completed "); This. $data. Processshow = false}}) / / mass production qr code relationship productBatch (row) {the console. The log (StoreService. GetStationId ()) This $API. The req ("/am/device/img/produceBatch ", the row, res = > {this. $message. Success (" mass production of qr code, please wait..." ) this.$stomp.sub(TopicConst.DEVICE_BATCH_PRODUCE+StoreService.getStationId(),data =>{ this.$bus.emit(TopicConst.DEVICE_BATCH_PRODUCE+StoreService.getStationId(),data) }) this.$data.loading = false; this.$data.processshow=true; },res=>{ this.$message.error(res.msg) }) }, },Copy the code

However, it seems that vue’s progress bar can only show integers and cannot pass decimals, otherwise the console will get a lot of alerts, so HERE I convert the values to integers again.

Finally, this little feature was implemented, and the process really grew a lot.