近来响应式编程成为一种流行的模式,涌现出很多支持各种编程语言的库和框架和相关的博文文章。像Facebook,SoundCloud,Microsoft,Netflix等大公司也开始支持和使用这种模式。所以我们这些程序员需要弄清楚关于响应式编程的一些问题。为什么人们会对于响应式编程如此狂热?什么事响应式编程?使用它会对于我们的项目有哪些帮助?我们应该去学习和使用它吗?
同时,Java作为一门支持多线程、高效可靠的流行编程语言,被广泛的应用于建立复杂的应用程序(搜索引擎,通过数据库为复杂的网络应用提供支持的服务端应用等)。但是Java也有一些令人诟病的地方——仅使用内置工具很难写一些简单的并发程序,使用Java编程会经常写大量的样式化代码。并且如果你使用异步方式(例如使用Future),会很容易进入“CallBack hell”,当然几乎所有的编程语言都有这个通病。
CallBack hell:Java进行异步编程的时候往往会使用CallBack的方式来通知结果,在复杂的业务中会因为CallBack的层层嵌套导致整个代码难以理解。
换句话说,Java足够强大能够让你完成一些好的应用,但是也会遇到困扰。好消息是已经有一个解决问题的办法——使用相应式编程。
这里我们将会介绍RxJava这个项目,一个开源的基于Java语言的相应式编程实现。使用RxJava写代码跟通常的思考方式有一些不同,但是会让你能够用简单且结构鲜明的代码来处理复杂的业务逻辑。
这里我们会介绍:
- 什么事响应式编程
- 学习和使用响应式编程的原因
- 开始使用RxJava和熟悉这个模式框架
- 一个RxJava的小例子
什么事响应式编程?
响应式编程是一种解决关于事件传播的范式。换句话说,如果一个程序把所有数据的变化事件广播给对它感兴趣的部分(用户、其他程序、其他组件,以及下属部分),那么这个程序就可以称为一个响应式程序。
一个更简单的例子使用是Microsoft Excel。如果你在A1单元格输入一个数字,也在在B1输入一个数字,并且设置C1的内容为SUM(A1, B1)
,一旦A1或者是B1出现变化,那么C1将会立即更新他们的和。
我们把这种叫做响应式求和。
响应式跟传统的方式不同点有哪些呢?我们可以看看下面的例子:
int a = 4;
int b = 5;
int c = a + b;
System.out.println("" + c);//9
a = 6;
System.out.println("" + c);//还是9
// c虽然是a和b的和,但是我们改变a的值后,c的值并没有随之改变。
这个例子简单介绍了响应式编程的意义。当然我们有许多办法能够解决这个问题,但是也有一些问题需要解决。
为什么我们需要响应式编程?
回答这个问题的最简单方式是思考目前我们开发项目所面临的需求。
10-15年之前,网站出现故障或者是相应很慢是一种正常的现象,但是现在必须保证能够24*7在线可用,并且必须快速响应。一旦变慢或者不可用,使用者会倾向于转向其他的服务。在今天响应慢就是不可用或者出问题了。现在我们处理的是大量数据,并且需要提供快速的服务和处理。
过去网络请求的失败并不是什么大事,但是现在我们必须处理容错和给用户提供合理的原因信息。
过去,我们一般是开发简单的桌面程序,现在我们需要处理的是要求快速响应的网络应用。并且大多数情况,这些应用需要跟大量的远程服务进行通信。
如果我们想要我们的产品具有竞争力,就必须满足以上的所有需求。换句话说,我们必须做好以下内容:
- 模块化/动态化:这可以帮助我们的产品24*7可用。因为模块是可以在不当机的情况下上线和下线的。并且这种方式,也利于我们项目的架构能够解决随着规模增加更好代码管理的问题。
- 可扩展:这种方式使我们能够处理大数据和大量的请求。
- 容错机制:这可以是系统更稳定。
- 快速响应:意味着系统运行快速和高度可用。
让我们思考如何才能解决上面的需求呢:
- 如果我们的系统是事件驱动的那么就可以实现模块化。我们可以把整个系统划分为小的模块组件,并通过通知来进行组件间通信。这样我们可以响应处理通知表示的系统数据流。
- 可扩展意味着能够正常载入来响应持续增长的数据。
- 对失败和错误进行响应能够增加系统的稳定性。
- 快速响应就是能够迅速对于用户的活动进行反馈。
介绍RxJava
要进行响应式编程,我们需要一个库或者是一种特殊的编程语言,毕竟自己实现对于大多数人是一件非常困难的任务。并且Java并不是一门真正的相应式编程语言(虽然提供了java.util.Observable
,但是功能太局限)。Java是一门静态,面向对象的语言,我们要完成一些功能需要写一些模式代码(简单的Java Bean等)。但是目前有一个支持Java的响应式编程库——RxJava。RxJava是一个由Netflix最初建立的一个开源项目,目前已经有很多人为这个项目贡献代码进行完善。
下载和配置RxJava
你可以从Github来下载源码,自己进行打包(https://github.com/ReactiveX/RxJava)。RxJava不依赖其他的库,支持Java8的lambda。有非常好的Javadoc和Github wiki,你可以从中查找资料和学习。你可以通过下面的方式下载和建立:
$ git clone git@github.com:ReactiveX/RxJava.git
$ cd RxJava/
$ ./gradlew build
当然我们更常用的是只下载已经生成好的jar文件,这里我们以1.0.14版本为例。
如果你使用Maven,你可以通过在pom.xml
中添加RxJava作为一个依赖:
<dependency>
<groupId>io.reactivex</groupId>
<artifactId>rxjava</artifactId>
<version>1.0.14</version>
</dependency>
使用Gradle则需要配置你的build.gradle
,添加下面的内容:
dependencies {
...
compile 'io.reactivex:rxjava:1.0.14'
...
}
一个使用RxJava实现两个数相加的简单例子
public static void main(String[] args) {
ConnectableObservable<String> cob = getConnect(System.in);
Observable<Double> a = varInput("a", cob);
Observable<Double> b = varInput("b", cob);
reactSum(a, b);
cob.connect();
}
static ConnectableObservable<String> getConnect(InputStream inputStream) {
final BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
return Observable.create(new Observable.OnSubscribe<String>() {
@Override public void call(Subscriber<? super String> subscriber) {
String line;
if (subscriber.isUnsubscribed()) return;
try {
while (!subscriber.isUnsubscribed()
&& (line = reader.readLine()) != null) {
if (line.equals("exit")) {
break;
}
subscriber.onNext(line);
}
if (!subscriber.isUnsubscribed()) {
subscriber.onCompleted();
}
} catch (IOException e) {
e.printStackTrace();
subscriber.onError(e);
}
}
}
).publish();
}
static Observable<Double> varInput(String tag, Observable<String> observable) {
final Pattern pattern = Pattern.compile("^\\s*" + tag + "\\s*[:|=]\\s*(-?\\d+\\.?\\d*)$");
return observable.map(new Func1<String, Matcher>() {
@Override public Matcher call(String s) {
return pattern.matcher(s);
}
})
.filter(new Func1<Matcher, Boolean>() {
@Override public Boolean call(Matcher matcher) {
return matcher.find();
}
})
.map(new Func1<Matcher, String>() {
@Override public String call(Matcher matcher) {
return matcher.group(1);
}
})
.map(new Func1<String, Double>() {
@Override public Double call(String s) {
return Double.parseDouble(s);
}
});
}
static void reactSum(Observable<Double> a, Observable<Double> b) {
Observable.combineLatest(a, b, new Func2<Double, Double, Double>() {
@Override public Double call(Double aDouble, Double aDouble2) {
return aDouble + aDouble2;
}
})
.subscribe(new Subscriber<Double>() {
@Override public void onCompleted() {
System.out.println("onComplete");
}
@Override public void onError(Throwable e) {
System.out.println("onError:" + e.getMessage());
}
@Override public void onNext(Double aDouble) {
System.out.println("a + b is:" + aDouble);
}
});
}
运行结果可能如下:
a:1
b:3
a + b is:4.0
a:5
a + b is:8.0
exit
onComplete
如果搭配Java8的lambda,程序会更加的简洁。这里我们暂时先不使用lambda。