Camel部署与路由:从JBoss到OSGi及注解路由的实践
1. Camel在JBoss AS中的部署
在启动JBoss AS后,控制台会输出启动信息,如:
15:16:43,882 INFO [ServerImpl] JBoss (Microcontainer) [5.1.0.GA (build: SVNTag=JBoss_5_1_0_GA date=200905221053)] Started in 27s:452ms
之后,将WAR文件复制到指定目录:
cp target/riderautoparts-war-jboss-1.0.war ~/jboss/server/default/deploy
可以通过查看JBoss控制台来了解部署进度。例如,Camel会报告它已获取JBoss类加载器:
15:18:42,636 INFO [STDOUT] 2010-05-30 15:18:42,636 [main] INFO CamelContextFactoryBean - Using custom PackageScanClassResolver: org.apache.camel.jboss.JBossPackageScanClassResolver@75d8af
JBoss AS使用嵌入式的Apache Tomcat作为Servlet容器,应用的Web服务可通过以下URL访问:
http://localhost:8080/riderautoparts-war-jboss-1.0/services/inventory?wsdl
以下是在JBoss应用服务器中部署Camel的优缺点:
| 优点 | 缺点 |
| — | — |
| 利用JBoss AS容器 | 需要特殊的Camel JBoss组件来解决类加载问题 |
| 应用可利用Java EE应用服务器提供的功能 | |
| JBoss AS容器管理Camel的生命周期 | |
| 受益于应用服务器的管理和监控功能 | |
| 提供熟悉的运行时平台 | |
2. Camel与OSGi
OSGi是Java平台的分层模块系统,提供完整的动态组件模型,支持热部署。Apache Camel的所有JAR文件都符合OSGi规范,可在OSGi容器中部署。下面以Apache Karaf OSGi运行时为例,介绍如何准备和部署Rider Auto Parts应用。
2.1 设置Maven生成OSGi包
在 pom.xml 文件中,将 packaging 元素设置为 bundle :
<packaging>bundle</packaging>
使用Apache Felix Maven Bundle插件生成JAR文件中的 MANIFEST.MF 条目,在 pom.xml 的 <build> 部分添加以下配置:
<build>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>2.1.0</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-Name>${project.artifactId}</Bundle-Name>
<Bundle-SymbolicName>riderautoparts-osgi</Bundle-SymbolicName>
<Export-Package>
camelinaction,
camelinaction.inventory
</Export-Package>
<Import-Package>*</Import-Package>
<DynamicImport-Package>*</DynamicImport-Package>
<Implementation-Title>Rider Auto Parts OSGi</Implementation-Title>
<Implementation-Version>${project.version}</Implementation-Version>
</instructions>
</configuration>
</plugin>
</build>
该插件可设置要导入和导出的包,这里设置导出 camelinaction 和 camelinaction.inventory 两个包。运行 mvn package 目标,可在 target/classes/META-INF 目录中看到生成的 MANIFEST.MF 条目。
2.2 安装和运行Apache Karaf
从 http://karaf.apache.org 下载并安装最新版本的Apache Karaf(编写时为Apache Karaf 2.1.2),安装只需解压zip文件。
启动Karaf:
- Unix系统: bin/karaf
- Windows系统: bin/karaf.bat
在安装Rider Auto Parts应用之前,需要安装Camel和Apache CXF。通过以下命令添加Camel特性描述:
features:addUrl mvn:org.apache.camel.karaf/apache-camel/2.5.0/xml/features
由于Apache Karaf 2.1版本在使用Apache CXF时存在问题,需要下载一个文件并保存到 etc 目录,命名为 custom.properties :
curl http://svn.apache.org/repos/asf/servicemix/smx4/features/trunk/assembly/src/main/filtered-resources/etc/jre.properties > etc/custom.properties
该问题将在2.2版本中修复。
使用以下命令查看可用特性:
features:list
安装示例应用所需的特性:
features:install http
features:install camel
features:install camel-cxf
2.3 部署示例应用
Karaf可从多种源安装OSGi包,如文件系统或本地Maven仓库。使用Maven安装时,先将应用安装到本地Maven仓库:
mvn install
然后在Karaf shell中使用以下命令部署应用:
osgi:install mvn:com.camelinaction/riderautoparts-osgi/1.0
安装后,Karaf会输出分配给安装包的Bundle ID,例如:
Bundle ID: 98
使用 osgi:list 命令查看应用安装情况:
[98] [Installed] [] [] [60] riderautoparts-osgi (1.0.0)
应用默认未启动,使用以下命令启动:
osgi:start 98
再次使用 osgi:list 命令查看应用状态:
[98] [Installed] [] [Started] [60] riderautoparts-osgi (1.0.0)
可以使用 log:display 命令查看日志,确认Apache Camel已启动:
15:46:32,396 | INFO | ExtenderThread-6 | DefaultCamelContext | e.camel.impl.DefaultCamelContext 1025 | Apache Camel 2.5.0 (CamelContext: myCamelContext) started
使用SoapUI发送测试请求,WSDL文件可通过以下URL访问:
http://localhost:9000/inventory?wsdl
测试完成后,使用以下命令停止OSGi容器:
osgi:shutdown
以下是在OSGi容器中部署Camel的优缺点:
| 优点 | 缺点 |
| — | — |
| 利用OSGi实现模块化 | 涉及OSGi的学习曲线 |
| 提供类加载器隔离和热部署 | 许多第三方框架不支持OSGi |
| 开源社区和大厂商支持OSGi | 需要额外努力确定模块的包导入和导出 |
3. 使用注解进行消息路由
Camel提供基于注解的路由功能,允许使用普通Java bean进行路由,无需编写DSL代码。以下以Rider Auto Parts的库存更新为例进行说明。
3.1 Rider Auto Parts的库存更新场景
Rider Auto Parts的供应商定期发送库存更新信息,通过JMS的 partnerInventoryUpdate 队列进入系统,更新 inventoryDB 数据库。假设有一个 InventoryUpdater 类可将库存更新信息写入数据库:
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
public class InventoryUpdater {
private JdbcTemplate jdbc;
public InventoryUpdater(DataSource ds) {
jdbc = new JdbcTemplate(ds);
}
public void updateInventory(Inventory inventory) {
jdbc.execute(toSql(inventory));
}
private String toSql(Inventory inventory) {
Object[] args = new Object[] {
inventory.getSupplierId(), inventory.getPartId(),
inventory.getName(), inventory.getAmount()};
return String.format("insert into partner_inventory " +
"(supplier_id, part_id, name, amount) values " +
"('%s', '%s', '%s', '%s')", args);
}
}
3.2 使用 @Consume 接收消息
使用 @Consume 注解将 partnerInventoryUpdate 队列直接连接到 updateInventory 方法:
import javax.sql.DataSource;
import org.apache.camel.Consume;
import org.springframework.jdbc.core.JdbcTemplate;
public class InventoryUpdater {
...
@Consume(uri = "jms:partnerInventoryUpdate")
public void updateInventory(Inventory inventory) {
jdbc.execute(toSql(inventory));
}
...
}
添加该注解后,Camel会从 partnerInventoryUpdate 队列消费消息,并调用 updateInventory 方法。 @Consume 注解接受任何Camel端点URI。
运行以下Maven命令测试示例:
mvn clean test
测试用例会向 partnerInventoryUpdate 队列发送消息,并检查更新是否写入数据库。
3.3 加载注解类到Camel
使用这些注解需要设置必要的依赖: camel-core 和 camel-spring ,并使用 SpringCamelContext 作为基础应用。在Spring XML文件中创建 SpringCamelContext :
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring" />
这会在Spring应用上下文中注册 CamelBeanPostProcessor ,该处理器会检查新注册的bean是否有 @Consume 和 @Produce 注解,并将bean连接到指定的Camel端点。
在Spring XML文件中创建bean:
<bean class="camelinaction.InventoryUpdater"/>
3.4 引用共享端点
如果多个bean从相同的端点URI消费消息,可以定义一个端点并从每个bean引用它。在上下文设置Camel端点:
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
<endpoint id="inventoryQueue" uri="jms:partnerInventoryUpdate"/>
</camelContext>
在 @Consume 注解中引用该端点:
@Consume(ref = "inventoryQueue")
public void updateInventory(Inventory inventory) {
jdbc.execute(toSql(inventory));
}
3.5 指定使用的CamelContext
如果在一个Spring XML文件中定义了两个 CamelContext 元素,需要确保 @Consume 注解指定应用的 CamelContext ,避免为每个 CamelContext 创建新的消费者。
4. 总结与最佳实践
- 确保可靠关闭 :花时间配置和测试应用,确保在计划维护、升级或出现意外问题时能可靠关闭,避免对业务产生负面影响。
- 使用现有运行时环境 :Camel灵活可嵌入现有生产环境,无需为使用Camel引入新的生产环境,应尽早在项目中测试应用能否在现有环境中部署和运行。
Camel部署与路由:从JBoss到OSGi及注解路由的实践
5. 隐藏Camel API的意义与挑战
在某些场景下,我们希望限制对Camel的依赖,这就需要隐藏Camel API。隐藏这些API不仅能降低系统对Camel的依赖程度,还能为解决集成问题提供新的思路。然而,这也带来了一些挑战,比如如何在不使用Camel DSL的情况下实现消息路由和处理。接下来,我们将深入探讨如何利用Camel的注解和相关技术来实现这一目标。
6. 注解路由的进阶应用
6.1 @Produce 注解的使用
前面提到了 @Consume 注解用于接收消息,而 @Produce 注解则用于向Camel端点发送消息。例如,我们有一个需要发送库存更新请求的场景,以下是一个简单的示例:
import org.apache.camel.Produce;
import org.apache.camel.ProducerTemplate;
public class InventoryRequestSender {
@Produce(uri = "jms:partnerInventoryRequest")
private ProducerTemplate producerTemplate;
public void sendInventoryRequest(InventoryRequest request) {
producerTemplate.sendBody(request);
}
}
在这个示例中, @Produce 注解将 producerTemplate 与 jms:partnerInventoryRequest 端点关联起来,通过调用 sendBody 方法,我们可以将 InventoryRequest 对象发送到指定端点。
6.2 结合 @Consume 和 @Produce 实现消息流转
我们可以将 @Consume 和 @Produce 注解结合使用,实现消息的完整流转。例如,当接收到供应商的库存更新消息后,经过处理再发送一个确认消息给供应商:
import javax.sql.DataSource;
import org.apache.camel.Consume;
import org.apache.camel.Produce;
import org.apache.camel.ProducerTemplate;
import org.springframework.jdbc.core.JdbcTemplate;
public class InventoryProcessor {
private JdbcTemplate jdbc;
@Produce(uri = "jms:partnerInventoryConfirmation")
private ProducerTemplate producerTemplate;
public InventoryProcessor(DataSource ds) {
jdbc = new JdbcTemplate(ds);
}
@Consume(uri = "jms:partnerInventoryUpdate")
public void processInventoryUpdate(Inventory inventory) {
jdbc.execute(toSql(inventory));
InventoryConfirmation confirmation = new InventoryConfirmation(inventory.getSupplierId());
producerTemplate.sendBody(confirmation);
}
private String toSql(Inventory inventory) {
Object[] args = new Object[] {
inventory.getSupplierId(), inventory.getPartId(),
inventory.getName(), inventory.getAmount()};
return String.format("insert into partner_inventory " +
"(supplier_id, part_id, name, amount) values " +
"('%s', '%s', '%s', '%s')", args);
}
}
在这个示例中, InventoryProcessor 类使用 @Consume 注解接收库存更新消息,处理后使用 @Produce 注解发送确认消息。
7. 隐藏中间件的实现思路
隐藏中间件可以让用户只看到业务接口,将远程传输和Camel API的复杂性隐藏在干净的客户端API之后。以下是实现这一目标的一般步骤:
1. 定义业务接口 :明确系统的业务功能,定义相应的接口。例如,定义一个 InventoryService 接口:
public interface InventoryService {
void updateInventory(Inventory inventory);
Inventory getInventory(String supplierId, String partId);
}
- 实现业务接口 :在实现类中使用Camel的注解和组件来完成具体的业务逻辑。例如:
import org.apache.camel.Consume;
import org.apache.camel.Produce;
import org.apache.camel.ProducerTemplate;
public class InventoryServiceImpl implements InventoryService {
@Produce(uri = "jms:partnerInventoryUpdate")
private ProducerTemplate updateProducer;
@Consume(uri = "jms:partnerInventoryResponse")
private ProducerTemplate responseConsumer;
@Override
public void updateInventory(Inventory inventory) {
updateProducer.sendBody(inventory);
}
@Override
public Inventory getInventory(String supplierId, String partId) {
// 发送查询请求
InventoryQuery query = new InventoryQuery(supplierId, partId);
updateProducer.sendBody(query);
// 接收响应
return (Inventory) responseConsumer.receiveBody();
}
}
- 提供客户端API :将业务接口暴露给客户端,客户端只需要调用接口方法,无需关心底层的Camel实现。
8. 总结与未来展望
通过上述的介绍,我们了解了Camel在不同环境下的部署方式,包括在JBoss AS和OSGi容器中的部署,以及如何使用注解进行消息路由和隐藏Camel API。以下是一些关键要点总结:
| 部署方式 | 优点 | 缺点 |
| — | — | — |
| JBoss AS | 利用容器功能、管理生命周期、提供熟悉平台 | 需要特殊组件解决类加载问题 |
| OSGi | 模块化、热部署、社区和厂商支持 | 学习曲线、第三方框架支持不足 |
在使用注解进行路由时, @Consume 和 @Produce 注解为我们提供了简单而强大的消息处理能力,同时隐藏Camel API和中间件可以降低系统对Camel的依赖,提高系统的可维护性和扩展性。
未来,随着技术的不断发展,Camel可能会在更多的场景中得到应用,我们也可以进一步探索如何结合其他技术,如微服务架构、云计算等,来提升系统的性能和可靠性。同时,对于OSGi等技术,我们可以深入学习其原理和应用,充分发挥其模块化和动态性的优势。
总之,掌握Camel的部署和路由技术,能够为我们解决复杂的集成问题提供有力的支持,帮助我们构建更加高效、灵活的系统。
超级会员免费看

2781

被折叠的 条评论
为什么被折叠?



