RxJava监控实战
RxJava是什么
一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库。
基本概念
- Observable 被观察者,在观察者模式中是被观察的对象,一旦数据产生或发生变化,会通过某种方式通知观察者或订阅者
- Observer/Subscriber 观察者,监听Observable发射的数据并做出响应,Subscriber是它的一个特殊实现
- subscribe (订阅)、事件
- emit 直译为发射,发布,发出,含义是Observable在数据产生或变化时发送通知给Observer,调用Observer对应的方法
Observable 和 Observer 通过 subscribe() 方法实现订阅关系,从而 Observable 可以在需要的时候发出事件来通知 Observer。
简单例子
//被观察者
Observable<Integer> observable = Observable.create(new Observable.OnSubscribe<Integer>() {
@Override
public void call(Subscriber<? super Integer> subscriber) {
for (int i = 0; i <= 5; i++) {
//向观察者发布事件
subscriber.onNext(i);
}
//通知观察者,订阅结束
subscriber.onCompleted();
}
});
Observer<Integer> observer = new Observer<Integer>() {
@Override
public void onCompleted() {
//被观察者发布结束事件的时候,该方法会被调用
System.out.println("======onCompleted=====");
}
@Override
public void onError(Throwable e) {
// 被观察者发布事件期间,和观察者处理事件期间,发生异常的时候,该方法都会被调用
System.out.println("====onError======="+e.getMessage());
}
@Override
public void onNext(Integer integer) {
// 被观察者发布事件后,该方法会被调用
System.out.println("OnNext= " + integer);
}
};
//执行订阅
observable.subscribe(observer);
工厂方法
常见的工厂方法
just()
,from()
,create()
,interval()
,defer()
,delay()
Action
Action是RxJava 的一个接口,常用的有Action0和Action1
Action0: 它只有一个方法 call(),这个方法是无参无返回值的;由于 onCompleted() 方法也是无参无返回值的,因此 Action0 可以被当成一个包装对象,将 onCompleted() 的内容打包起来将自己作为一个参数传入 subscribe() 以实现不完整定义的回调。
Ation1:它同样只有一个方法 call(T param),这个方法也无返回值,但有一个参数;与 Action0 同理,由于 onNext(T obj) 和 onError(Throwable error) 也是单参数无返回值的,因此 Action1 可以将 onNext(obj)和 onError(error) 打包起来传入 subscribe() 以实现不完整定义的回调
Observable<Integer> observable = Observable.create(subscriber -> {
subscriber.onNext(12);
subscriber.onCompleted();
});
// 适用于onNext(obj)和 onError(error)
Action1<Integer> action1 = new Action1<Integer> () {
@Override
public void call(Integer o) {
System.out.println("===action1==="+o);
}
};
// 适用于onCompleted()
Action0 action0 = new Action0() {
@Override
public void call() {
System.out.println("===-doOnCompleted===");
}
};
observable.doOnCompleted(action0).subscribe(action1);
Func
func 适用于map,filter。
Func0:与Action0非常相似,也有call()方法,但是它是无参有返回值的
Func1 有参返回值。 Func1<T, R>
@Test
public void testFunc(){
Observable<Integer> observable = Observable.just(3,5,7,11,15,90);
Action1<Integer> action1 = (value)-> System.out.println("====="+value);
Func1<Integer,Integer> func1 =(t)-> t * 10;
observable.map(func1).subscribe(action1);
}
Scheduler
默认情况下,RxJava事件产生和消费均在同一个线程中,例如在主线程中调用,那么事件的产生和消费都在主线程。
RxJava 内部提供了4个调度器
- Schedulers.io(): I/O 操作(读写文件、数据库、网络请求等),与newThread()差不多,区别在于io() 的内部实现是是用一个无数量上限的线程池,可以重用空闲的线程,因此多数情况下 io() 效率比 newThread()更高。值得注意的是,在 io() 下,不要进行大量的计算,以免产生不必要的线程;
- Schedulers.newThread(): 开启新线程操作;
- Schedulers.immediate(): 默认指定的线程,也就是当前线程;
- Schedulers.computation():计算所使用的调度器。这个计算指的是 CPU 密集型计算,即不会被 I/O等操作限制性能的操作,例如图形的计算。这个 Scheduler 使用的固定的线程池,大小为 CPU 核数。值得注意的是,不要把 I/O 操作放在 computation()中,否则 I/O 操作的等待时间会浪费 CPU;
- AndroidSchedulers.mainThread(): RxJava 扩展的 Android 主线程;
final ImageView ivLogo = (ImageView) findViewById(R.id.ivLogo);
Observable.create(new Observable.OnSubscribe<Drawable>() {
@Override
public void call(Subscriber<? super Drawable> subscriber) {
try {
Drawable drawable = Drawable.createFromStream(new URL("https://ss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=2502144641,437990411&fm=80&w=179&h=119&img.JPEG").openStream(), "src");
subscriber.onNext(drawable);
} catch (IOException e) {
subscriber.onError(e);
}
}
})
// 指定 subscribe() 所在的线程,也就是上面call()方法调用的线程
.subscribeOn(Schedulers.io())
// 指定 Subscriber 回调方法所在的线程,也就是onCompleted, onError, onNext回调的线程
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Drawable>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Log.e(TAG, e.toString());
}
@Override
public void onNext(Drawable drawable) {
ivLogo.setImageDrawable(drawable);
}
});
操作符
Scan
Scan操作符对原始Observable发射的第一项数据应用一个函数,然后将那个函数的结果作为自己的第一项数据发射。它将函数的结果同第二项数据一起填充给这个函数来产生它自己的第二项数据。它持续进行这个过程来产生剩余的数据序列。这个操作符在某些情况下被叫做accumulator(累加器)
Observable.range(0, 5)
.scan(new Func2<Integer, Integer, Integer>() {
@Override
public Integer call(Integer result, Integer result2) {
System.out.println("result " + result + " result2 " + result2);
return result + result2;
}
})
.subscribe(new Action1<Integer>() {
@Override
public void call(Integer integer) {
// 第一次直接输出,并不参与计算
System.out.println("integer " + integer);
}
});
输出结果:
integer 0
result 0 result2 1
integer 1
result 1 result2 2
integer 3
result 3 result2 3
integer 6
result 6 result2 4
integer 10
skip
skipe(n)操作符跳过源Obsrvable的前面n个数据项
Observable.range(1, 10).skip(5).subscribe(new Action1<Integer>() {
@Override
public void call(Integer integer) {
LogUtils.d("----->call():" + integer);
}
});
输出结果:
----->:6
----->:7
----->:8
----->:9
----->:10
buffer
buffer()函数将源Observable变换一个新的Observable,这个新的Observable每次发射一组列表值而不是一个一个发射。
List<String> list = new ArrayList<>();
for (int i = 0; i < 30; i++)
{
list.add("hello i:" + i);
}
Observable.from(list).buffer(5,1).subscribe(new Subscriber<List<String>>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(List<String> strings) {
for (String s : strings)
{
System.out.println(s);
}
System.out.println("========");
}
});
输出结果:
hello i:0
hello i:1
hello i:2
hello i:3
hello i:4
========
hello i:1
hello i:2
hello i:3
hello i:4
hello i:5
========
hello i:2
hello i:3
hello i:4
hello i:5
hello i:6
========
groupBy
Observable.range(1, 8).groupBy(new Func1<Integer, String>() {
@Override
public String call(Integer integer) {
return (integer % 2 == 0) ? "偶数组" : "奇数组";
}
}).subscribe(new Action1<GroupedObservable<String, Integer>>() {
@Override
public void call(final GroupedObservable<String, Integer> stringIntegerGroupedObservable) {
System.out.println("group name:" + stringIntegerGroupedObservable.getKey());
stringIntegerGroupedObservable.subscribe(new Action1<Integer>() {
@Override
public void call(Integer integer) {
System.out.println(stringIntegerGroupedObservable.getKey() + "'member: " + integer);
}
});
}
});
输出结果:
group name:奇数组
奇数组'member: 1
group name:偶数组
偶数组'member: 2
奇数组'member: 3
偶数组'member: 4
奇数组'member: 5
偶数组'member: 6
奇数组'member: 7
偶数组'member: 8
更多
综合案例
有了以上基础后,我们要实战一个例子。 需要对不同厂家的返回结果进行异常数据监控。
监控条件:
- 请求结果连续10个为空时则认为异常,偶尔为空为正常情况。异常后触发报警设置并通知运营人员。
- 请求错误连续10个时则进行错误报警,通知运营人员,并记录错误日志。
2个小时候进行重试处理。
@Component
public class RealTimeMonitorService {
private final PublishSubject<MonitorResponse> subject = PublishSubject.create();
private final PublishSubject tryRecover = PublishSubject.create();
private final SerializedSubject<MonitorResponse, MonitorResponse> serializedSubject = new SerializedSubject<>(subject);
private final Logger log = LoggerFactory.getLogger(getClass());
private MonitorProperties monitorProperties;
private CreditRouteClient creditRouteClient;
public TxScoreRealTimeMonitorService(MonitorProperties monitorProperties,CreditRouteClient creditRouteClient) {
this.monitorProperties = monitorProperties;
this.creditRouteClient =creditRouteClient;
dataMonitor();
}
private void dataMonitor() {
Observable<GroupedObservable<String, MonitorResponse>> groupedObservableObservable = subject.groupBy(monitorResponse -> monitorResponse.getChannelCode());
groupedObservableObservable.subscribe(stringMonitorResponseGroupedObservable -> {
String channelCode = stringMonitorResponseGroupedObservable.getKey();
AtomicBoolean hasError = new AtomicBoolean(true);
tryReconverAfterTwoHours(hasError);
monitorCondition( stringMonitorResponseGroupedObservable, channelCode, hasError);
});
}
private void monitorCondition(GroupedObservable<String, MonitorResponse> stringMonitorResponseGroupedObservable,
String channelCode, AtomicBoolean hasError) {
stringMonitorResponseGroupedObservable.buffer(monitorProperties.getSize(),monitorProperties.getSkip()).onBackpressureDrop().subscribe(t -> {
if (t.stream().filter(i -> i.getStatus() == MonitorResponseStatus.NOTFOUND).count() == monitorProperties.getSize()) {
if (updateBeforeLastmonth(channelCode,hasError));
}
if (t.stream().filter(i -> i.getStatus() == MonitorResponseStatus.ERROR).count() == monitorProperties.getSize()) {
log.error("服务错误,请记录查询记录到数据库中,后面让客户重查");
// TODO
}
});
}
private void tryReconverAfterTwoHours( AtomicBoolean hasError) {
tryRecover.delay(30, TimeUnit.SECONDS).subscribe(s -> {
log.error("2小时后尝试恢复到上月!");
// TODO
hasError.set(true);
});
}
private boolean updateBeforeLastmonth(String channelCode, AtomicBoolean hasError) {
if(hasError.get()) {
log.error("数据异常了,把查询时间切换至上上月");
// TODO
tryRecover.onNext(channelCode);
hasError.set(false);
return true;
}
return false;
}
public void monitor(MonitorResponse monitorResponse) {
serializedSubject.onNext(monitorResponse);
}
}