0%

需求

T2,是某厂商开发的ESB产品,用于证券公司交易相关系统之间的互相调用。该产品的设计思路是一套核心的组件实现消息通信,
通过不同的插件实现ESB的协议转换、消息路由、访问控制等功能。每个节点基于xml配置不同的插件,即可扮演不同的角色。

恰恰由于这种灵活性,导致其配置非常复杂,人工进行配置修改和检查很容易出错。再加上每个架构角色可能会有多个节点,重复性的工作让人厌烦。

为此,我想通过salt进行自动化的配置。主要需求如下:

  • 只定义最基本的、非冗余的数据,从这些数据生成所有的配置
  • 支持多个版本的部署
  • 自动实现停止服务、部署、启动服务等一系列操作
  • 支持升级包的部署

机制

salt适合固定的配置(state)部署多个实例(minion)的场景。尽管可以在pillar、state中使用python脚本,但仍无法满足本场景中需要的复杂度。
所以拟构建python应用,通过salt client API与salt master进行交互。

为了降低对使用者的要求,需要实现一个web界面。拟采用bottle.py。

设计

salt的“三驾马车”是 statepillargrains 。其中:

  • state是配置项(CI)的集合。配置项即可以是固定的内容,也可以是模板+数据,还可以是命令和脚本
  • pillar中定义数据并分配给minion。这些数据可以用于state中模板的上下文(context)数据
  • grains记录minion自身的属性。可以作为为minion分配pillar和state的依据,也可以用于模板的上下文

针对本文提到的场景,关系如下:

  • CI
    在svn中维护各个版本的所有配置项
  • datas
    在文件中维护部署需要的数据
  • pillars.top
    根据datas和各节点的grains数据,组合出每个minion的pillars
  • states.top
    根据各节点的grains和pillars数据,将CI分配到minion
  • pillars
    节点数据,其中可以引用grains
  • states
    节点状态,其中可以引用grains, pillars

pillar_roots目录结构

`pillarroots` 是 `/etc/salt/master`
中配置的参数,指定所有可用pillar的保存位置。
salt中可以按照环境划分不同的pillars,比如 `base` , `dev` , `prod`
等, 还可以指定多个pillar, 比如 `extpillar` 。

本文的场景中,不需要在salt中区分各种环境,故只使用 `base`
目录。其默认值为 /srv/pillar 。设计的目录结构如下:

1
2
3
4
5
6
/srv/pillar
├─ dev.yaml
├─ uat.yaml
├─ prod.yaml
├─ xxx.yaml
└─ top.sls

其中,yaml是部署配置,通过工具编辑; top.sls是pillar分配,通过脚本生成。

file_root目录结构

file_root/etc/salt/master
中配置的参数,指定所有可用state的保存位置。
salt中可以按照环境划分不同的states,比如 basedev , prod 等。

本文的场景中,不需要在salt中区分各种环境,故只使用 base
目录。其默认值为 /srv/salt 。设计的目录结构如下:

1
2
3
4
5
6
7
8
/srv/salt
├─ v0.1/
│ ├─ config items/ 配置项,可能是多个目录
│ └─ states/ salt state定义。会引用上面的 config item。
├─ v1.0/
├─ v1.0.1/
├─ vx.x.x/
└─ top.sls 状态分配,通过脚本生成

流程

  1. 从svn中获取需要的各个版本,放入 /srv/salt/
  2. 编辑 /srv/pillar 中的各个部署配置
  3. 生成 /srv/pillar/top.sls/srv/salt/top.sls
  4. 调用salt client API进行部署

快速开始

因为 JBOSS FUSE:你必须知道的那些事 ,而ServiceMix的核心组成部分是Apache Camel,所以“用Fuse开发路由”也就是“开发Camel路由”。

Camel提供了大量的开发工具,其中camel-archetype-blueprint 是一个maven
archetype
,可以基于 Blueprint,以依赖注入的方式配置CamelContext。下面快速创建一个demo:

1
2
3
4
5
6
7
mvn archetype:generate  \
-DarchetypeGroupId=org.apache.camel.archetypes \
-DarchetypeArtifactId=camel-archetype-blueprint \
-DarchetypeVersion=2.12.2 \
-DgroupId=thinkinside.demo.fuse \
-DartifactId=route-demo \
-Dversion=1.0.0-SNAPSHOT

会创建如下结构的一个工程:

pom.xml来看,这是一个 使用maven-bundle-plugin构建的OSGibundle工程

基于Blueprint装配Camel

工程的`META-INF/blueprint/blueprint.xml’文件是一个Blueprint配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:camel="http://camel.apache.org/schema/blueprint"
xsi:schemaLocation="
http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd">

<bean id="helloBean" class="thinkinside.demo.fuse.HelloBean">
<property name="say" value="Hi from Camel"/>
</bean>

<camelContext id="blueprintContext" trace="false" xmlns="http://camel.apache.org/schema/blueprint">
<route id="timerToLog">
<from uri="timer:foo?period=5000"/>
<setBody>
<method ref="helloBean" method="hello"/>
</setBody>
<log message="The message contains${body}"/>
<to uri="mock:result"/>
</route>
</camelContext>

</blueprint>

该配置文件中,定义了一个id为 blueprintContext 的Camel
Context。这个Context中定义了一个路由:

  1. 入口为一个Timer类型的Endpoint
  2. 使用预定义的bean为Message设置body
  3. 记录日志
  4. 出口为一个Mock类型的Endpoint

如果使用FuseIDE,可以看到图形化的配置界面:

部署到ServiceMix

执行 mvn package 后,得到 route-demo-1.0.0-SNAPSHOT.jar
,这是一个OSGi bundle。可以将jar文件部署到

$SERVICEMIX_HOME/deploy/
目录中。正常情况下,bundle的依赖关系被满足,该bundle会被自动启动。

从ServiceMix到Fuse

上述的过程也适用于JBoss Fuse。

但是Fuse对ServiceMix进行了再次封装,需要使用Fuse对应的版本。比如,=camel-archetype-blueprint=
的版本可能要使用 2.10.0.redhat-60024
这样的“Fuse版本号”,否则在部署到Fuse是可能会发生版本不匹配的问题。

Fuse提供了一个maven仓库,专门提供这种定制版本的组件,需要在maven中配置:

1
2
3
4
5
6
7
8
9
10
<repository>
<id>fusesource</id>
<url>http://repo.fusesource.com/nexus/content/groups/public/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</repository>

个人时代的软件开发

众所周知,软件开发一般需要编码、构建、测试、打包、发布等步骤,如下图:

G Code Code Build Build Code->Build Test Test Build->Test Package Package Test->Package Deploy Deploy Package->Deploy

在“个人英雄”时代,一个人完成所有的事情,一切都不是问题。

团队开发

很快,软件的规模就上升到了个人无法完成的程度。这就需要团队开发。在团队开发模式中,一定要解决代码共享的问题:

G cluster_process 开发过程 Code Code Build Build Code->Build repo1 代码库 Code->repo1 Test Test Build->Test repo2 配置库 Build->repo2 Package Package Test->Package Deploy Deploy Package->Deploy

图中的“代码库”,通常由版本管理软件负责,比如git,svn。

对于软件的配置信息,开发团队通常不会很十分在意——要么随源代码一起放入版本库,要么由“实施人员”临时配置。

开发和运维

企业应用的环境中,通常开发团队和运维团队是分开的。这就形成了一堵墙:

当软件系统从墙的一端(Dev)传递到另一端(Ops)时,会发生各种各样不可预知的情况发生。这些“异常”通常是由于交付了不正确的东西:

  • 开发人员不清楚生成环境,而运维人员又搞不定即没有规范又没有文档的配置
  • 开发过程中的不成熟版本进入了生成环境
  • ……

在开发团队和运维团队之间,也应该有一个信息交换和缓冲带,主要是:

G cluster_process 开发过程 Code Code Build Build Code->Build repo1 代码库 Code->repo1 Test Test Build->Test repo2 配置库 Build->repo2 Package Package Test->Package Deploy Deploy Package->Deploy repo3 交付库 Package->repo3 repo3->Deploy
  • 配置库

    有严格的环境(开发、测试、生产环境)区分,有版本管理的配置库,开发人员和运维人员各自维护自己需要的配置信息。

  • 交付库

    经过测试,符合要求的软件版本,经过规范的打包后,才允许进入交付库。交付库的软件包是部署生成环境的唯一来源。

自动化工具

有了代码库、配置库和交付库,加上繁杂的管理规范、流程指南,额外配上检查员、管理员,大概就可以完成上述的过程。

但是,我们需要自动化!持续集成(CI)工具就是时上述过程自动化的有力工具——以至于随时可以以很小的代价将上述过程跑一遍。

持续集成工具的主要功能如下图:

G cluster_ci 持续集成 repo1 代码库 Code Code repo1->Code m1 工程管理 repo1->m1 Build Build Code->Build Test Test Build->Test Package Package Test->Package Deploy Deploy Package->Deploy m3 交付库 Package->m3 m2 配置库 m2->Build m4 自动执行 m4->Build m4->Test m4->Package m4->Deploy m5 发布管理 servers 服务器 m5->servers

CI工具实现了与代码库关联的软件工程管理,能够自动执行构建、测试、打包、发布等操作。
CI工具通常内置了配置库和交付库,在自动构建时可以从配置库获取配置信息,在自动发布时从交付库取得指定的软件版本并部署到服务器。
此外,CI工具还会提供事件通知、生成报告等功能。

常见的CI工具

这个大概是最流行的CI工具了,使用Java开发。

  • BuildBot

    使用Python语言开发。好用,但是有点小众。

  • Jenkins

    也是使用Java开发。

为什么需要“OSGi中间件”

尽管在OSGi Runtime(Felix, Equinox等)的基础上,OSGi组织又规定了Blueprint规范以实现OSGi环境下的依赖注入
但这还不够——没有提供类似Web开发框架那样的一些“平台级”的功能。比如日志,控制台,配置文件等。

很难想象没有Tomcat这样的Web中间件,开发Java Web应用的工作量有多大。同样的,OSGi应用也需要一种“中间件”,来实现各应用共性的一些功能,并管理应用的部署。

Apache Karaf就是这样的一个”OSGi中间件”。最早,Karaf只是Apache ServiceMix的Kernel子项目,后来独立出来成为Apache的顶级项目。
目前,Apache Karaf已经用于Apache Geronimo, Apache ServiceMix, Fuse ESB等项目。

Apache Karaf的主要竞争对手是Eclipse Virgo

Apache Karaf的功能

Apache Karaf提供了如下“开箱即用”的功能:

  • 热部署

    尽管OSGi支持热部署,但并不是自动热部署,需要调用一些API去执行插拔的动作。Karaf在运行时可以自动处理[home]/deploy文件夹中的OSGi bundle,能够自动加载并在满足依赖关系时自动启动。

  • 动态配置

    Karaf在$KARAF_HOME/etc文件夹中存储配置文件。这些配置内容可以在Karaf运行时动态修改。

  • 日志处理

    基于Log4J的日志系统,同时支持多种日志API,如JDK 1.4, JCL, SLF4J, Avalon, Tomcat, OSGi等。

  • 系统服务

    Karaf可以作为系统服务运行。

  • 控制台

    可以在控制台进行服务管理、安装bundle等操作。还可以扩展自己的控制台命令。

    可以通过SSH远程访问其他服务器上的Karaf控制台。

  • 多实例管理

    一个服务器上可以运行多个Karaf实例。对实例的管理可以在Karaf控制台中进行。

  • Bundle仓库

    Karaf中内置了Pax URL的MVN协议,可以从Maven中央仓库安装bundle。

  • Bundle集合(Feature)

    类似于Eclipse的Feature,Karaf中也支持Feature,即bundle的集合。使用Feature可以简化OSGi应用的部署。

Karaf初体验

安装好Java环境,加压缩Karaf,执行$KARAF_HOME/bin/karaf,可以看到Karaf的启动界面:

如图的提示,使用<tab>可以列出所有可用的命令,所有的命令支持--help参数。

如果执行list命令,可以列出所有的bundles:

还有很多其他的命令,比如

  • bundle:install 安装bundle
  • feature:repo-add 安装feature库
  • feature:install 安装feature

等等。详情可以参考官方的Quick Start

如果查看Karaf的目录,可以看到如下的目录结构:

  • /bin: 启动、停止、登录karaf控制台的脚本
  • /etc: 配置文件
  • /data: 存放运行时的工作文件,清空这个目录可以使Karaf回到初始状态
  • /cache: OSGi framework的bundle缓存
  • /generated-bundles: 开发用的临时目录
  • /log: 日志
  • /deploy: 用于bundle的热部署
  • /instances: 存放karaf的各个实例用到的数据
  • /lib: 启动Karaf时需要的一些库
  • /lib/ext: Karaf需要的扩展库
  • /lib/endorsed: Karaf对其他API的再封装(背书)的一些jar包
  • /system: OSGi bundles库,使用Maven 2仓库的结构

Karaf启动模式和实例

  • 常规模式(regular mode) 使用命令bin/karaf,可以在前端启动Karaf并进入控制台
  • 服务模式(server mode) 使用命令bin/karaf server,可以在前端启动Karaf,但不进入控制台
  • 后台模式(background mode) 使用命令bin/start,可以在后台启动Karaf。

如果安装了service-wrapper(可以在Karaf中执行feature:install service-wrapper),还可以将
Karaf安装为系统服务。

一个Karaf节点可以运行多个”Karaf实例”。实际上,默认情况下Karaf会启动一个名为root的实例。
实例是包含单独的配置文件、数据文件等信息的一个Karaf副本,每个实例可以单独启动或部署bundle。

Karaf控制台中提供了一些管理实例的命令:

  • instance:create 创建实例
  • instance:clone 从现有的实例克隆一个新的实例
  • instance:start 启动实例
  • instance:status 查看实例状态
  • instance:connect 连接(切换)到某个实例
  • instance:stop 停止实例
  • instance:destroy 删除实例

ssh、客户端和Web控制台

Karaf内置了一个SSHd server,可以通过ssh远程访问Karaf控制台。
要启动远程控制台服务,需要在控制台中启动bundle: bundle:restart -f org.apache.karaf.shell.ssh。此时,远端可以使用ssh远程访问控制台,比如: ssh -p 8101 karaf@localhost。默认karaf用户的密码也是karaf。ssh的用户、密码、端口等都可以在etc/org.apache.karaf.shell.cfg中配置。

Karaf的客户端’bin/client’即可以连接本地控制台,也可以通过ssh连接远程控制台,甚至可以执行单个的命令。比如:

1
bin/client -u karaf -p karaf -a 8101 hostname osgi:shutdown

Karaf还可以安装Web控制台(feature:install webconsole)。通过Web控制台能够管理
Karaf的bundle、feature、实例、配置以及查看日志。启动web控制台后,默认可以通过http://localhost:8181/system/console
访问web控制台,默认的用户名/密码为karaf/karaf。Web控制台的配置文件位于etc/org.ops4j.pax.web.cfg

bundle URL和bundle仓库

Karaf在安装bundle时,可以使用多种URL来定位bundle,比如:

  • http http://repo1.maven.org/maven2/org/apache/servicemix/nmr/org.apache.servicemix.nmr.api/1.0.0-m2/org.apache.servicemix.nmr.api-1.0.0-m2.jar

  • file file:base/bundles/org.apache.servicemix.nmr.api-1.0.0-m2.jar

  • maven库 mvn:org.apache.servicemix.nmr/org.apache.servicemix.nmr.api/1.0.0-m2

其中,maven库方式是使用maven库作为bundle仓库,从其中检索需要的bundle。与maven类似,可以自动解决bundle之间的依赖问题。
Karaf提供了OBR (OSGi Bundle Repository),能够管理bundle仓库。在控制台使用命令feature:install obr安装OBR之后,就可以:

  • 增加新的bundle仓库

    obr:url-add file:///user/.m2/repository/repository.xml

  • 查看已安装的bundle仓库

    obr:url-list

  • 刷新仓库

    obr:url-refresh

  • 删除仓库

    obr:url-remove

  • 列出所有可用的bundle

    obr:list

  • 查找bundle

    obr:find

OSGi应用,Karaf Feature和KAR

OSGi运行的基本单元是bundle,bundle之间有依赖关系。一组满足依赖关系的bundle集合,加上相关的配置信息,称为一个“OSGi应用”。
将OSGi应用部署到Karaf的行为称为“provisioning”。

为了简化OSGi应用的部署,Karaf定义了feature,用于描述OSGi应用的部署信息,包括:

  • 名称
  • 版本号
  • 描述信息
  • bundle集合
  • 配置文件信息
  • 依赖的其他feature

可以看出,Karaf中的feature与Eclipse Feature非常类似。

Karaf控制台中提供了一系列的feature和feature库的管理命令,包括:

  • feature:info
  • feature:install
  • feature:list
  • feature:repo-add
  • feature:repo-list
  • feature:repo-refresh
  • feature:repo-remove
  • feature:uninstall
  • feature:version-list

由于feature中可能使用远端的bundle,Karaf提出了KAR格式,可以把一个bundle相关的所有资源打包在一起。
这类似于使用maven定义的web工程,和最终打包的war文件之间的关系。
Karaf控制台中,可以打包(kar:create)、安装(kar:install)、卸载(kar:uninstall)或列出(kar:list)KAR文件。

企业级特性

Karaf提供了一系列的功能,以支持企业级应用的开发,包括:

  • 日志

    Karaf提供了灵活的日志系统,支持

    • OSGi Log Service
    • Log4j
    • Commons Logging
    • Logback
    • SLF4J
    • java Util Logging

    等日志框架。不管应用中使用了上述哪种日志框架,Karaf中都可以进行统一的管理,如log level, appender等。

  • 安全

    Karaf提供了基于JAAS(Java Authentication and Authorization Service)的安全框架,可以控制对OSGi service、控制台命令、JMX、Web控制台等资源的访问。并且在应用中也可以使用Karaf的安全框架。

  • Web

    Karaf可以作为Web容器使用,完全支持JSP/Servlet规范。
    Karaf的Web容器同时支持WAR和WAB的部署。

  • JNDI

    Karaf支持OSGi中的JNDI服务

  • JDBC、JPA和JTA

    Karaf中即可以使用JDBC直接连接数据库,
    也可以使用JPA作为持久层框架。Karaf可以通过Blueprint管理JPA的persist Unit。

    Karaf支持JTA,以实现容器管理的事务。

  • 其他

    Karaf还支持EJBCDIJMXJMS等企业级特性。

    Karaf内置支持主备方式的部署,以保证高可用。使用Apache Karaf Cellar可以实现Karaf的集群部署。

Graphviz是一个强大的绘图工具,使用DOT语言描述图像。

Graphviz是什么

Graphviz是基于dot语言的绘图工具,可以画有向图、无向图、关系图、目录图、流程图等各种你知道的或不知道的图形。可以欣赏Graphviz的Gallery,以及Yifan Hu的gallery of large graphs

Graphviz使用DOT语言描述图形,并提供一组工具:

  • dot

    将生成的图形转换成多种输出格式,如PostScript,PDF,SVG,PNG,含注解的文本等等。

  • neato

    用于sprint model的生成(在Mac OS版本中称为energy minimized)。

  • twopi

    用于放射状图形的生成

  • circo

    用于圆形图形的生成。

  • fdp

    另一个用于生成无向图的工具。

  • dotty

    一个用于可视化与修改图形的图形用户界面程序。

  • lefty

    一个可编程的控件,它可以显示DOT图形,并允许用户用鼠标在图上执行操作。Lefty可以作为MVC模型的使用图形的GUI程序中的视图部分。

Graphviz使用DOT语言描述图形,而不是依赖于鼠标绘图,这就提供了无限的可能,主要体现在两个方面:

  • 自动生成图形描述文件,从而自动生成图形
  • 更加Geek的写作方式,比如在Org-mode中嵌入图形代码,在导出时自动生成图形文件

有一些强大的工具依赖或支持Graphviz,比如OmniGraffle
PlantUML

DOT语言简介

DOT语言使用离散数学中的Graph描述图形,包括三个基本元素:grahp,node,edge。下面是一个例子:

example.dot

1
2
3
4
5
6
7
8
9
10
digraph G {
main -> parse -> execute[label="edge label2"];
main -> init[label="edge label1"];
main -> cleanup;
execute -> make_string;
execute -> printf;
init -> make_string;
main -> printf;
execute -> compare;
}

执行命令dot -Tpng example.dot -o example.png,就可以生成如下的图形:

DOT通过属性可以设置node和edge的样式,比如:

1
2
3
4
5
6
7
graph graphname {
// label属性可以改变节点的显示名称
a [label="Foo"]; // 节点形状被改变了
b [shape=box]; // a-b边和b-c边有相同的属性
a -- b -- c [color=blue];
b -- d [style=dotted, label="无向图"];
}

生成的图形如下:

更多的属性设置可以参考官方文档

应用场景

Graphviz可以有很多玩法,常见的比如:

Apache Camel的整体架构

Apache Camel是一个消息处理引擎,实现了EIP(Enterprise Integration Patterns,企业整合模式)。

Camel能够用来处理来自于不同源的事件和信息,定义规则进行消息的传递和转换等处理,以实现基于消息的应用整合。其整体架构如下图所示:

Message 和 Exchange

org.apache.camel.Message接口是对“消息”的抽象。消息由head、body、attachment等部分组成。

Camel中提供了一个默认的实现:org.apache.camel.impl.DefaultMessage,可以适应大部分的应用场景。

不管是请求、响应,或者异常,都可以作为消息在上下文(CamelContext)的消息处理器(Processor)之间进行交换(Exchange)。

org.apache.camel.Exchange接口就是对“消息交换”的抽象。

其中:

  • Exchange ID : 区分每次消息交换的标识
  • MEP: (message exchange pattern,消息交换模式),分为单向(InOnly)和请求-应答(InOut)两种
  • Exception: 用于记录消息交换时发生的异常
  • In message: 上一个节点传入的消息
  • Out message: 当MEP 是InOut时,需要传出的消息

Camel提供了默认的org.apache.camel.impl.DefaultExchange实现。

Camel处理消息时,每个节点都在处理Exchange

Endpoint 和 Component

Endpoint(端点),接收或发送消息的通道。通过URI连接消息源或目标。

为了适应各种不同的URI协议,如http,ftp,JMS,smtp等,Camel中提供了多种Endpoint,也支持扩展自己的Endpoint。

Component(不应该叫做组件,而应该是连接器connector)。org.apache.camel.Component接口只定义了两个方法:

  • createConfiguration(String)
  • createEndpoint(String)

通常,客户代码不会直接调用createEndoint()方法,而是由CamelContext对象进行调用。

Camel中提供了大量的Component的实现:

Processor

不管是消息路由(Message Routing)、消息转换(Message Transformation)还是消息过滤(Message Filter),都是对消息的某种处理(Process)。

Camel中,抽象出org.apache.camel.Processor接口,表示对消息的处理。该接口只定义了一个方法:

1
void process(Exchange exchange) throws Exception;

从接口定义可以看出,Camel中认为可以处理消息交换(Exchange)的类都是消息处理器(Processor)。

基于Camle的应用可以开发自己的Processor实现,同时Camel提供了大量的内置Processor,以支持EIP(Enterprise Integration Patterns)

CamelContext

CamelContext是对Camel运行时的抽象,提供了API用于管理Component、Endpoint、Processor等节点:

一般来说,使用Camel的步骤如下:

  1. 创建一个CamelContext对象。
  2. 向CamelContext对象中添加Endpoints或者是Components
  3. 向CamelContext对象中添加路由(routes)规则
  4. 调用CamelContext的start()方法启动Camel引擎
  5. 通过Endpoint发送或接收消息
  6. 调用CamelContext的stop()方法时

定义路由(Route)

每个消息处理流程是由一系列的Processor连接而成的图(Graph),每个图称为一个路由(Route)。

在开始使用Camel之前,需要在CamelContext中定义一个或多个路由。Camel支持使用DSL或者Spring XML进行配置。比如:

1
2
3
4
5
6
7
8
9
10
11
12
RouteBuilder builder = new RouteBuilder() {
public void configure() {
from("queue:a").filter(header("foo").isEqualTo("bar")).to(
"queue:b");
from("queue:c").choice().when(header("foo").isEqualTo("bar"))
.to("queue:d").when(header("foo").isEqualTo("cheese"))
.to("queue:e").otherwise().to("queue:f");
}
};

CamelContext myCamelContext = new DefaultCamelContext();
myCamelContext.addRoutes(builder);

或者:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
">

<!-- this is an included XML file where we only the the routeContext -->
<routeContext id="myCoolRoutes" xmlns="http://camel.apache.org/schema/spring">
<!-- we can have a route -->
<route id="cool">
<from uri="direct:start"/>
<to uri="mock:result"/>
</route>
<!-- and another route, you can have as many your like -->
<route id="bar">
<from uri="direct:bar"/>
<to uri="mock:bar"/>
</route>
</routeContext>

</beans>

FUSE Mediation Router: 企业级Camel

FuseSource提供Camel的经测试、认证并提供支持的企业级版本,称作FUSE Mediation Router

尽管8年后的1973年出现了图形界面程序,
16年后的1981年出现了图形界面的操作系统,
但是在此之后,至今三十几年过去了,图形界面(GUI)仍无法取代命令行界面(CLI)。

命令行界面是否过时

答案是:不会的!

1965年OS/360的发布标志着与硬件分离的“通用”操作系统的出现。

尽管8年后的1973年出现了图形界面程序
16年后的1981年出现了图形界面的操作系统
但是在此之后,至今三十几年过去了,图形界面(GUI)仍无法取代命令行界面(CLI)。

有什么理由可以说,图形界面终将取代命令行界面呢?

不管是传统的linux“神器”,如find, grep, curl, netcat, xargs, rsync,
screen,awk, vi, emacs, 还是让人惊叹的新作,如maven, git,
salt,都有着强大的命令行界面。甚至你会有一种感觉:
为这些软件设计图形界面根本就是画蛇添足。

命令行界面的优势在于:

  • 效率高

鼠标操作是最简单,同时也是最笨拙的操作

  • 更专注

鼠标操作容易分心

  • 速度快

CLI程序需要的资源少,启动和执行速度更快

  • 易远程

ssh、telnet的连接比vnc、remote desktop等更容易

  • 自动化

CLI不仅可以给人使用,也适用于自动化脚本

  • zhuangbility

^o^

命令行界面的类型

按照复杂程度,命令行界面可以分为:

  • 非交互式

一次性输入所有的参数,程序执行期间不需要用户的干预。这是最常见的命令行界面形式。

  • 基于行的交互

在执行过程中,需要用户输入一些内容,比如确认信息、路径参数等。

由于用户交互会使程序难以用于自动化脚本,所以这种命令行界面并不多见,常用于基于命令行的安装程序。

  • 文本用户界面(TUI)

类似于图形用户界面,没有明确的执行流程,完全由用户控制程序的执行步骤。比如vi和emacs。

CLI世界的潜规则

为了使你的CLI程序不会显得格格不入,在设计CLI程序时要遵守一些潜规则。

  • 良好命名

  • 容易理解

  • 名字要短

  • 容易记忆

  • 必备选项

所有的命令行工具都应该提供=-v/–version=和=-h/–help=选项。

  • 保持安静

程序的输出要”恰到好处”,让用户/其他程序明确知道必要的信息,又不过分“啰嗦”。过多的输出即会浪费系统资源和带宽,也会让用户感觉不舒服,更重要的是会使得其他程序的处理逻辑变得复杂。以下的原则有利于保持安静:

  • 不要输出无关的信息,比如版本号、作者名——除非用户要求

  • 对于很明确的结果,不需要再提醒用户。只应提示例外(exception)情况

  • 不需要告诉用户输出的是什么东西——用户会知道的

  • 如有必要,可以提供=-v/–verbose=和=-q/–quiet=选项,供用户选择

  • 明确要求

在基于行的交互式CLI中,需要用户输入时要给出明确的提示,比如:

=Do you really want to do this (y/n)?=

=Enter a date (YYYY-MM-DD):=

  • 支持管道

程序应该支持从管道或文件重定向中读取数据:

  • 如果文件名作为参数传递给程序,就读取文件的内容作为输入

  • 如果没有提供这样的参数,就从标准输入中读取,一直到 CTRL+D

  • 功能单一

UNIX哲学中重要的一条原则就是:每个程序只做一件事情,并把它做好。

复杂的功能通过程序间的配合完成,而为了与其他的程序配合,要尽量支持管道和重定向。

既然“只做一件事”,就要“做好一件事”。

  • 遵循惯例

UNIX中命令行参数会有一些惯例,比如=-=后面的单字母选项可以连用(如=ls -Al=),
=–=后面使用多字母选项等;此外,遵循已经被广泛使用的命令的参数,也会容易被接受。

CLI支持库

CLI是如此重要,以至于很多语言/平台都提供了开发CLI的支持库,比如:

  • python的optparse和argparse

    argparse更先进,旨在替代optparse,但是从python2.7开始才支持。如果希望在比较旧的linux上运行(通常支持python2.4),最好还是使用optparse。

  • java的Apache Commons CLI

  • Apache karaf的karaf-command-archetype

python的TUI支持库