Camel路由简介
Camel最重要的特色之一就是路由,没有它Camel本质上只是一个传输连接器库。本篇文章将从各个角度介绍Camel的路由并通过实例,展示如何用代码实现Camel的路由。
其实路由在生活中随处可见,比如我们在淘宝下单后,卖家将宝贝使用某个物流快递公司发出,宝贝经过各个中转站经过各种检查最终到我们的手上。其实宝贝在这个中转的过程就可以叫做路由。从专业角度出发,“路由”一词多用与网络工程术语,“路由是指分组从源到目的地时,决定端到端路径的网络范围的进程。”在OSI参考模型中,路由工作在第三层即网络层,负责数据包的转发以实现网络的互连互通。
Camel路由和上述“路由”有相似的地方,也有不同的地方。其相似的地方在于两者的功能是相似的,即都是在端点间进行数据的转发。但两者还是有很多不同的地方的,首先,网络中的”路由“多工作在网络设备(路由器)间,通过动态维护路由表的方式反映当前的网络拓扑;而Camel的路由用于应用中的通信或者应用间通信,两者实现的方式也有区别,Camel的路由需要通过我们手动编排的方式,在指定的(或可变的)端点间进行数据的传输、过滤、转换等操作。
在企业消息系统中,路由的应用会更有目的性,多是从输入队列中获取消息,并根据一组条件将消息发送到多个输出队列之一的,或者应用直接作为起始端点即生产者,根据一组条件将消息发送到多个输出队列的过程。
在Apache Camel中,路由被定义为信息一步一步移动的过程,它起源于作为消费者角色的端点,使用者可以从外部服务器接收消息,在某个系统上轮询消息甚至创建消息本身。然后此消息将通过一个处理组件、处理器、拦截器等传递。消息最终被发送到作为生产者的目标端点。
Camel路由易于使用的一个特性是端点URI,通过指定URI,可以确定要使用的组件以及该组件是如何被配置的。然后可以决定将消息发送到由该URI配置的组件,或者使用该组件发出消息。
接下来,让我们一起写一个非常简单的Demo来入门Camel。
简单上手
创建Maven工程
使用Camel的场景多为Web环境,所以本系列文章的Demo基本都是Maven工程,这样加载Camel的jar包以及组件的jar包依赖会比较方便一点。
创建一个普通的Maven工程,然后在pom.xml文件中添加Camel的依赖:
<dependencies>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-core</artifactId>
<version>2.22.1</version>
</dependency>
</dependencies>
本次Demo的功能很简单,将一个文件夹下的文件复制到另一个文件夹,不涉及其他的camel组件,所以只添加Camel-core依赖即可。首先让我们看看使用Java编写实现此功能的代码是怎样的。
// Java代码实现
public class FileCopier {
public static void main(String[] args) throws Exception {
File inboxDirectory = new File("data/inbox");
File outboxDirectory = new File("data/outbox");
outboxDirectory.mkdir();
File[] files = inboxDirectory.listFiles();
for (File source : files) {
if (source.isFile()) {
File dest = new File(
outboxDirectory.getPath()
+ File.separator
+ source.getName());
copyFile(source, dest);
}
}
}
private static void copyFile(File source, File dest)
throws IOException {
OutputStream out = new FileOutputStream(dest);
byte[] buffer = new byte[(int) source.length()];
FileInputStream in = new FileInputStream(source);
in.read(buffer);
try {
out.write(buffer);
} finally {
out.close();
in.close();
}
}
}
使用Java代码,我们需要手动的去遍历指定文件夹下的所有文件,然后操作输入输出流。再看看Camel是如何实现的:
public class FileCopierWithCamel {
public static void main(String[] args) throws Exception {
CamelContext context = new DefaultCamelContext();
context.addRoutes(new RouteBuilder() {
@Override
public void configure() throws Exception {
// noop=true: 表示此操作为复制,如果去掉此参数则为移动
from("file:data/inbox?noop=true")
.to("file:data/outbox");
}
});
context.start();
Thread.sleep(10000);
context.stop();
}
}
放眼望去少了一半的代码,其实再仔细一看,其中还有些代码是自动生成的。
代码详解
我们看完代码有不懂的地方先stash一下,先思考一下这个文件拷贝的功能是怎么和Camel的路由扯上关系的。因为今后在使用Camel时,不管什么场合都要和路由打交道,首先我们知道路由是肯定要有起始地和目的地的,那么在这个Demo中路由的起始地就是原文件存放的文件夹,那么目的地就是要将文件复制到的那个文件夹,那么复制的过程就相当于是路由的过程。然后我们结合一下代码来看一下:
from("file:data/inbox?noop=true")
.to("file:data/outbox");
上述代码是由DSL(域特定语言)描述的,在Java中使用Camel编排路由主要有两种实现方式,一种就是在Java代码中使用DSL语言(如上述代码),而另一种是通过XML的方式,两种方式各有利弊,其功能基本是一致的。
现在我们开始分析上述代码:
- 其中明显可以看到
from
和to
两个关键字,正如其字面意思,Camel会从from
声明的起始端点将消息路由至to
声明的终点。 - 我们将from中的内容拆解开来看,Camel路由中对于端点的描述都是类似这样的形式的,首先是
file:
,这个部分可以视为端点头,声明了对于该端点Camel应该使用哪种组件进行处理,此处即使用file组件。 - 然后就是
data/inbox
,这个部分其实就是文件夹路径。 - ?表示后面的内容都是Camel对于端点配置的参数,这些参数都是Camel特定的。此处的
noop=true
意义为移动文件的操作为复制,如果去掉此参数,操作将会变为移动即剪切。
对于to声明的部分,在此处除了没有参数,和from的内容是一个道理。
然后我们再看整段代码,首先是创建了 CamelContext
对象,它表示一个Camel路由的规则库,和Spring的 ApplicationContext
类似。完成了Camel上下文的初始化,我们便可调用CamelContext
的 addRoutes
方法去添加路由,这里也是有两种方法,代码中的方法是匿名内部对象的方式,另一种方法是创建一个类继承自 RouteBuilder
。两种方法只是实现形式不一样,功能相同。在 RouteBuilder
中会又一个 configure
方法,我们的路由都要在其中编写。
编排好路由之后,便可使用start方法去开启路由,这里的线程sleep只是为了给Camel足够的时间让它完成文件复制的操作,最后使用stop结束掉Camel。
总结
希望大家都可以动手实现一下这个Demo,虽然很简单,但是可以借此熟悉Camel使用的规则形式等。
通过这个Demo,相信大家已经看到了Camel的强大之处。作为开发人员,Camel对于我们的意义在于我们在学习了解了Camel的实现机制后,便可通过Camel的各种组件灵活的进行路由编排,从而实现各种强大的功能。