標籤:main ack task put todo 應用 int stack 設計
本文主要是解析SpoutOutputCollector源碼,順便分析該類中所涉及的設計模式–代理模式。
首先介紹一下Spout輸出收集器介面–ISpoutOutputCollector,該介面主要聲明了以下3個抽象方法用來約束ISpoutOutputCollector的實作類別。介面定義與方法說明如下:
/** * ISpoutOutputCollector:Spout輸出收集器介面 */public interface ISpoutOutputCollector { /** * 改方法用來向外發送資料,它的傳回值是該訊息所有發送目標的taskID集合; * 參數: * streamId:訊息Tuple將要被輸出到的流 * tuple:要輸出的訊息,是一個Object列表 * messageId:輸出訊息的標記資訊,如果messageId被設定為null,則Storm不會追蹤該訊息, * 否則它會被用來追蹤所發出的訊息處理情況 */ List<Integer> emit(String streamId, List<Object> tuple, Object messageId); /** * 該方法與上面emit方法類似,區別在於: * 1.資料(訊息)只由所指定taskId的Task接收;(這就意味著如果沒有下遊節點接收該訊息,則該訊息就沒有被真正發送) * 2.該方法要求參數streamId所對應的流必須為直接流,接收端的Task必須以直接分組的方式來接收訊息, * 否則會拋出異常. */ void emitDirect(int taskId, String streamId, List<Object> tuple, Object messageId); /** * 用來處理異常 */ void reportError(Throwable error);}
Storm提供了介面ISpoutOutputCollector的預設類SpoutOutputCollector,這個類實際上是一個代理類,該類持有一個ISpoutOutputCollector類型的對象,所有的操作實際上都過該對象來實現的。SpoutOutputCollector定義如下:
public class SpoutOutputCollector implements ISpoutOutputCollector { /** * 持有SpoutOutputCollector要代理的對象 */ ISpoutOutputCollector _delegate; public SpoutOutputCollector(ISpoutOutputCollector delegate) { _delegate = delegate; } /** * 實現了介面中的emit方法,並且提供了它的幾個重載方法 * eg.如果不指定streamId,預設使用default,如果不指定messageId,則預設使用空(null) */ public List<Integer> emit(String streamId, List<Object> tuple, Object messageId){ return _delegate.emit(streamId, tuple, messageId); } public List<Integer> emit(List<Object> tuple, Object messageId) { return emit(Utils.DEFAULT_STREAM_ID, tuple, messageId); } public List<Integer> emit(List<Object> tuple) { return emit(tuple, null); } public List<Integer> emit(String streamId, List<Object> tuple) { return emit(streamId, tuple, null); } /** * 實現了介面中的emitDirect方法,同時也提供了幾個重載方法,與上面emit方法一致. */ public void emitDirect(int taskId, String streamId, List<Object> tuple, Object messageId) { _delegate.emitDirect(taskId, streamId, tuple, messageId); } public void emitDirect(int taskId, List<Object> tuple, Object messageId) { emitDirect(taskId, Utils.DEFAULT_STREAM_ID, tuple, messageId); } public void emitDirect(int taskId, String streamId, List<Object> tuple) { emitDirect(taskId, streamId, tuple, null); } public void emitDirect(int taskId, List<Object> tuple) { emitDirect(taskId, tuple, null); } /** * 處理異常方法的實現 */ @Override public void reportError(Throwable error) { _delegate.reportError(error); }}
PS:
代理模式主要分為兩種:靜態代理和動態代理
靜態代理:
在程式運行前代理類與委託類的關係在運行前就確定,即在程式運行前就已經存在代理類的位元組碼檔案了.
代理模式角色:
Subject(抽象主題角色):可以是抽象類別也可以是介面,聲明了被委託角色和委託類共有的處理方法;
RealSubject(具體主題角色):又稱被委託角色、被代理角色,是商務邏輯的具體執行者;
ProxySubject(代理主題角色):又稱委託類、代理類,負責對真實角色的應用,
把所有抽象主題類定義的方法限制委託給具體主題角色來實現,並且在具體主題角色處理完畢前後做預先處理和善後處理.
靜態代理模式案例如下:
//抽象主題public interface Subject { public void process(String taskName);}
被代理角色:
public class RealSubject implements Subject { @Override public void process(String taskName) { System.out.println("正在執行任務:"+taskName); try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }}
代理類:
public class ProxySubject implements Subject { //代理類持有一個委託類的對象引用 private Subject delegate; public ProxySubject(Subject delegate){ this.delegate=delegate; } @Override public void process(String taskName) { //預先處理 this.before(); //將請求指派給委託類處理 delegate.process(taskName); //善後處理 this.after(); } private void before(){ System.out.println("預先處理!"); } private void after(){ System.out.println("善後處理!"); }}
案例測試:
public class Test { public static void main(String[] args) { RealSubject subject = new RealSubject(); ProxySubject p = new ProxySubject(subject); p.process("排水"); }}
測試結果:
預先處理!正在執行任務:排水善後處理!
靜態代理類的優缺點:
優點:
業務類只需關注商務邏輯本身,這樣就保證了業務類的重用性.
缺點:
代理對象的一個介面只服務於一種類型的對象.當要代理的方法很多,就要為每一種方法進行代理。因此靜態代理在程式規模變大時就無法很好地勝任工作了.
動態代理:
代理類和委託類的關係在程式運行時才確定的.動態代理類的源碼是在程式運行期間由JVM根據反射等機制動態產生,所以不存在代理類的位元組碼檔案.
動態代理模式案例如下:
public interface Service { //目標方法 public void process();}
public class UserServiceImpl implements Service { @Override public void process() { System.out.println("使用者service處理"); }}
動態代理實現執行個體:
public class MyInvocatioHandler implements InvocationHandler { private Object target; public MyInvocatioHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //System.out.println("-----before-----"); this.before(); Object result = method.invoke(target, args); // System.out.println("-----end-----"); this.after(); return result; } // 組建代理程式對象 public Object getProxy() { ClassLoader loader = Thread.currentThread().getContextClassLoader(); Class<?>[] interfaces = target.getClass().getInterfaces(); return Proxy.newProxyInstance(loader, interfaces, this); } private void before(){ System.out.println("預先處理!"); } private void after(){ System.out.println("善後處理!"); }}
案列測試:
public class ProxyTest { public static void main(String[] args) { Service service = new UserServiceImpl(); MyInvocatioHandler handler = new MyInvocatioHandler(service); Service serviceProxy = (Service)handler.getProxy(); serviceProxy.process(); }}
測試結果:
預先處理!使用者service處理善後處理!
動態代理的優缺點:
優點:
介面中的所有方法都被轉移到調用處理器一個集中的方法中在方法“運行時”動態加入,決定你是什麼類型,較靈活
缺點:
1. 與靜態代理相比,效率降低了
2. JDK動態代理只能對實現了介面的類進行代理
歡迎關注下面二維碼進行技術交流:
JStorm與Storm源碼分析(五)--SpoutOutputCollector與代理模式