目录
目录
一、预备知识
1.响应式编程
响应式编程(reactive programming)是一种基于数据流(data stream)和变化传递(propagation of change)的声明式(declarative)的编程范式。
响应式编程常见的各种listener,callback,UI。
2.观察者模式(发布订阅模式)
二、RxJava
1.RxJava是什么?
RxJava是一个遵循观察者设计模式,封装后的异步操作库。
2.RxJava的优缺点
优点:
1.流式处理,代码简洁;
2.功能强大,各种操作符丰富,错误处理;
3.异步调度。
缺点:
1.上手困难,与传统编程方式差异很大;
2.服务端开发多同步操作,应用场景有限,Android编程更广泛。
3.基本原理
RxJava四要素
一个基本使用的例子
Observable.create((ObservableOnSubscribe<Integer>) e -> {
e.onNext(1);
e.onNext(2);
e.onNext(3);
e.onComplete();
}).subscribe(new Observer<>() {
@Override
public void onSubscribe(Disposable disposable) {
System.out.println("onSubscribe...");
}
@Override
public void onNext(Integer integer) {
System.out.println("onNext..." + integer);
}
@Override
public void onError(Throwable throwable) {
System.out.println("onError..." + throwable.getMessage());
}
@Override
public void onComplete() {
System.out.println("onComplete...");
}
});
输出:
onSubscribe...
onNext...1
onNext...2
onNext...3
onComplete...
线程转换
subscribeOn(Schedulers.single()) 产生事件的线程
observeOn(Schedulers.single()) 消费线程
IO
最常见的调度器之一。用于IO相关操作。比如网络请求和文件操作。IO 调度器背后由线程池支撑。无限大小多线程池。
Computation
这个是计算工作默认的调度器,它与I/O操作无关。它也是许多RxJava方法的默认调度器:buffer()
,debounce()
,delay()
,interval()
,sample()
,skip()
。
Single
此款调度器非常简单,由一个线程支持。所以无论有多少个observables,都将只运行在这个线程上。也可将其认为是主线程的一个替代
Trampoline
当我们想在当前线程执行一个任务时,并不是立即,我们可以用.trampoline()
将它入队。这个调度器将会处理它的队列并且按序运行队列中每一个任务。它是repeat()
和retry()
方法默认的调度器。
Executor Scheduler
更像是一种自定义的IO调度器。我们可以通过制定线程池的大小来创建一个自定义的线程池。适用于observables的数量对于IO调度器太多的场景使用,使用如下:
三、基本操作符
Map
FlatMap
它可以把一个发射器 Observable
通过某种方法转换为多个 Observables
,然后再把这些分散的 Observables
装进一个单一的发射器 Observable
。但有个需要注意的是,flatMap
并不能保证事件的顺序,如果需要保证,需要用到我们下面要讲的 ConcatMap
。
Observable.create((ObservableOnSubscribe<Integer>) e -> {
e.onNext(1);
e.onNext(2);
e.onNext(3);
}).flatMap((Function<Integer, ObservableSource<String>>) integer -> {
List<String> list = new ArrayList<>();
for (int i = 0; i < integer; i++) {
list.add("I am value " + integer);
}
return Observable.fromIterable(list);
}).subscribe(flatElement -> System.out.println("subscribe-" + flatElement));
输出:
subscribe-I am value 1
subscribe-I am value 2
subscribe-I am value 2
subscribe-I am value 3
subscribe-I am value 3
subscribe-I am value 3
Buffer
buffer
操作符接受两个参数,buffer(count,skip)
,作用是将 Observable
中的数据按 skip
(步长) 分成最大不超过 count 的 buffer
,然后生成一个 Observable
。
Observable.just(1, 2, 3, 4, 5)
.buffer(3, 2)
.subscribe(integers -> {
System.out.println("buffer size : " + integers.size() + ", buffer value :");
for (Integer i : integers) {
System.out.print(i + ",");
}
System.out.println();
});
输出:
buffer size : 3, buffer value :
1,2,3,
buffer size : 3, buffer value :
3,4,5,
buffer size : 1, buffer value :
5,
Interval
interval
操作符用于间隔时间执行某个操作,其接受三个参数,分别是第一次发送延迟,间隔时间,时间单位。
Observable.interval(0, 1, TimeUnit.SECONDS, Schedulers.trampoline())
.observeOn(Schedulers.trampoline())
.subscribe(aLong -> System.out.println("interval :" + aLong + " at " + LocalDateTime.now()));
输出:
interval :0 at 2021-06-06T23:28:52.988059
interval :1 at 2021-06-06T23:28:53.965217
interval :2 at 2021-06-06T23:28:54.964462
interval :3 at 2021-06-06T23:28:55.964698
interval :4 at 2021-06-06T23:28:56.965941
interval :5 at 2021-06-06T23:28:57.964382
interval :6 at 2021-06-06T23:28:58.969673
interval :7 at 2021-06-06T23:28:59.966106
四、应用场景
1.线程切换
@Test
public void testThreadControl() {
Function<Integer, Integer> function = integer -> {
log.info("currentThread: {}, data:{}", Thread.currentThread().getName(), integer);
return integer;
};
Supplier<Integer> supplier = () -> {
Thread.sleep(3000);
return 1;
};
ExecutorService executor = Executors.newFixedThreadPool(1);
PublishSubject.fromSupplier(supplier)
.subscribeOn(Schedulers.trampoline())//主线程发送数据
.map(function)//new 1 线程接收数据
.observeOn(Schedulers.single())//切换single 线程接收数据
.map(function)//single 线程接收数据
.observeOn(Schedulers.from(executor))//自定义线程池
.map(function)//自定义线程池 接收数据
.subscribe(System.out::println);
}
日志:
13:57:34.911 [main] INFO RxJavaTest - currentThread: main, data:1
13:57:34.925 [RxSingleScheduler-1] INFO RxJavaTest - currentThread: RxSingleScheduler-1, data:1
13:57:34.929 [pool-2-thread-1] INFO RxJavaTest - currentThread: pool-2-thread-1, data:1
1
2.心跳/指标收集
@SpringBootTest(classes = Application.class)
public class RxJavaTest {
private Subject<Long> collectPipeline;
@Before
public void init() {
this.collectPipeline = PublishSubject.create();
this.collectPipeline.observeOn(Schedulers.trampoline()).subscribeOn(Schedulers.trampoline())
.buffer(1, TimeUnit.SECONDS, Schedulers.computation(), 5, HashSet::new, false)
.filter(CollectionUtils::isNotEmpty).subscribe(this::batchSaveUserLastActiveTime);
}
public void emitActiveUserId(Long userId) {
collectPipeline.onNext(userId);
System.out.println(String.format("online userId: %s", userId));
}
private void batchSaveUserLastActiveTime(Set<Long> idSet) {
System.out.println(
String.format("save user last active time, size:%s, ids:%s", idSet.size(), idSet));
}
@Test
public void testBuffer() throws InterruptedException {
List<Long> idList = Arrays.asList(1000L, 1001L, 1000L, 1002L, 1003L, 1004L, 1005L, 1006L);
for (Long id : idList) {
emitActiveUserId(id);
}
Thread.sleep(2000);
}
}
日志:
online userId: 1000
online userId: 1001
online userId: 1000
online userId: 1002
online userId: 1003
save user last active time, size:5, ids:[1000, 1001, 1002, 1003, 1004]
online userId: 1004
online userId: 1005
online userId: 1006
save user last active time, size:2, ids:[1005, 1006]
使用concat和first做缓存
使用timer做定时操作。当有“x秒后执行y操作”类似的需求的时候,想到使用timer
使用interval做周期性操作。当有“每隔xx秒后执行yy操作”类似的需求的时候,想到使用interval
Hystrix使用RxJava简洁的window API来构建metric.