采用Apache Camel的开源系统集成方案与Fuse IDE的使用

采用Apache Camel的开源系统集成方案与Fuse IDE的使用

原文链接 https://dzone.com/articles/open-source-integration-apache

通过系统集成工具(项目),可以使不同平台上的多个应用以多种传输方式进行“交谈“。但可以想像,企业应用的扩张会使应用间的“交谈”急剧复杂化。这些复杂性主要由两个因素导致:

  1. 难以协调程序与传输的细节,以及
  2. 对于系统集成问题一直没有好的解决方案

你自己可以很轻松地让应用通过API或其他方式进行交互。我肯定每个人都知道如何将JMS消息发送到指定的消息代理;虽然这需要对JMS规范有一定深入的了解,但很多开发者却没有。但最重要的是如果要将该JMS消息发送到其他应用呢?你不仅需要小心翼翼地将JMS消息转发到另一个应用,还要了解下这个应用的相关概念。如果好几个应用要加入进来呢?相信你会很头疼吧。

如果不考虑与协议和API的交互细节,我们可以从高级的角度来设计应用间的交互。幸运的是,已经有很多规范的解决方案来应对企业集成问题。Gregor Hohpe与Bobby Woolfe的著作《企业系统集成模式:设计、构建与部署消息解决方案》将企业架构的设计经验锤炼成65个企业系统集成模式(EIP)。虽然很不错,但我们还是要自己手写这些模式中的所有代码;它们只是一些建议,但却不是拿来即用的解决方案。

Apache Camel是为解决这两个问题而生的。在本文中我会先对Camel做一个简单介绍。然后带大家使用Camel解决一个系统集成中的问题。最后展示一个新颖的图形化工具来简化Camel的使用。

Camel是什么?

Apache Camel是一个关注于简化系统集成的开源java框架。它提供了:

  • 广泛使用的EIP的具体实现

  • 与各种传输协议和API的连接性

  • 连接EIP与传输协议的方便易用的DSL

图1(选自《Camel in Action》)展示了以上三个部分在Camel中对应的概念。为了便于理解Camel的组织结构,我会为大家介绍Component、Endpoint、Processor与DSL。除此以外还有很多其他概念,但不在本文讨论范围之内。

Figure 1: High level view of Camel's architecture (from Camel in Action).

图1: Camel架构概览

Component是Camel中的扩展点,它为其他系统提供了可连接性。Camel的核心(core)项目非常小,在降低了依赖度的同时提升了可嵌入性,因此它只包含了13个必要的Component。但在core以外却有着超过80个的Component。为了将这些系统暴露出来,Component提供了一个Endpoint接口。通过使用URI以一种统一的方式在Endpoint间发送或接收数据。例如,从JMS队列aQueue中接收数据并发送到文件夹"/tmp"中,你可以使用像 "jms:aQueue"和"file:/tmp"的URI。

Processor用于Endpoint间消息的处理与传递。在Camel中,EIP被定义为Processor或Processor集。在编写本文时,Camel提供了超过40个EIP书中介绍的EIP模式和很多实用的Processor。

为了将Processor与Endpoint连接起来,Camel定义了多种常见编程语言的DSL。Camel也支持使用XML制定路由规则。如下是一些使用不同语言编写的具有同等功能的DSL。

  • Java DSL
from ("file:/tmp").to("jms:aQueue");
  • Spring DSL
<route>
  <from uri="file:/tmp"/>
  <to uri="jms:aQueue"/>
</route>
  • Scala DSL
from "file:/tmp" -> "jms:aQueue"

在上面的示例中我们定义了一个路由规则,它会将"/tmp"文件夹中的文件加载到内存,然后用文件中的内容创建一个JMS消息并将消息发送到名为aQueue的JMS消息队列中。

这些就是支撑Camel的底层概念。因为Camel中添加了许多其他有趣的特性,我推荐大家读下我与Claus Ibsen撰写的《Camel in Action》,以便对Camel的功能有一个全面的认识。下面是一些Camel中的其他特性:

  • 灵活的数据格式化与类型转换器:简化CSV, EDI, Flatpack, HL7, JAXB, JSON, XmlBeans, XStream, Zip等消息格式间的转换

  • 灵活的语言:创建DSL中要用到的表达式或断言。这些语言包括EL, JXPath, Mvel, OGNL, BeanShell, JavaScript, Groovy, Python, PHP, Ruby, SQL, XPath, XQuery等等。

  • Camel中bean与POJO整合的支持。

  • 利用消息方式测试分布式与异步系统的支持。

  • 。。。。

骑士汽配(Rider Auto Parts)

接下来的这个例子是贯穿《Camel in Action》全书的虚拟摩托车配件业务。这个名为Rider Auto Parts(骑士汽车配件)的公司要为摩托车制造商提供配件。在过去的几年中,他们曾几次更改订单的接收方式。最开始,订单通过上传CSV文件到FTP服务器来提交。后来消息格式又变为XML。现在他们又弄了一个网站,通过HTTP将XML消息发送到网站上来提交订单。

骑士汽配要求新客户通过网站来提交订单,但由于与老主顾签订的服务等级协议(SLAs),他们必须兼容原来的消息格式与接口。在进行处理之前,所有的消息要转换成POJO格式。图2展示了订单处理系统的抽象表示。

Figure 2: High level view of order processing at Rider Auto Parts (from Camel in Action).

图2: 骑士汽配订单处理系统

我们需要为上图中的"Ride order frontend"找到最佳的实现方案。我们先来看看《企业集成模式》中的表示方法。

采用EIP的解决方案

骑士汽配面临着一个很常见的问题;经过几年的运营,由于业务需要,软件需要兼容已经过时的传输协议与数据格式。采用EIP模式我们可以设想出如下的解决方案。

Figure 3: This shows the solution to Rider Auto Parts integration problem using notation from the Enterprise Integration Patterns book.

图3:采用EIP表示法的解决方案

在这里我们用到了几种模式:

  1. 两个消息Endpoint;分别用于FTP与HTTP的连接

  2. 将这两个endpoint中得到的消息注入incomingOrders消息管道。

  3. 用于处理incomingOrders消息管道中的数据并将其路由到两个消息解释器(Message Translator)的Content-Based Router。正如它的名字那样,路由的目的地取决于消息的内容。这里我们需要根据文件的类型(CSV/XML)来进行路由。

  4. 两个消息解释器都会将消息内容转换成POJO并传入orders消息管道。

使用一个Content-Based Router与多个消息解释器的部分称为标准化调节器(Normalizer)。组合模式图可以表述这个标准化调节器,但为了简单起见,这里只展示了它的子模式。

使用Camel的实现

正如前面所提到的,Camel默认只提供有限的核心组件。其他的组件作为独立的模块存在。在需要多种连接性的应用中,需要导入相应的Camel模块。列表1展示了Camel实现的骑士汽配所需的maven依赖。当然,你不一定非得用maven管理依赖——它只是添加依赖最简便的方式。这个列表中的依赖包括Camel core, ActiveMQ, JAXB序列化, CSV序列化与HTTP。为了简化问题,我决定使用File endpoint而非FTP,但如果使用FTP endpoint的话我们也要为项目添加camel-ftp模块依赖。

列表1:Camel实现的maven依赖

<dependencies>
    <!-- Core Camel -->
    <dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-core</artifactId>
      <version>${camel-version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-spring</artifactId>
      <version>${camel-version}</version>
    </dependency>

    <!-- Embedded ActiveMQ broker -->
    <dependency>
      <groupId>org.apache.activemq</groupId>
      <artifactId>activemq-core</artifactId>
      <version>${activemq-version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.xbean</groupId>
      <artifactId>xbean-spring</artifactId>
      <version>${xbean-spring-version}</version>
    </dependency>

    <!-- ActiveMQ connectivity for Camel -->           
    <dependency>
      <groupId>org.apache.activemq</groupId>
      <artifactId>activemq-camel</artifactId>
      <version>${activemq-version}</version>
    </dependency>

    <!-- Add support for JAXB marshaling -->
    <dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-jaxb</artifactId>
      <version>${camel-version}</version>
    </dependency>

    <!-- Add support for CSV marshaling -->   
    <dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-bindy</artifactId>
      <version>${camel-version}</version>
    </dependency>    

    <!-- Add support for HTTP -->
    <dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-jetty</artifactId>
      <version>${camel-version}</version>
    </dependency>

</dependencies>

虽然可以将Camel封装成一个独立的Java程序,但将它嵌入到容器中可能会更有用。因此,我们将使用Spring来加载Camel。实际上,整个解决方案(除了POJO与Maven脚本)可以很简洁的置于一个Spring XML文件中。因为我们采用了Spring XML来配置路由规则。Camel中的每个DSL都有不同的优缺点,但在本示例中我主要为大家展示一些与基于XML的路由规则配套的工具。Spring XML文件内容参见列表2

列表2: 配置了一个内嵌的ActiveMQ消息代理并用三个路由(route)初始化Camel上下文(Context)的完整Spring XML

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:amq="http://activemq.apache.org/schema/core"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://activemq.apache.org/camel/schema/spring
       http://activemq.apache.org/camel/schema/spring/camel-spring.xsd
       http://activemq.apache.org/schema/core
       http://activemq.apache.org/schema/core/activemq-core.xsd">

  <amq:broker brokerName="localhost" persistent="false" useJmx="false"/>

  <bean id="jms" class="org.apache.activemq.camel.component.ActiveMQComponent">
    <property name="brokerURL" value="vm://localhost" />
  </bean>

  <camelContext xmlns="http://camel.apache.org/schema/spring">
    <route id="FileToJMS">
      <from uri="file:target/placeorder" />
      <to uri="jms:incomingOrders" />
    </route>

    <route id="HTTPtoJMS">
      <from uri="jetty:http://0.0.0.0:8888/placeorder" />
      <inOnly uri="jms:incomingOrders" />
      <transform>
        <constant>OK</constant>
      </transform>
    </route>

    <route id="NormalizeMessageData">
      <from uri="jms:incomingOrders" />
      <convertBodyTo type="java.lang.String" />
      <choice>
        <when>
          <simple>${body} contains '?xml'</simple>
          <unmarshal>
            <jaxb contextPath="org.fusesource.camel" />
          </unmarshal>
          <to uri="jms:orders" />
        </when>
        <otherwise>
          <unmarshal>
            <bindy packages="org.fusesource.camel" type="Csv" />
          </unmarshal>
          <to uri="jms:orders" />
        </otherwise>
      </choice>     
    </route>
  </camelContext>
</beans>

在这个文件中我们首先启动了一个内嵌的ActiveMQ消息代理,然后将之与Camel连接。接着在camelContext元素中定义了路由规则。根据图3所示,我们需要从FTP(本文中使用File替代)与HTTP endpoint中接收订单,数据格式如列表3中所示。在Spring XML配置中我们使用了两个from元素定义入口endpoint。“FileToJMS"与"HTTPtoJMS"路由以from元素开始。两个from元素都连接到一个URI为“jms:incomingOrders”的producer(toinOnly元素),这会将消息发送到一个名为incomingOrders的ActiveMQ队列中。

列表3:传入消息格式;左边为XML右边为CSV

XMLCSV
<?xml version="1.0" encoding="UTF-8"?><br/><order name="motor" amount="1"/>"name", "amount"<br/>"brake pad", "2"

对于HTTP endpoint有几点需要注意。首先HTTP客户端需要得到响应,所以我们不得不进行处理。在Camel中我们可以完全控制客户端从HTTP endpoint处得到的响应。所有响应都由route定义中的最后一个方法决定。在这个例子中我们使用trsform将响应设为字符串常量“OK"。另外因为我们手动处理了请求结果,所以不希望从JMS incomingOrders队列中得到响应。使用inOnly元素我们可以以一种”发送就忘记“(向目的地发送消息后并不验证消息的接收)的方式向这个队列发送消息。

列表2中的"NormalizeMessageData"定义了一个Normalizer(标准化调节器), 它有一个Content-Based Router和两个Message Translator。首先我们指定了要从incomingOrders队列中取得消息。由choice,when,与otherwise元素实现了基于消息内容的路由。在示例中,我们希望将CSV与XML消息发送给不同的Message Translator。我们使用了一个Simple表示式来检查消息的数据格式,这个表示式可以检查消息体是否以<?xml标签开始。Simple是一个内置于Camel中的表达式语言。当然,这里主要是为了演示,在真实的案例中你可能需要更加全面的内容类型检查。

我们使用Camel提供的数据格式化将XML与CSV数据分解到一个Order对象中。列表2展示了如何使用unmarshal元素定义数据格式化。在列表5中,Order类使用了JAXB和Camel Bindy注解来表示对象与XML和CSV之间的映射。

列表5:Order领域类采用JAXB/Bindy注解实现与XML/CSV之间的映射

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@CsvRecord(separator = ",", skipFirstLine = true)
public class Order implements Serializable {
    @XmlAttribute
    @DataField(pos = 1)
    private String name;

    @XmlAttribute
    @DataField(pos = 2)
    private int amount;

   public Order() {
    }

    public Order(String name, int amount) {
        this.name = name;
        this.amount = amount;
    }
}

到这里,成功标准化后的消息会被发送到orders队列,然后由骑士汽配的其他程序做进一步处理。

使用Fuse IDE for Camel实现

正如我们看到的,Apache Camel极大地简化了系统集成。支持多种语言的DSL设计简洁,功能强大且易于阅读——所有这都是以提升开发者的效率为目的。本着提高生产率的目的,FuseSource专门为Camel开发了一款名为Fuse IDE for Camel的集成开发环境。

Fuse IDE是一款基于Eclipse的工具,通过在画布上拖拽并连接EIP图标来生成路由规则。IDE会在后台生成Spring XMl格式的Camel路由规则,所以你不再需要深究Camel DSL的细节。其实你只需要创建一个像图3那样的工程图,然后用Fuse IDE生成Camel程序就可以了。

图4展示了Fuse IDE designer(设计器)加载了列表2配置后的界面。

Figure 4: Spring XML file in Listing 2 loaded into Fuse IDE’s designer.

图4:加载了列表2中Spring XML后的Fuse IDE设计器

从设计器视图(disigner view)我们可以以图形化的方式预览在Spring XML中创建的路由规则。同时我们也可以改变EIP图标的连接方式、endpoint URI的属性或路由中的其他事物,等等。另外,Fuse IDE的1.0版本会支持Apache Camel中的全部EIP与组件——所以不需要为了Camel的某些特性而苦等2.0版本。

本文以手写Spring XML配置开始。虽然这样做不会对你产生误导——但却也不是很有必要。实际上,因为Fuse IDE支持Camel Spring XML与设计器视图间的相互转换,你可以选择自己喜欢的方式来设计路由规则。图5展示了Spring XML文件的源码视图(source view)。在源码视图中的操作都会对应到设计器视图(designer view)上。

图5在原文中没有

在设计好路由之后,你可以使用Fuse IDE提供的另一个功能:根据Camel Spring XMl文件生成JUnit测试用例。在测试通过之后,你可以在本地Spring容器中进行调试,或直接部署到容器中——这些都是Fuse IDE提供的。Fuse IDE后继会提供更多功能,所以Camel的开发会越来越简单。

总结

本文中展示了一个项目集成开发者可能遇到的两个常见问题:应用与传输的细节处理、为系统集成问题找到好的解决方案。Apache Camel很好地解决了这两个问题。正如示例所体现的,使用Camel解决系统集成问题是很直观的,并以简洁的路由定义作为产出。通过它我们看到使用Fuse IDE for Camel进行Camel开发是多么简单。

转载于:https://my.oschina.net/wisedream/blog/533890

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值