Spring integration是一个企业应用集成系统。主要通过消息、通道、消息端点等概念完成不同系统之间的集成。
消息包括header(头)和payload(负载)
message的header一般是id,时间戳或者目的地址什么的,比如如果你发的消息是个文件,那么header应该存放文件的名称,消息头可以根据需要修改。Payload就是负载,也就睡消息的主要内容,主要通过这个进行通信。
消息传输的通道。分两种,一种是point-to-point点对点的,一种是publish-subscribe发布订阅形式的。如果是点对点的channel,至少会有一个消费者consumer能收到发送的message,另一种订阅发布的channel,spring integration试图去用广播的形式发布message给那些订阅者subscriber。
主要实现如下:
PublishSubscribeChannel:将消息广播给所有的订阅者。
QueueChannel:可以缓存消息,在缓存消息没有达到上限时,消息发送者将消息发送到该通道后立即返回。如果缓存消息数量达到设定的容量,则消息发送者发送消息后会被阻塞,直到消息队列中有空间为止或者超时。对于消息接收者正好相反,尝试获取消息时,如果队列中有消息会立即返回,如果队列中没有消息则会一直阻塞直到超时(需要设定超时时间,不设定的话一直阻塞)。
PriorityChannel:一个排序队列。默认是根据消息头中的“priority”属性值进行排序,也可以通过实现Comparator
RendezvousChannel:类似于QueueChannel,只是容量为0。也就是发送者发送消息时,接受者必须将其接收才会返回,否则一直被阻塞。接受者也是一样。
DirectChannel:用于点对点场景,但实现的是PublishSubscribeChannel接口,因此该通道会将消息直接分发给接收者,和PublishSubscribeChannel的区别就是,接收者只有一个。除此之外,DirectChannel还有一个最重要的特点就是发送和接收双方处于一个线程当中,如下:发送者发送消息->接收者接收消息并触发处理操作->返回。Spring Integration中的缺省的通道就是DirectChannel。例如
ExecutorChannel:用于点对点场景,和DirectChannel的配置相同,但主要的区别是ExecutorChannel将消息分发的操作委派给一个TaskExecutor的实例来进行,因此发送消息时不会进行阻塞。
Scoped Channel:暂不理解。
可以对消息进行处理的地方。包括如下的主要构件:
可以对消息的内容或者结构进行修改,最常见的是修改消息内容的格式,或者对消息头的内容进行添加、修改和删除。用法如下:
method="transform" output-channel="outChannel"/>
决定一个消息是否被过滤掉,只有没有被过滤掉的消息才发送到输出通道上。filter多用于publish subscribe模式中,很多consumer消费者可以收到相同的message并且可以用filter来接收指定类型的消息并将其加工处理。
ref="exampleObject" method="someBooleanReturningMethod"/>
路由器,通常是一个输入通道,多个输出通道。根据消息头或者消息体的内容,决定将消息转发到哪个输出通道上。
对应一个输入通道,多个输出通道。Splitter把消息从输入通道上分割发送到它的输出通道上。比如用于把一个复合型的payload负载分割成很多子负载payloads,发送到多个输出通道上。
集合器,和splitter对应。用于把多种message组合成一个单一的message。事实上aggregator比splitter要复杂一些,因为它需要维持message的状态,决定什么时候提供组合,什么时候超时timeout,甚至可以将一个局部的结果放弃,并发送到一个隔离的channel里。Spring integration提供了一个CompletionStrategy来配置timeout超时,是否在超时的时候发送一个结果并且废弃这个channel(这里应该是说消息废弃这个channel)。Aggregator在聚合时,会将消息头中消息ID相同的消息进行聚合。
用来连接应用的接口和message framework消息框架的组件,一个输入的频道input message channel必须被设定,一个service activator的方法被执行并且返回了一个值,那么可以提供一个输出频道output message channel(如果消息提供自己的返回地址,那么这是可选的)。这个规则适用于所有的consumer endpoints。输入从input channel到service activator再到message handler,然后返回output message到service activator到output message channel。归根结底:被继承组件的主要对外接口。
连接一个消息通道和其他实体之间的对象。channel adapter也分inbound内绑定和outbound外绑定。
Inbound通道适配:通常的作用是将一个外部系统的资源进行转换,通过消息通道输送到系统中,用于进行后续的处理。
Outbound通道适配:将系统中的资源通过消息通道发送给Outbound通道适配,然后该“Outbound通道适配”将其转换为外部的资源。
举例:
Spring Integration目前支持的常用channel-adapter如下:
File
FTP/FTPS
HTTP
JDBC
SFTP
XML
等等。
Spring Integration下载远程服务器可以使用
1) 下载一次后,如果本地文件已经存在,就不会重复下载。
2) 即使将本地文件进行删除,此时虽然会下载,但下载到本地后不会将文件对象装载到channel中进行后续的处理。
Spring社区中也没有好的处理办法,这种情况下,可以使用下载到本地后对文件进行改名的方法进行规避,使用方法如下,见红色部分:
id="sftpInboundAdapter"
session-factory="sftpSessionFactory"
remote-directory="/srv/data/stc/crm/"
local-directory="file:///srv/data/stc/crm/upload"
local-filename-generator-expression="substring(0,lastIndexOf('.'))
+ '_' + T(java.lang.System).currentTimeMillis()
+ substring(lastIndexOf('.'))"
channel="downloadFileChannel"
delete-remote-files="true"
auto-startup="true"
filter="entryListFilter">
进行了上面的配置后,下载下来的文件名会变成原来的文件名中加上了时间戳,这样就规避了上述的问题,如果也许需要还原原来的文件名,可以在进入channel后的后续的处理中再进行处理。
context = new FileSystemXmlApplicationContext("/conf/stc-job.xml");
SourcePollingChannelAdapter adapter = context.getBean("sftpInboundAdapter", SourcePollingChannelAdapter.class);
adapter.stop(); //停止工作
adapter.start(); //开始工作
Spring Integration的配置如下:
channel="inputFileChannel" filename-pattern="*.csv">
output-channel="jobLaunchRequestChannel"
ref="fileToJobLaunchRequestAdapter"
method="adapt"/>
其中,红色stcJob为Spring batch的job名称,需要做如下的配置:
…….
对于Spring Job的触发类FileToJobLaunchRequestAdapter,代码如下:
public class FileToJobLaunchRequestAdapter implements InitializingBean
{
private Job job;
public void setJob(Job job)
{
this.job = job;
}
public void afterPropertiesSet() throws Exception
{
Assert.notNull(job, "A Job must be provided");
}
@ServiceActivator
public JobLaunchRequest adapt(File file) throws NoSuchJobException
{
String fileName = file.getAbsolutePath();
if (!fileName.startsWith("/"))
{
fileName = "/" + fileName;
}
fileName = "file://" + fileName;
String outPutPath = "file:///srv/smartcare/export/";
String outPutFilePath = outPutPath + file.getName();
String outPutTmpFilePath = outPutFilePath;
JobParameters jobParameters = new JobParametersBuilder().addString("input.file.path",
fileName)
.addString("output.file.path", outPutTmpFilePath)
.addString("file.src", outPutTmpFilePath)
.addString("file.original.name", file.getName())
.addString("file.dst", outPutFilePath)
.addLong("time.stamp", System.currentTimeMillis())
.toJobParameters();
//确保job多次执行时,通过让jobParameters发生变化,从而区分job
if (job.getJobParametersIncrementer() != null)
{
jobParameters = job.getJobParametersIncrementer()
.getNext(jobParameters);
}
return new JobLaunchRequest(job, jobParameters);
}
public void output(File file)
{
System.out.println("receive file = " + file.getName() + ", path = "
+ file.getPath());
return;
}
}
Java代码:
Message
sftpChannel.send(message);
sftpChannel为Spring integration的channel名称。
在Spring Integration的配置文件中,做如下的配置:
session-factory="sftpSessionFactory"
channel="outFileChannel"
charset="UTF-8"
temporary-file-suffix=".uploading"
remote-directory="/srv"/>
其中,sftpChannel的名称等于outFileChannel就可以了。