Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Spring AMQP × RabbitMQ        @Keisuke69
自己紹介西谷圭介  TIS株式会社  eXcaleというPaaSやってます  http://www.excale.net/Twitter ID : @Keisuke69Github : Keisuke69
Spring AMQPとは?Springのサブプロジェクトの1つJava版と.Net版がある比較的若いプロダクト  最初のGAリリースが2011/08  最新は1.1.3AMQPベースのメッセージングをSpringっぽく扱える  アプリケーショ...
AMQPメッセージングミドルウェアのオープンなプロトコル仕様プラットフォーム問わず、実装言語非依存インターオペラビリティ高いAPIだけでなくワイヤレベルプロトコルである    JMSはAPIだけビジネス上のリアルな問題を解決するようデザインされ...
基本モデル                                                 Server   Publisher                    Exchange                      ...
RabbitMQErlangで書かれているブローカー VMwareによって開発されているAMQPのインプリメンテーションクラスタリングやキューのミラーリングが可能 本来AMQPのSpecではクラスタ等については 定義されていない
なぜメッセージングかコンポーネントを疎結合にできる非同期負荷分散とスケーラビリティ
Spring AMQPを使う
Java-based container    configuration 従来、XMLで定義していたSpringの各種設定をJavaクラス で定義   冗長になりがちなXMLを書かなくていいので個人的に   はおすすめ @Configuratio...
ConnectionFactoryRabbitMQへの接続を管理するインターフェースcom.rabbitmq.client.ConnectionのラッパーCachingConnectionFactoryだけが実装クラスとして提供されている
TemplateAmqpTemplate  汎用インターフェースRabbitOperation  RabbitMQ用インターフェースRabbitTemplate  実際のインプリメンテーション実情として現時点ではサポートしているのがRabbit...
Template  各種メソッドおよびコールバックを提供  メッセージの送受信は基本的にTemplateを利用して行  うvoid send(Message message) throws AmqpException;void convertA...
MessageConverterSpring AMQPの大きな特徴オブジェクトとメッセージ間で変換が行えるデフォルトではSimpleMessageConverter JsonMessageConverter MarshallingMessage...
Configuration@Configurationpublic class SampleConfig {		    @Bean	    public ConnectionFactory connectionFactory() {	      ...
メッセージの送信        AmqpTemplateを使って行う        下記サンプルではアノテーションベースの設定を行なっ        ているのでAnnotationConfitgApplicationContextを使用publi...
メッセージの受信       送信と同じくAmqpTemplateを使うpublic class Consumer {    public static void main(String[] args){        ApplicationC...
MessageConverter           Templateのbean登録時に利用したいコンバータをセットする               今回は JsonMessageConverterを使用           JsonMessa...
MessageConverter POJOを用意する 今回はシンプルにkeyとmessageというフィールドを用意してそれらに対するgetter/setter を定義したものを用意 DB用のモデルクラスなどと兼用することも可能public cl...
コンバータを使った送信       POJOをnewしたオブジェクトに値をセット       AmqpTemplateのconvertAndSendの引数としてオブジェクトを渡       すpublic class Publisher {  ...
コンバータを使った受信                同期処理                AmqpTemplate.receiveAndConvert()                変換先の型にキャストする必要があるpublic cla...
MessageListener
MessageListener非同期受信のためのインターフェース  メッセージドリブンなコールバック処理を簡  単に実装できるSimpleMessageListenerContainer  Springが提供する軽量リスナーコンテナメッセージ受...
MessageListener               HelloWorldHandlerというハンドラクラスをMessageListenerAdapterで紐付け               もちろん、MessageConverterも利...
ハンドラクラス        特別なクラスの継承やインターフェースの実装は不要        handleMessageというメソッドはデフォルト          変更可能        メソッドの引数として変換先の型を指定する       ...
MessageListenerの起動        基本的に変わらない        標準ではApplicationContext起動時に自動でリスナーも        起動される        受信時の処理がハンドラで行われるようになっている...
困ったところメッセージの型が固定になる ヘッダの値を元に変換先の型が特定されている(FQCN) PublisherがSpring以外でもこのヘッダのセットが必要 我々の場合、MQにメッセージを送る側が異なる言語で実装されていた 独自のクラスマッ...
サンプル (独自ClassMapper)DefaultClassMapperを継承してtoClassメソッドをオーバーライドメッセージヘッダの"__TypeId__"に指定された型名(FQCNじゃない)を使って指定のパッケージ配下からクラスを探...
サンプル           (独自ClassMapper)	   @Override	   public Class<?> toClass(MessageProperties properties) {	   	   if (targetCl...
サンプル(汎用ハンドラクラス)メッセージヘッダのTypeId+Handlerという名前で委譲先のハンドラクラスを決定“__Method__”ヘッダにセットされたメソッドを実行全オブジェクト分のSimpleListenerContainerのbe...
サンプル                            (汎用ハンドラクラス)public class GenericHandler {	    private String path;	    @Autowired	    BeanF...
その他の考慮事項RabbitMQを冗長化した場合の話 特にMirroredQueueを利用する場合 →Codezineさんに寄稿した記事に書いておきました  http://codezine.jp/article/detail/6943エラー時の...
なぜメッセージングかコンポーネントを疎結合にできる MQを境界としてコンポーネント間の依存関係を切り離せる メッセージのフォーマットさえ正しければ言語が異なってもOK非同期 疎結合になるため、障害時などの影響を小さくできる 部分的なスケールアウ...
Spring AMQPとRabbitMQ使うと 簡単に非同期アプリが書けますよ!      http://www.excale.net/
Upcoming SlideShare
Loading in …5
×

Spring AMQP × RabbitMQ

6,339 views

Published on

Published in: Technology

Spring AMQP × RabbitMQ

  1. 1. Spring AMQP × RabbitMQ @Keisuke69
  2. 2. 自己紹介西谷圭介 TIS株式会社 eXcaleというPaaSやってます http://www.excale.net/Twitter ID : @Keisuke69Github : Keisuke69
  3. 3. Spring AMQPとは?Springのサブプロジェクトの1つJava版と.Net版がある比較的若いプロダクト 最初のGAリリースが2011/08 最新は1.1.3AMQPベースのメッセージングをSpringっぽく扱える アプリケーションロジックとインフラ周りの設定を切り離せるメッセージ送受信用に抽象化されたtemplateを提供POJOによるメッセージ送受信をサポート
  4. 4. AMQPメッセージングミドルウェアのオープンなプロトコル仕様プラットフォーム問わず、実装言語非依存インターオペラビリティ高いAPIだけでなくワイヤレベルプロトコルである JMSはAPIだけビジネス上のリアルな問題を解決するようデザインされているhttp://www.amqp.org/
  5. 5. 基本モデル Server Publisher Exchange Binding Consumer Message Queue Virtual HostExchangeがメッセージを受け取ってMessage Queueへと受け渡すMessage QueueはMessageをためてConsumerに渡すExchangeとMessageQueueを対応付けるのがBinding ExchangeのタイプはDirect、Fanout、Topic、Headerがある MessageQueueを複数にする構成も可能
  6. 6. RabbitMQErlangで書かれているブローカー VMwareによって開発されているAMQPのインプリメンテーションクラスタリングやキューのミラーリングが可能 本来AMQPのSpecではクラスタ等については 定義されていない
  7. 7. なぜメッセージングかコンポーネントを疎結合にできる非同期負荷分散とスケーラビリティ
  8. 8. Spring AMQPを使う
  9. 9. Java-based container configuration 従来、XMLで定義していたSpringの各種設定をJavaクラス で定義 冗長になりがちなXMLを書かなくていいので個人的に はおすすめ @Configurationをつけたクラスに、@Beanをつけたメ ソッドを実装してbean登録 ApplicationContextはAnnotationConfigApplicationContextで 今回のサンプルは全てこの方式で設定
  10. 10. ConnectionFactoryRabbitMQへの接続を管理するインターフェースcom.rabbitmq.client.ConnectionのラッパーCachingConnectionFactoryだけが実装クラスとして提供されている
  11. 11. TemplateAmqpTemplate 汎用インターフェースRabbitOperation RabbitMQ用インターフェースRabbitTemplate 実際のインプリメンテーション実情として現時点ではサポートしているのがRabbitMQのみ 後々、他のAMQPミドルウェアをサポート予定(らしい)
  12. 12. Template 各種メソッドおよびコールバックを提供 メッセージの送受信は基本的にTemplateを利用して行 うvoid send(Message message) throws AmqpException;void convertAndSend(Object message) throws AmqpException;Message receive() throws AmqpException;Object receiveAndConvert() throws AmqpException;
  13. 13. MessageConverterSpring AMQPの大きな特徴オブジェクトとメッセージ間で変換が行えるデフォルトではSimpleMessageConverter JsonMessageConverter MarshallingMessageConverter
  14. 14. Configuration@Configurationpublic class SampleConfig { @Bean public ConnectionFactory connectionFactory() { CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost"); connectionFactory.setUsername("guest"); connectionFactory.setPassword("guest"); return connectionFactory; } @Bean public AmqpAdmin amqpAdmin() { return new RabbitAdmin(connectionFactory()); } @Bean public RabbitTemplate rabbitTemplate() { RabbitTemplate template = new RabbitTemplate(connectionFactory()); template.setRoutingKey("sample_queue"); template.setQueue("sample_queue"); return template; } @Bean public Queue helloWorldQueue() { return new Queue("sample_queue"); }}
  15. 15. メッセージの送信 AmqpTemplateを使って行う 下記サンプルではアノテーションベースの設定を行なっ ているのでAnnotationConfitgApplicationContextを使用public class Publisher {    public static void main(String[] args){        ApplicationContext context = new AnnotationConfigApplicationContext(SampleConfig.class);        AmqpTemplate amqpTemplate = context.getBean(RabbitTemplate.class);        amqpTemplate.convertAndSend("Hello world");        System.out.println("Sent message.");    }}
  16. 16. メッセージの受信 送信と同じくAmqpTemplateを使うpublic class Consumer {    public static void main(String[] args){        ApplicationContext context = newAnnotationConfigApplicationContext(SampleConfig.class);        AmqpTemplate amqpTemplate = context.getBean(RabbitTemplate.class);        String message = (String)amqpTemplate.receiveAndConvert();        System.out.println("Receive: " + message);    }}
  17. 17. MessageConverter Templateのbean登録時に利用したいコンバータをセットする 今回は JsonMessageConverterを使用 JsonMessageConverterを利用する場合、Jacksonパーサ必須@Configurationpublic class SampleConfig { (省略) @Bean public RabbitTemplate rabbitTemplate() { RabbitTemplate template = new RabbitTemplate(connectionFactory()); template.setRoutingKey("sample_queue"); template.setQueue("sample_queue"); template.setMessageConverter(new JsonMessageConverter()); return template; } (省略)}
  18. 18. MessageConverter POJOを用意する 今回はシンプルにkeyとmessageというフィールドを用意してそれらに対するgetter/setter を定義したものを用意 DB用のモデルクラスなどと兼用することも可能public class SimplePojo {    private String key;    private String message;       public String getKey() {        return key;    }    public void setKey(String key) {        this.key = key;    }    public String getMessage() {        return message;    }    public void setMessage(String message) {        this.message = message;    }}
  19. 19. コンバータを使った送信 POJOをnewしたオブジェクトに値をセット AmqpTemplateのconvertAndSendの引数としてオブジェクトを渡 すpublic class Publisher {    public static void main(String[] args){        ApplicationContext context = newAnnotationConfigApplicationContext(SampleConfig.class);        AmqpTemplate amqpTemplate = context.getBean(RabbitTemplate.class);        SimplePojo simplePojo = new SimplePojo();        simplePojo.setKey("ABC");        simplePojo.setMessage("This is message made by pojo.");        amqpTemplate.convertAndSend(simplePojo);        System.out.println("Sent message.");    }}
  20. 20. コンバータを使った受信 同期処理 AmqpTemplate.receiveAndConvert() 変換先の型にキャストする必要があるpublic class Consumer { public static void main(String[] args){ ApplicationContext context = new AnnotationConfigApplicationContext(SampleConfig.class); AmqpTemplate amqpTemplate = context.getBean(RabbitTemplate.class); SimplePojo simplePojo = (SimplePojo)amqpTemplate.receiveAndConvert(); System.out.println("Receive message: key = " + simplePojo.getKey() + ", message = " + simplePojo.getMessage()); }}
  21. 21. MessageListener
  22. 22. MessageListener非同期受信のためのインターフェース メッセージドリブンなコールバック処理を簡 単に実装できるSimpleMessageListenerContainer Springが提供する軽量リスナーコンテナメッセージ受信時の処理をハンドラとして実装
  23. 23. MessageListener HelloWorldHandlerというハンドラクラスをMessageListenerAdapterで紐付け もちろん、MessageConverterも利用可能@Configurationpublic class SampleListenerConfig {    String queueName = "sample_queue";       @Bean    public ConnectionFactory connectionFactory() {        CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost");        connectionFactory.setUsername("guest");        connectionFactory.setPassword("guest");        return connectionFactory;    }    @Bean    public SimpleMessageListenerContainer listenerContainer(){        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();        container.setConnectionFactory(connectionFactory());        container.setQueueNames(queueName);        container.setMessageListener(new MessageListenerAdapter(new HelloWorldHandler(),new JsonMessageConverter()));        return container;    }}
  24. 24. ハンドラクラス 特別なクラスの継承やインターフェースの実装は不要 handleMessageというメソッドはデフォルト 変更可能 メソッドの引数として変換先の型を指定する 自動的に変換されて、getterでアクセス可能public class HelloWorldHandler {    public void handleMessage(SimplePojo simplePojo) {        System.out.println("Received: Key = " + simplePojo.getKey()                 + ", message = " + simplePojo.getMessage());    }}
  25. 25. MessageListenerの起動 基本的に変わらない 標準ではApplicationContext起動時に自動でリスナーも 起動される 受信時の処理がハンドラで行われるようになっている ためその辺りの記述が不要public class Consumer {    public static void main(String[] args){        new AnnotationConfigApplicationContext(SampleListenerConfig.class);    }}
  26. 26. 困ったところメッセージの型が固定になる ヘッダの値を元に変換先の型が特定されている(FQCN) PublisherがSpring以外でもこのヘッダのセットが必要 我々の場合、MQにメッセージを送る側が異なる言語で実装されていた 独自のクラスマッパーを用意 とは言ってもFQCNでの指定を不要にしただけ1つの型につき、定義できる処理が1つ 複数のキュー、型を利用したい場合に大量に定義が必要、かつ1つの型で複数の処理 は定義できない 同じ型で異なる処理をさせたかった 汎用ハンドラクラスを用意し、型と命名規則で実際のハンドラクラスへ処理を実装
  27. 27. サンプル (独自ClassMapper)DefaultClassMapperを継承してtoClassメソッドをオーバーライドメッセージヘッダの"__TypeId__"に指定された型名(FQCNじゃない)を使って指定のパッケージ配下からクラスを探しだして返却
  28. 28. サンプル (独自ClassMapper) @Override public Class<?> toClass(MessageProperties properties) { if (targetClass != null) { return targetClass; } Map<String, Object> headers = properties.getHeaders(); Object classIdFieldNameValue = headers.get(getClassIdFieldName()); String classId = null; if (classIdFieldNameValue != null) { classId = classIdFieldNameValue.toString(); } String className = null; try { className = ClassResolver.scan(classId, this.basePackage); if (className != null) { return ClassUtils.forName(className, getClass() .getClassLoader()); } } catch (IOException e1) { } catch (ClassNotFoundException e) { } return super.toClass(properties); }
  29. 29. サンプル(汎用ハンドラクラス)メッセージヘッダのTypeId+Handlerという名前で委譲先のハンドラクラスを決定“__Method__”ヘッダにセットされたメソッドを実行全オブジェクト分のSimpleListenerContainerのbean登録が不要に1つのキューで複数のオブジェクトを元にしたメッセージの送受信が可能
  30. 30. サンプル (汎用ハンドラクラス)public class GenericHandler { private String path; @Autowired BeanFactory beanFactory; public GenericHandler() { this.path = "org.hoge"; } public GenericHandler(String path) { this.path = path; } public void handleMessage(Object obj, MessageProperties message) { String typeId = null; String methodName = null; try { Map<String, Object> headers = message.getHeaders(); typeId = (String) headers.get("__TypeId__"); methodName = ((String) headers.get("__Method__")); methodName = StringUtils.uncapitalize(methodName); String delegateClassName = ClassResolver.scan(typeId + "Handler",this.path); Object delegate = beanFactory.getBean(ClassUtils.forName(delegateClassName, getClass().getClassLoader())); MethodInvoker methodInvoker = new MethodInvoker(); methodInvoker.setTargetObject(delegate); methodInvoker.setTargetMethod(methodName); Object[] objects = { obj }; methodInvoker.setArguments(objects); methodInvoker.prepare(); methodInvoker.invoke(); } catch (Exception e) { throw new HandlerExecutionFailureException(e); } }}
  31. 31. その他の考慮事項RabbitMQを冗長化した場合の話 特にMirroredQueueを利用する場合 →Codezineさんに寄稿した記事に書いておきました  http://codezine.jp/article/detail/6943エラー時のハンドリング RetryTemplateとMessageRecovererとかトランザクション @TransactionalとTransactionManager
  32. 32. なぜメッセージングかコンポーネントを疎結合にできる MQを境界としてコンポーネント間の依存関係を切り離せる メッセージのフォーマットさえ正しければ言語が異なってもOK非同期 疎結合になるため、障害時などの影響を小さくできる 部分的なスケールアウトなどが可能に負荷分散とスケーラビリティ Consumerプロセスを増やすだけでスケールできる SpringMQを使えば1プロセス内の並行度の設定も可能 Queueを増やすことも簡単、Exchangeの構成だけでラウンドロビン等も可能
  33. 33. Spring AMQPとRabbitMQ使うと 簡単に非同期アプリが書けますよ! http://www.excale.net/

×