Camel運行原理分析
以一個簡單的例子說明一下camel的運行原理,例子本身很簡單,目的就是將一個目錄下的文件搬運到另一個文件夾,處理器只是將文件(限於文本文件)的內容打印到控制台,首先代碼如下:
public static void main(String[] args) throws Exception { //創建Camel上下文 DefaultCamelContext camelContext = new DefaultCamelContext(); //添加一個路由,參數為路由建造者 camelContext.addRoutes(new RouteBuilder() { @Override public void configure() throws Exception { this.from("file:D:/temp/in").process(new Processor() { @Override public void process(Exchange exchange) throws Exception { GenericFile<File> gf = exchange.getIn().getBody(GenericFile.class); File file = gf.getFile(); PrintStream ps = new PrintStream(System.out); BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file))); String line = null; while((line=br.readLine())!=null) { ps.println(line); } ps.close(); br.close(); } }).to("file:D:/temp/out"); } }); //啟動上下文 camelContext.start(); //防止主線程退出 Object object = new Object(); synchronized (object) { object.wait(); } } |
對於camel來說,其原理核心的部分主要包含路由信息的構建、組件查找、和路由啟動過程三方面,下面結合以上簡單例子對三個方面來對camel的源碼進行分析:
一、路由的構建
其實這里說的路由構建其實是構建路由定義,對應Camel中的RouteDefinition類,一個RouteDefinition對象規定了或者說指定了一個消息從哪里產生,中間要經過什么樣的處理,最后路由到什么地方。RouteDefinition由RouteBuilder進行構建,再具體點就是調用RouteBuilder的configure()方法,構建完成后再添加到CamelContext中,那么該路由定義就可以運行了。RouteBuilder的configure()方法是一個抽象方法,所以該方法要由開發者進行實現,其實就是在進行路由定義的構建過程。
首先,調用RouteBuilder的from方法獲取RouteDefinition,並且根據from傳入的參數將一個FromDefinition類型的實例設置給RouteDefinition的inputs(輸入)列表,然后調用RouteDefinition的process()和to()方法,分別生成ProcessDefinition和ToDefinition對象,因為ProcessDefinition和ToDefinition都繼承自ProcessorDefinition,所以都可以看出輸出放到RouteDefinition的outputs(輸出)列表中,至此,整個路由定義就構建完成了,其實該路由定義最重要的就是輸入與輸出,輸入與輸出都可以有多個,默認情況下,在路由運行后,Camel會依賴調用這些輸出處理器並最終將消息路由到指定目的地。
二、組件查找方式
Camel的運行是由組件(component)進行組織的,我們前面的調用from()方法傳個字符串,camel是要根據uri來查找到對應的組件,即要維護uri到組件之間的映射關系,查找組件的過程是調用DefaultCamelContext中的getComponent(String name)方法來完成的,是在DefaultCamelContext啟動的時候調用,獲取相應的組件,生成對應的endPoint,組件的查找是從DefaultCamelContext上下文緩存的components中去找,如果沒有找到,就會去根據uri前綴生成一個component,生成的代碼大致如下:
// 先在注冊表中查找 Object bean = null; try { bean = context.getRegistry().lookupByName(name); getLog().debug("Found component: {} in registry: {}", name, bean); } catch (Exception e) { getLog().debug("Ignored error looking up bean: " + name, e); } //省略了一部分代碼.... // 注冊表中沒有時,使用Component工廠創建一個 Class<?> type; try { type = findComponent(name, context); if (type == null) { // not found return null; } } catch (NoFactoryAvailableException e) { return null; } catch (Exception e) { throw new IllegalArgumentException("Invalid URI, no Component registered for scheme: " + name, e); } // 創建Component if (Component.class.isAssignableFrom(type)) { return (Component) context.getInjector().newInstance(type); } else { throw new IllegalArgumentException("Type is not a Component implementation. Found: " + type.getName()); } |
其中的findComponent方法是在camel包中默認路徑META-INF/services/org/apache/camel/component/+uri前綴名
找對應的Properties(不帶.properties后綴)文件,文件中有Component組件實例化的類名,找到后通過反射機制生成一個component組件,放到緩存中。我們自己也可以按照這個規則來拓展自己的component。
三、路由的啟動原理
Camel啟動原理圖
四、路由的運行原理
圖一
圖二