Camel要素
Apache Camel相关代码已经上传GitHub,需要的自取:GitHub - Apache Camel 完整Demo
如果觉得还行,麻烦点个Star
1. Endpoint控制端点
Apache Camel中关于Endpoint最直白的解释就是:
Camel作为系统集成的基础服务组件,在以及编排好的路由规则中,和其他系统进行通信的设定点。
这个"其他系统",可以是存在于本地或者远程的文件系统,可以是进行业务处理的订单系统,可以是消息队列服务,可以是提供了访问地址、访问IP、访问路径的任何服务。
Apache Camel利用自身提供的广泛的通信协议支持,使这里的"通信"动作可以采用大多数已知的协议,例如RPC协议、JMS协议、FTP协议、HTTP协议等....
Camel中的Endpoint控制端点使用URL的方式描述对目标系统的通信。
例如以下URL描述了对外部MQ服务的通信,消息格式是Stomp:
// 以下代码表示从名为test的MQ队列中接收消息,消息格式为stomp // 用户名为username,监听本地端口61613 from("stomp:queue:test?tcp://localhost:61613&login=username")
// 以下代码表示将消息发送到名为test的MQ队列中,消息格式为stomp to("stomp:queue:test?tcp://localhost:61613&login=username"); |
我们也可以使用HTTP协议和某一个外部系统进行通信。
// 主动向http URI描述的路径发出请求(http的URI笔者不需要再介绍了吧) from("http://localhost:8080/dbk.manager.web/queryOrgDetailById")
// 将上一个路由元素上Message Out中消息作为请求内容, // 向http URI描述的路径发出请求 // 注意,Message Out中的Body内容将作为数据流映射到Http Request Body中 to("http://localhost:8080/dbk.manager.web/queryOrgDetailById") |
以上示例中,要注意"from"部分的说明!!
from("http://localhost:8080/dbk.manager.web/queryOrgDetailById")
这个是主动向目标http url发起访问。
from("jetty:http://0.0.0.0:8282/doHelloWorld")
这个是向外部提供服务,使用jetty开头。
2. Exchange和Message
数据消息已经在我们编排好的业务路径上进行传递,通过我们自定义的消息转换方式或者Apache Camel提供的消息转换方式进行消息格式转换。
那么为了完成这些消息,消息转换过程Camel中的消息必须使用统一的消息描述格式,并且保证路径上的控制端点都能存取消息。
Camel提供的Exchange要素帮助开发人员在控制端点到处理器、处理器到处理器的路由过程中完成消息的同一描述。
一个Exchange元素的结构如下图所示:
Exchange中的基本属性
ExchangeID:
一个Exchange贯穿着整个编排的路由规则,ExchangeID就是它的唯一编号信息,同一个路由规则的不同实例(对路由规则分别独立的两次调用),ExchangeID不相同。
fromEndpoint:
表示exchange实例初始来源的Endpoint控制端点(类的实例),一般来说就是开发人员设置路由时由"from"关键字所表示的Endpoint。
properties:
Exchange对象贯穿整个路由执行过程中的控制端点、处理器甚至还有表达式、路由条件判断。
为了让这些元素共享一些开发人员自定义的参数配置信息,Exchange以K-V结构提供了这样的参数存储方式。
and
在org.apache.camel.impl.DefaultExchange类中,对应properties的源代码如下所示:
...... public Map<String, Object> getProperties() { if (properties == null) { properties = new ConcurrentHashMap<String, Object>(); } return properties; } ...... |
Pattern:
Exception:
如果在处理器Processor处理过程中,开发人员需要抛出异常并终止整个消息路由的执行过程,可以通过设置Exchange中的exception属性实现
Exchange中的Message
Exchange中还有两个重要属性 inMessage 和 outMessage。
这两个属性分别代表Exchange在某个处理元素(处理器、表达式等)上的输入消息和输出消息。
当控制端点和处理器、处理器和处理器之间的Message在Exchange中传递时,Exchange会自动将上一个元素的输出值作为这个元素的输入值进行使用。
如果再上一个处理器中,开发人员没有在Exchange中设置任何out message内容(即Exchange中out属性为null),那么上一个处理器中的in message 内容将作为这个处理器的in message内容。
需要注意一下:
在DefaultExchange类中关于getOut()方法的实现,有这样的代码片段:
...... public Message getOut() { // lazy create if (out == null) { out = (in != null && in instanceof MessageSupport) ? ((MessageSupport)in).newInstance() : new DefaultMessage(); configureMessage(out); } return out; } ...... |
所以在处理器中对out message属性赋值,并不需要开发人员明确的"new"一个Message对象。
只需要调用getOut()方法,就可以完成out message属性赋值。
我们看下原博中的一个栗子:
以下路由代码片段在fromEndpoint后,连续进入两个Processor处理器,且Exchange的ExchangePattern为InOut。
我们来观察从第一个处理处理完后,到第二个处理收到消息时Exchange对象中的各个属性产生的变化:
...... from("jetty:http://0.0.0.0:8282/doHelloWorld") .process(new HttpProcessor()) .process(new OtherProcessor()) ...... |
第一个HttpProcessor执行末尾时,Exchange中的属性
上图显示了当前内存区域中,Exchange对象的id为452,fromEndpoint属性是一个JettyHttpEndpoint的实例,对象id为479。
注意两个重要的inMessage和outMessage,它们分别是HttpMessage的实例(对象id467)和DefaultMessage的实例(对象id476),
这里说明一下无论是HttpMessage还是DefaultMessage,它们都是org.apache.camel.Message接口的实现。
outMessage中的body部分存储了一个字符串信息,我们随后验证一下信息在下一个OtherProcessor处理器中的记录方式。
第二个OtherProcessor开始执行时,Exchange中的属性
可以看到HttpProcessor处理器中outMessage的Message对象作为了这个OtherProcessor处理器的inMessage属性,
对象的id编号都是476,说明他们使用的内存区域都是相同的,是同一个对象。
Excahnge对象的其它信息也从HttpProcessor处理器原封不动的传递到了OtherProcessor处理器。
每一个Message(无论是inMessage还是outMessage)对象主要包括四个属性:MessageID、Header、Body、Attachment。
MessageID:
在系统开发阶段,提供给开发人员使用的,标示信息对象唯一性的属性,这个属性可以没有值。
Header:
消息结构中的"头部"信息,在这个属性中的信息采用K-V的方式进行存储,并可以随着Message对象的传递将信息带到下一个参与路由的元素中。
需要注意的是,在org.apache.camel.impl.DefaultMessage中对headers属性的实现是一个名叫org.apache.camel.util.CaseInsensitiveMap的类。
看这个类的名字就知道:headers属性的特点是忽略大小写。也就是说:
...... outMessage.setHeader("testHeader", "headerValue"); outMessage.setHeader("TESTHEADER", "headerValue"); outMessage.setHeader("testheader", "HEADERVALUE"); ...... |
以上代码片段设置后,Message中的Headers属性只有一个K-V键值对信息,且以最后一次设置的testheader为准。
Body:
Message的业务信息内容存放在这里。
Attachement:
Message中使用attachement属性存储各种文件内容信息,以便这些文件内容在Camel路由的各个元素间进行流转。
attachement同样使用K-V键值对形式进行文件内容的存储。
但不同的是,这里的V是一个javax.activation.DataHandler类型的对象。
Processor处理器
Camel中另一个重要的元素是Processor处理器,它用于接收从控制端点、路由选择条件又或者另一个处理器的Exchange中传来的消息,并进行处理。
Camel核心包和各个Plugin组件都提供了很多Processor的实现,开发人员也可以通过实现org.apache.camel.Processor接口自定义处理器(后者是通常做法)。
既然是做编码,那么我们自然可以在自定义的Processor处理器中做很多事情。
这些事情可能包括处理业务逻辑、建立数据库连接去做业务数据存储、建立和某个第三方业务系统的RPC连接,但是我们一般不会那样做——那是Endpoint的工作。
Processor处理器中最主要的工作是进行业务数据格式的转换和中间数据的临时存储。
这样做是因为Processor处理器是Camel编排的路由中,主要进行Exchange输入输出消息交换的地方。
不过开发人员当然可以在Processor处理器中连接数据库。
例如开发人员需要根据上一个Endpoint中携带的“订单编号前缀”信息,在Processor处理器中连接到一个独立的数据库中(或者缓存服务中)查找其对应的路由信息,以便动态决定下一个路由路径。
由于Camel支持和Java语言的spring框架无缝集成,所以要在Processor处理器中操作数据库只需要进行非常简单的配置。