Camel 为使用许多不同的通信技术实现的端点提供开箱即用的支持。下面是 Camel 支持的端点技术的一些示例。
- JMS 队列。
- Web 服务。
- 一个文件(本文样例)。文件可能听起来像是不太可能的终结点类型,直到您意识到在某些系统中,一个应用程序可能会将信息写入文件,而稍后,另一个应用程序可能会读取该文件。
- FTP 服务器。
- 电子邮件地址。客户端可以向电子邮件地址发送消息,服务器可以从邮件服务器读取传入邮件。
- POJO(普通旧 Java 对象)。
使用案例
基于文件的camel的使用案例
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>it.luke</groupId>
<artifactId>Camel_Pro</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-core</artifactId>
<version>2.25.1</version>
</dependency>
</dependencies>
</project>
1.从目录读取并写入另一个目录
from(“file://inputdir/?delete=true”).to(“file://outputdir”)
package it.luke.camel_fileTofile;
import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
/**
* @author luke
* @date 2020/11/70:33
*/
public class CamelFile {
public static void main(String[] args) {
//创建一个CamelContext对象
CamelContext camelContext = new DefaultCamelContext();
//定义路由规则
try {
camelContext.addRoutes(new RouteBuilder() {
public void configure() {
//读取指定目录的文件,写入到指定的输出目录中 ?后拼接配置参数(delete=true)写入后删除源文件
from("file://D:\\Server\\Camel_Pro\\testData\\input\\?delete=true")
.to("file://D:\\Server\\Camel_Pro\\testData\\output\\");
}
});
//启动线程
camelContext.start();
Thread.sleep( 5000);
//关闭线程
camelContext.stop();
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.递归地从目录读取并写入另一个目录
路由:from(“file://inputdir/?recursive=true&delete=true”).to(“file://outputdir”)
camelContext.addRoutes(new RouteBuilder() {
public void configure() {
//读取指定目录的文件,写入到指定的输出目录中 ?后拼接配置参数(recursive=true)递归采集目录下的文件
from("file://D:\\Server\\Camel_Pro\\testData\\input\\?delete=true&recursive=true")
.to("file://D:\\Server\\Camel_Pro\\testData\\output\\");
}
});
补充:delete=true 虽然可以删除源文件,但是并不能删除源目录
如果仅需要采集文件而不需要目录结构的话,可以在输出路由添加参数?flatten=true
from("file://D:\\Server\\Camel_Pro\\testData\\input\\?delete=true&recursive=true")
.to("file://D:\\Server\\Camel_Pro\\testData\\output\\?flatten=true");
便可以得到平铺后的文件
3.从目录读取并处理 JAVA 中的消息
提取目录下的文件的内容,经过处理后,写入到另一个文件中
package it.luke.camel_fileTofile;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
/**
* @author luke
* @date 2020/11/71:10
*/
public class CamelFileObj {
public static void main(String[] args) {
//创建一个CamelContext对象
CamelContext camelContext = new DefaultCamelContext();
//定义路由规则
try {
camelContext.addRoutes(new RouteBuilder() {
public void configure() {
from("file://D:\\Server\\Camel_Pro\\testData\\input\\").process(new Processor() {
public void process(Exchange exchange) throws Exception {
Object body = exchange.getIn().getBody(String.class);
System.out.println(body);
exchange.getMessage().setBody("我是重新处理过的数据"+body);
// do some business logic with the input body
}
}).to("file://D:\\Server\\Camel_Pro\\testData\\output\\?flatten=true");
}
});
//启动线程
camelContext.start();
Thread.sleep( 5000);
//关闭线程
camelContext.stop();
} catch (Exception e) {
e.printStackTrace();
}
}
}
可以看到写入内容已经被我们修改了
4.通过相对于最终目标的临时目录写入文件
写入临时目录,然后通过原子操作,将数据直接移动到目的目录,主要是为了防止正在写入时被其它程序异常读取,这样可以保证数据的完整性
to(“file:///var/myapp/finalDirectory?tempPrefix=/…/filesInProgress/”);
camelContext.addRoutes(new RouteBuilder() {
public void configure() {
//读取指定目录的文件,写入到指定的输出目录中 ?后拼接配置参数tempPrefix=/../filesInProgress/ 将文件的写入操作指向临时目录以作原子转移操作
from("file://D:\\Server\\Camel_Pro\\testData\\input\\?delete=true&recursive=true")
.to("file://D:\\Server\\Camel_Pro\\testData\\output\\?tempPrefix=/../filesInProgress/");
}
});
5.对文件名使用表达式
from(“file://inbox?move=backup/ d a t e : n o w : y y y y M M d d / {date:now:yyyyMMdd}/ date:now:yyyyMMdd/{file:name}”).to("…");
注意move参数和delete不能共存
camelContext.addRoutes(new RouteBuilder() {
public void configure() {
//读取指定目录的文件,写入到指定的输出目录中 ?后拼接配置参数move=backup/${date:now:yyyyMMdd}/${file:name} 在读取完文件后将文件转移到指定目录
from("file://D:\\Server\\Camel_Pro\\testData\\input\\?recursive=true&move=backup/${date:now:yyyyMMdd}/${file:name}")
.to("file://D:\\Server\\Camel_Pro\\testData\\output\\?tempPrefix=/../filesInProgress/");
}
});
move常用来指定文件在哪个时间段完成读取,读取的内容是什么,是问题回溯的一大利器
6.避免重复读取同一文件(幂等使用者)+ 定时调度
from(“file://inbox?idempotent=true”).to("…");
按照一定的规则拒绝重复读取
为了方便观察,这边开启定时任务
添加依赖
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-quartz2</artifactId>
<version>2.16.2</version>
</dependency>
package it.luke.camel_fileTofile;
import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
/**
* @author luke
* @date 2020/11/712:06
*/
public class CamelQuzFile {
public static void main(String[] args) {
//创建一个CamelContext对象
CamelContext camelContext = new DefaultCamelContext();
//定义路由规则
try {
camelContext.addRoutes(new RouteBuilder() {
public void configure() {
//配置定时器quartz2:Demo?cron=1/10 * * * * ?
from("quartz2:Demo?cron=1/10 * * * * ?").
from("file://D:\\Server\\Camel_Pro\\testData\\input\\")
.to("file://D:\\Server\\Camel_Pro\\testData\\output\\");
}
});
//启动线程
camelContext.start();
synchronized (CamelQuzFile.class) {
CamelQuzFile.class.wait();
}
} catch (Exception e) {
e.printStackTrace();
//关闭线程
try {
camelContext.stop();
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
}
没有添加幂等的情况下,往input目录里面添加重复名字的文件是会重复读取写入的
故添加幂等
camelContext.addRoutes(new RouteBuilder() {
public void configure() {
//读取指定目录的文件,写入到指定的输出目录中 ?后拼接配置参数idempotent=true幂等文件名,通过判断文件名是否一致,拒绝重复读取
from("quartz2:Demo?cron=1/10 * * * * ?").
from("file://D:\\Server\\Camel_Pro\\testData\\input\\?idempotent=true")
.to("file://D:\\Server\\Camel_Pro\\testData\\output\\");
}
这边在输入目录同时写入两份内容一样的文件1.log和2.log
调度时间到了之后,发现1.log 内容并没有刷新过来,因为默认的幂等是按照文件的绝对路径的
此外可以通过&idempotentKey= f i l e : n a m e − {file:name}- file:name−{file:size}
来指定幂等的key
7.使用过滤器
路由中使用filter可以对传输的数据 进行过滤
public FilterDefinition filter(@AsPredicate Predicate predicate) {
FilterDefinition filter = new FilterDefinition(predicate);
this.addOutput(filter);
return filter;
}
//new Predicate() 需要传入一个对象,则是一个抽象接口,需要重写matches方法,因为这边只调用一次,所以这边采用匿名内部类的方式
camelContext.addRoutes(new RouteBuilder() {
public void configure() {
//读取指定目录的文件,写入到指定的输出目录中
//通过filter进行message的过滤
from("file://D:\\Server\\Camel_Pro\\testData\\input\\").filter(
new Predicate() {
public boolean matches(Exchange exchange) {
String body = exchange.getIn().getBody(String.class);
System.out.println(body.length());
if (body.contains("filter")) {
System.out.println(body);
return true;
}
return false;
}
}
)
.to("file://D:\\Server\\Camel_Pro\\testData\\output\\");
}
});
这边在新建两个测试文件,一个文件内容带有filter字样用于过滤
可以看到只有带有filter的文件进行了路由写入
控制条件可以根据自己的需求进行修改
废话:
个人觉得camel是一个特别适合企业集成使用的技术,提供的高级抽象有点类似SpringJPAData
目的就是将很多不同协议的数据通道,通过以相类似的路由形式表示出来使用
这里只总结了个人觉得比较常用的用法,更多的参数可以参考官网
更多内容参考官网:https://camel.apache.org/components