用JAVA实现一套背压机制

 更新时间:2023年06月30日 08:47:27   作者:hwp0710  
背压依我的理解来说,是指订阅者能和发布者交互,可以调节发布者发布数据的速率,解决把订阅者压垮的问题,这篇文章主要介绍了用JAVA自己实现一套背压机制,需要的朋友可以参考下

Reactive Streams:一种支持背压的异步数据流处理标准,主流实现有RxJava和Reactor,Spring WebFlux默认集成的是Reactor。

Reactive Streams主要解决背压(back-pressure)问题。当传入的任务速率大于系统处理能力时,数据处理将会对未处理数据产生一个缓冲区。

背压依我的理解来说,是指订阅者能和发布者交互(通过代码里面的调用request和cancel方法交互),可以调节发布者发布数据的速率,解决把订阅者压垮的问题。关键在于上面例子里面的订阅关系Subscription这个接口,他有request和cancel 2个方法,用于通知发布者需要数据和通知发布者不再接受数据。

我们重点理解背压在jdk9里面是如何实现的。关键在于发布者Publisher的实现类SubmissionPublisher的submit方法是block方法。订阅者会有一个缓冲池,默认为Flow.defaultBufferSize() = 256。当订阅者的缓冲池满了之后,发布者调用submit方法发布数据就会被阻塞,发布者就会停(慢)下来;订阅者消费了数据之后(调用Subscription.request方法),缓冲池有位置了,submit方法就会继续执行下去,就是通过这样的机制,实现了调节发布者发布数据的速率,消费得快,生成就快,消费得慢,发布者就会被阻塞,当然就会慢下来了。

单线程版本:

一个生产者,一个消费者

import lombok.SneakyThrows;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class BackpressureExample {
    public static void main(String[] args) throws InterruptedException {
        BackpressureSubscriber subscriber = new BackpressureSubscriber();
        BackpressurePublisher publisher = new BackpressurePublisher(subscriber);
        publisher.start();
        subscriber.start();
        // 为了演示效果,这里让主线程休眠一段时间
        Thread.sleep(50000);
        publisher.stop();
        subscriber.stop();
    }
    @SneakyThrows
    public static void processDataLogic(List<Integer> batch) {
        //模拟任务执行
        int r = new Random().nextInt(3000);
        Thread.sleep(r);
        System.out.println(Thread.currentThread().getName() + ",Received batch: " + batch + ",sleep ms = " + r);
    }
    static class BackpressurePublisher {
        private final BackpressureSubscriber subscriber;
        private volatile boolean running;
        public BackpressurePublisher(BackpressureSubscriber subscriber) {
            this.subscriber = subscriber;
            this.running = true;
        }
        public void start() {
            Thread thread = new Thread(() -> {
                int item = 1;
                while (running) {
                    List<Integer> batch = new ArrayList<>();
                    for (int i = 0; i < 5; i++) {
                        System.out.println(Thread.currentThread().getName() + "-----produce data = " + item);
                        batch.add(item++);
                    }
                    while (!subscriber.accept(batch)) {
                        if (!running) {
                            break;
                        }
                    }
                }
            });
            thread.start();
        }
        public void stop() {
            running = false;
        }
    }
    static class BackpressureSubscriber {
        private volatile boolean running;
        public BackpressureSubscriber() {
            this.running = true;
        }
        public boolean accept(List<Integer> batch) {
            if (running) {
                processDataLogic(batch);
                return true;
            } else {
                return false;
            }
        }
        public void start() {
            // Subscriber 在 JDK 8 中没有异步处理的能力,因此不需要单独开启线程
        }
        public void stop() {
            running = false;
        }
    }
}

多线程版本

一个生产者,多个消费者

import lombok.SneakyThrows;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class BackpressureExample {
    public static void main(String[] args) throws InterruptedException {
        BackpressureSubscriber subscriber = new BackpressureSubscriber();
        BackpressurePublisher publisher = new BackpressurePublisher(subscriber);
        publisher.start();
        subscriber.start();
        // 为了演示效果,这里让主线程休眠一段时间
        Thread.sleep(50000);
        publisher.stop();
        subscriber.stop();
    }
    @SneakyThrows
    public static void processDataLogic(List<Integer> batch) {
        //模拟任务执行
        int r = new Random().nextInt(3000);
        Thread.sleep(r);
        System.out.println(Thread.currentThread().getName() + ",Received batch: " + batch + ",sleep ms = " + r);
    }
    static class BackpressurePublisher {
        private final BackpressureSubscriber subscriber;
        private volatile boolean running;
        public BackpressurePublisher(BackpressureSubscriber subscriber) {
            this.subscriber = subscriber;
            this.running = true;
        }
        public void start() {
            Thread thread = new Thread(() -> {
                int item = 1;
                while (running) {
                    List<Integer> batch = new ArrayList<>();
                    for (int i = 0; i < 5; i++) {
                        System.out.println(Thread.currentThread().getName() + "-----produce data = " + item);
                        batch.add(item++);
                    }
                    while (!subscriber.accept(batch)) {
                        if (!running) {
                            break;
                        }
                    }
                }
            });
            thread.start();
        }
        public void stop() {
            running = false;
        }
    }
    static class BackpressureSubscriber {
        private volatile boolean running;
        private final ExecutorService executor;
        private final int workerSize = 2;
        private final List<Future> futures;
        public BackpressureSubscriber() {
            this.running = true;
            this.executor = Executors.newFixedThreadPool(workerSize);
            futures = new ArrayList<>(workerSize);
        }
        public boolean accept(List<Integer> batch) {
            if (running) {
                Future f = executor.submit(() -> processDataLogic(batch));
                futures.add(f);
                waitForTaskDone(futures);
                return true;
            } else {
                return false;
            }
        }
        public void waitForTaskDone(List<Future> futures) {
            while (futures.size() >= workerSize) {
                for (Future future : futures) {
                    if (future.isDone()) {
                        // 只要有一个worker是空闲就重新获取任务
                        futures.remove(future);
                        return;
                    }
                }
            }
        }
        public void start() {
            // Subscriber 在 JDK 8 中没有异步处理的能力,因此不需要单独开启线程
        }
        public void stop() {
            running = false;
            executor.shutdown();
        }
    }
}

到此这篇关于用JAVA自己实现一套背压机制的文章就介绍到这了,更多相关java背压机制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 使用java生成json时产生栈溢出错误问题及解决方案

    使用java生成json时产生栈溢出错误问题及解决方案

    这篇文章主要介绍了使用java生成json时产生栈溢出错误问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • Springboot异常错误处理解决方案详解

    Springboot异常错误处理解决方案详解

    这篇文章主要介绍了Springboot异常错误处理解决方案详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08
  • Spring boot 无法注入service问题

    Spring boot 无法注入service问题

    这篇文章主要介绍了Spring boot 无法注入service问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • Java String的intern方法使用场景示例

    Java String的intern方法使用场景示例

    这篇文章主要介绍了Java String的intern方法使用场景示例,帮助大家更好的理解和使用Java,感兴趣的朋友可以了解下
    2020-11-11
  • Java内部类之间的闭包和回调详解

    Java内部类之间的闭包和回调详解

    相信闭包和回调对每位学习Java的人来说都不陌生,那么今天小编和大家分享一篇关于Java内部类之间的闭包和回调,有需要的可以参考借鉴。
    2016-09-09
  • SpringMvc获取页面中的参数方法详解

    SpringMvc获取页面中的参数方法详解

    这篇文章主要介绍了SpringMvc获取页面中的参数方法详解,获取页面的参数通常都是让类实现设置HttpServletRequest request接口然后重写接口中的方法的办法来得到参数,但是在Springmvc中有其他的获取方法,需要的朋友可以参考下
    2023-10-10
  • Spring Cloud搭建eureka过程图解

    Spring Cloud搭建eureka过程图解

    这篇文章主要介绍了Spring Cloud搭建eureka过程图解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • Springboot 过滤器、拦截器、全局异常处理的方案处理小结

    Springboot 过滤器、拦截器、全局异常处理的方案处理小结

    拦截器是一种动态拦截方法调用的机制,与过滤器类似,是Spring框架中提供的,用来动态拦截控制器方法的执行,这篇文章主要介绍了Springboot 过滤器、拦截器、全局异常处理,需要的朋友可以参考下
    2024-03-03
  • postman测试post请求参数为json类型的实例讲解

    postman测试post请求参数为json类型的实例讲解

    下面小编就为大家分享一篇postman测试post请求参数为json类型的实例讲解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-03-03
  • Spring Mybatis 分页插件使用教程

    Spring Mybatis 分页插件使用教程

    这篇文章主要介绍了Spring Mybatis分页插件使用教程,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2018-02-02

最新评论