本文不介绍amqp和rabbitmq相关知识,请自行网上查阅
本文是基于spring-rabbit中间件来实现消息的发送接受功能
see http://www.rabbitmq.com/tutorials/tutorial-one-Java.html
see http://www.springsource.org/spring-amqp
[html]
1. <!-- for rabbitmq -->
2. <dependency>
3. <groupId>com.rabbitmq</groupId>
4. <artifactId>amqp-client</artifactId>
5. <version>2.8.2</version>
6. </dependency>
7. <dependency>
8. <groupId>org.springframework.amqp</groupId>
9. <artifactId>spring-amqp</artifactId>
10. <version>1.1.1.RELEASE</version>
11. </dependency>
12. <dependency>
13. <groupId>org.springframework.amqp</groupId>
14. <artifactId>spring-rabbit</artifactId>
15. <version>1.1.1.RELEASE</version>
16. </dependency>
17. <dependency>
18. <groupId>com.caucho</groupId>
19. <artifactId>hessian</artifactId>
20. <version>4.0.7</version>
21. </dependency>
22. </dependencies>
首先我们需要一个用来在app和rabbitmq之间传递消息的持有对象
[java]
1. public class EventMessage implements Serializable{
2.
3. private String queueName;
4.
5. private String exchangeName;
6.
7. private byte[] eventData;
8.
9. public EventMessage(String queueName, String exchangeName, byte[] eventData) {
10. this.queueName = queueName;
11. this.exchangeName = exchangeName;
12. this.eventData = eventData;
13. }
14.
15. public EventMessage() {
16. }
17.
18. public String getQueueName() {
19. return queueName;
20. }
21.
22. public String getExchangeName() {
23. return exchangeName;
24. }
25.
26. public byte[] getEventData() {
27. return eventData;
28. }
29.
30. @Override
31. public String toString() {
32. return "EopEventMessage [queueName=" + queueName + ", exchangeName="
33. ", eventData=" + Arrays.toString(eventData)
34. "]";
35. }
36. }
为了可以发送和接受这个消息持有对象,我们还需要需要一个用来序列化和反序列化的工厂
[java]
1. public interface CodecFactory {
2.
3. byte[] serialize(Object obj) throws IOException;
4.
5. byte[] in) throws IOException;
6.
7. }
下面是编码解码的实现类,用了hessian来实现,大家可以自行选择序列化方式
[java]
1. public class HessionCodecFactory implements CodecFactory {
2.
3. private final Logger logger = Logger.getLogger(HessionCodecFactory.class);
4.
5. @Override
6. public byte[] serialize(Object obj) throws IOException {
7. null;
8. null;
9. try {
10. new ByteArrayOutputStream(1024);
11. new HessianOutput(baos);
12. output.startCall();
13. output.writeObject(obj);
14. output.completeCall();
15. catch (final IOException ex) {
16. throw ex;
17. finally {
18. if (output != null) {
19. try {
20. baos.close();
21. catch (final IOException ex) {
22. this.logger.error("Failed to close stream.", ex);
23. }
24. }
25. }
26. return baos != null ? baos.toByteArray() : null;
27. }
28.
29. @Override
30. public Object deSerialize(byte[] in) throws IOException {
31. null;
32. null;
33. null;
34. try {
35. new ByteArrayInputStream(in);
36. new HessianInput(bais);
37. input.startReply();
38. obj = input.readObject();
39. input.completeReply();
40. catch (final IOException ex) {
41. throw ex;
42. catch (final Throwable e) {
43. this.logger.error("Failed to decode object.", e);
44. finally {
45. if (input != null) {
46. try {
47. bais.close();
48. catch (final IOException ex) {
49. this.logger.error("Failed to close stream.", ex);
50. }
51. }
52. }
53. return obj;
54. }
55.
56. }
接下来就先实现发送功能,新增一个接口专门用来实现发送功能
[java]
1. public interface EventTemplate {
2.
3. void send(String queueName,String exchangeName,Object eventContent) throws SendRefuseException;
4.
5. void send(String queueName,String exchangeName,Object eventContent,CodecFactory codecFactory) throws SendRefuseException;
6. }
SendRefuseException是自定义的发送失败异常类
下面是它的实现类,主要的任务就是将数据转换为EventMessage
[java]
1. public class DefaultEventTemplate implements EventTemplate {
2.
3. private static final Logger logger = Logger.getLogger(DefaultEventTemplate.class);
4.
5. private AmqpTemplate eventAmqpTemplate;
6.
7. private CodecFactory defaultCodecFactory;
8.
9. // private DefaultEventController eec;
10. //
11. // public DefaultEventTemplate(AmqpTemplate eopAmqpTemplate,
12. // CodecFactory defaultCodecFactory, DefaultEventController eec) {
13. // this.eventAmqpTemplate = eopAmqpTemplate;
14. // this.defaultCodecFactory = defaultCodecFactory;
15. // this.eec = eec;
16. // }
17.
18. public DefaultEventTemplate(AmqpTemplate eopAmqpTemplate,CodecFactory defaultCodecFactory) {
19. this.eventAmqpTemplate = eopAmqpTemplate;
20. this.defaultCodecFactory = defaultCodecFactory;
21. }
22.
23. @Override
24. public void send(String queueName, String exchangeName, Object eventContent)
25. throws SendRefuseException {
26. this.send(queueName, exchangeName, eventContent, defaultCodecFactory);
27. }
28.
29. @Override
30. public void send(String queueName, String exchangeName, Object eventContent,
31. throws SendRefuseException {
32. if (StringUtils.isEmpty(queueName) || StringUtils.isEmpty(exchangeName)) {
33. throw new SendRefuseException("queueName exchangeName can not be empty.");
34. }
35.
36. // if (!eec.beBinded(exchangeName, queueName))
37. // eec.declareBinding(exchangeName, queueName);
38.
39. byte[] eventContentBytes = null;
40. if (codecFactory == null) {
41. if (eventContent == null) {
42. "Find eventContent is null,are you sure...");
43. else {
44. throw new SendRefuseException(
45. "codecFactory must not be null ,unless eventContent is null");
46. }
47. else {
48. try {
49. eventContentBytes = codecFactory.serialize(eventContent);
50. catch (IOException e) {
51. throw new SendRefuseException(e);
52. }
53. }
54.
55. // 构造成Message
56. new EventMessage(queueName, exchangeName,
57. eventContentBytes);
58. try {
59. eventAmqpTemplate.convertAndSend(exchangeName, queueName, msg);
60. catch (AmqpException e) {
61. "send event fail. Event Message : [" + eventContent + "]", e);
62. throw new SendRefuseException("send event fail", e);
63. }
64. }
65. }
注释的地方稍后会用到,主要是防止数据数据发送的地方没有事先声明
然后我们再实现接受消息
首先我们需要一个消费接口,所有的消费程序都实现这个类
[java]
1. public interface EventProcesser {
2. public void process(Object e);
3. }
为了能够将不同类型的消息交由对应的程序来处理,我们还需要一个消息处理适配器
[java]
1. /**
2. * MessageListenerAdapter的Pojo
3. * <p>消息处理适配器,主要功能:</p>
4. * <p>1、将不同的消息类型绑定到对应的处理器并本地缓存,如将queue01+exchange01的消息统一交由A处理器来出来</p>
5. * <p>2、执行消息的消费分发,调用相应的处理器来消费属于它的消息</p>
6. *
7. */
8. public class MessageAdapterHandler {
9.
10. private static final Logger logger = Logger.getLogger(MessageAdapterHandler.class);
11.
12. private ConcurrentMap<String, EventProcessorWrap> epwMap;
13.
14. public MessageAdapterHandler() {
15. this.epwMap = new ConcurrentHashMap<String, EventProcessorWrap>();
16. }
17.
18. public void handleMessage(EventMessage eem) {
19. "Receive an EventMessage: [" + eem + "]");
20. // 先要判断接收到的message是否是空的,在某些异常情况下,会产生空值
21. if (eem == null) {
22. "Receive an null EventMessage, it may product some errors, and processing message is canceled.");
23. return;
24. }
25. if (StringUtils.isEmpty(eem.getQueueName()) || StringUtils.isEmpty(eem.getExchangeName())) {
26. "The EventMessage's queueName and exchangeName is empty, this is not allowed, and processing message is canceled.");
27. return;
28. }
29. // 解码,并交给对应的EventHandle执行
30. "|"+eem.getExchangeName());
31. if (eepw == null) {
32. "Receive an EopEventMessage, but no processor can do it.");
33. return;
34. }
35. try {
36. eepw.process(eem.getEventData());
37. catch (IOException e) {
38. "Event content can not be Deserialized, check the provided CodecFactory.",e);
39. return;
40. }
41. }
42.
43. protected void add(String queueName, String exchangeName, EventProcesser processor,CodecFactory codecFactory) {
44. if (StringUtils.isEmpty(queueName) || StringUtils.isEmpty(exchangeName) || processor == null || codecFactory == null) {
45. throw new RuntimeException("queueName and exchangeName can not be empty,and processor or codecFactory can not be null. ");
46. }
47. new EventProcessorWrap(codecFactory,processor);
48. "|" + exchangeName, epw);
49. if (oldProcessorWrap != null) {
50. "The processor of this queue and exchange exists, and the new one can't be add");
51. }
52. }
53.
54. protected Set<String> getAllBinding() {
55. Set<String> keySet = epwMap.keySet();
56. return keySet;
57. }
58.
59. protected static class EventProcessorWrap {
60.
61. private CodecFactory codecFactory;
62.
63. private EventProcesser eep;
64.
65. protected EventProcessorWrap(CodecFactory codecFactory,
66. EventProcesser eep) {
67. this.codecFactory = codecFactory;
68. this.eep = eep;
69. }
70.
71. public void process(byte[] eventData) throws IOException{
72. Object obj = codecFactory.deSerialize(eventData);
73. eep.process(obj);
74. }
75. }
76. }
这是正常情况下的消息处理方式,如果rabbitmq消息接受发生异常,也要监控到,新增一个消费类专门用来处理错误异常的消息
[java]
1. public class MessageErrorHandler implements ErrorHandler{
2.
3. private static final Logger logger = Logger.getLogger(MessageErrorHandler.class);
4.
5. @Override
6. public void handleError(Throwable t) {
7. "RabbitMQ happen a error:" + t.getMessage(), t);
8. }
9.
10. }
接下来我们可能需要一个专门配置和rabbitmq通信的一些信息,比如地址,端口等信息
[java]
1. public class EventControlConfig {
2.
3. private final static int DEFAULT_PORT = 5672;
4.
5. private final static String DEFAULT_USERNAME = "guest";
6.
7. private final static String DEFAULT_PASSWORD = "guest";
8.
9. private final static int DEFAULT_PROCESS_THREAD_NUM = Runtime.getRuntime().availableProcessors() * 2;
10.
11. private static final int PREFETCH_SIZE = 1;
12.
13. private String serverHost ;
14.
15. private int port = DEFAULT_PORT;
16.
17. private String username = DEFAULT_USERNAME;
18.
19. private String password = DEFAULT_PASSWORD;
20.
21. private String virtualHost;
22.
23. /**
24. * 和rabbitmq建立连接的超时时间
25. */
26. private int connectionTimeout = 0;
27.
28. /**
29. * 事件消息处理线程数,默认是 CPU核数 * 2
30. */
31. private int eventMsgProcessNum;
32.
33. /**
34. * 每次消费消息的预取值
35. */
36. private int prefetchSize;
37.
38. public EventControlConfig(String serverHost) {
39. this(serverHost,DEFAULT_PORT,DEFAULT_USERNAME,DEFAULT_PASSWORD,null,0,DEFAULT_PROCESS_THREAD_NUM,DEFAULT_PROCESS_THREAD_NUM,new HessionCodecFactory());
40. }
41.
42. public EventControlConfig(String serverHost, int port, String username,
43. int connectionTimeout,
44. int eventMsgProcessNum,int prefetchSize,CodecFactory defaultCodecFactory) {
45. this.serverHost = serverHost;
46. this.port = port>0?port:DEFAULT_PORT;
47. this.username = username;
48. this.password = password;
49. this.virtualHost = virtualHost;
50. this.connectionTimeout = connectionTimeout>0?connectionTimeout:0;
51. this.eventMsgProcessNum = eventMsgProcessNum>0?eventMsgProcessNum:DEFAULT_PROCESS_THREAD_NUM;
52. this.prefetchSize = prefetchSize>0?prefetchSize:PREFETCH_SIZE;
53. }
54.
55. public String getServerHost() {
56. return serverHost;
57. }
58.
59. public int getPort() {
60. return port;
61. }
62.
63. public String getUsername() {
64. return username;
65. }
66.
67. public String getPassword() {
68. return password;
69. }
70.
71. public String getVirtualHost() {
72. return virtualHost;
73. }
74.
75. public int getConnectionTimeout() {
76. return connectionTimeout;
77. }
78.
79. public int getEventMsgProcessNum() {
80. return eventMsgProcessNum;
81. }
82.
83. public int getPrefetchSize() {
84. return prefetchSize;
85. }
86.
87. }
具体的发送、接受程序已经好了,接下来也是最重要的就是管理控制和rabbitmq的通信
[java]
1. public interface EventController {
2.
3. /**
4. * 控制器启动方法
5. */
6. void start();
7.
8. /**
9. * 获取发送模版
10. */
11. EventTemplate getEopEventTemplate();
12.
13. /**
14. * 绑定消费程序到对应的exchange和queue
15. */
16. EventController add(String queueName, String exchangeName, EventProcesser eventProcesser);
17.
18. /*in map, the key is queue name, but value is exchange name*/
19. EventController add(Map<String,String> bindings, EventProcesser eventProcesser);
20.
21. }
它的实现类如下:
[java]
1. /**
2. * 和rabbitmq通信的控制器,主要负责:
3. * <p>1、和rabbitmq建立连接</p>
4. * <p>2、声明exChange和queue以及它们的绑定关系</p>
5. * <p>3、启动消息监听容器,并将不同消息的处理者绑定到对应的exchange和queue上</p>
6. * <p>4、持有消息发送模版以及所有exchange、queue和绑定关系的本地缓存</p>
7. * @author yangyong
8. *
9. */
10. public class DefaultEventController implements EventController {
11.
12. private CachingConnectionFactory rabbitConnectionFactory;
13.
14. private EventControlConfig config;
15.
16. private RabbitAdmin rabbitAdmin;
17.
18. private CodecFactory defaultCodecFactory = new HessionCodecFactory();
19.
20. private SimpleMessageListenerContainer msgListenerContainer; // rabbitMQ msg listener container
21.
22. private MessageAdapterHandler msgAdapterHandler = new MessageAdapterHandler();
23.
24. private MessageConverter serializerMessageConverter = new SerializerMessageConverter(); // 直接指定
25. //queue cache, key is exchangeName
26. private Map<String, DirectExchange> exchanges = new HashMap<String,DirectExchange>();
27. //queue cache, key is queueName
28. private Map<String, Queue> queues = new HashMap<String, Queue>();
29. //bind relation of queue to exchange cache, value is exchangeName | queueName
30. private Set<String> binded = new HashSet<String>();
31.
32. private EventTemplate eventTemplate; // 给App使用的Event发送客户端
33.
34. private AtomicBoolean isStarted = new AtomicBoolean(false);
35.
36. private static DefaultEventController defaultEventController;
37.
38. public synchronized static DefaultEventController getInstance(EventControlConfig config){
39. if(defaultEventController==null){
40. new DefaultEventController(config);
41. }
42. return defaultEventController;
43. }
44.
45. private DefaultEventController(EventControlConfig config){
46. if (config == null) {
47. throw new IllegalArgumentException("Config can not be null.");
48. }
49. this.config = config;
50. initRabbitConnectionFactory();
51. // 初始化AmqpAdmin
52. new RabbitAdmin(rabbitConnectionFactory);
53. // 初始化RabbitTemplate
54. new RabbitTemplate(rabbitConnectionFactory);
55. rabbitTemplate.setMessageConverter(serializerMessageConverter);
56. new DefaultEventTemplate(rabbitTemplate,defaultCodecFactory, this);
57. }
58.
59. /**
60. * 初始化rabbitmq连接
61. */
62. private void initRabbitConnectionFactory() {
63. new CachingConnectionFactory();
64. rabbitConnectionFactory.setHost(config.getServerHost());
65. rabbitConnectionFactory.setChannelCacheSize(config.getEventMsgProcessNum());
66. rabbitConnectionFactory.setPort(config.getPort());
67. rabbitConnectionFactory.setUsername(config.getUsername());
68. rabbitConnectionFactory.setPassword(config.getPassword());
69. if (!StringUtils.isEmpty(config.getVirtualHost())) {
70. rabbitConnectionFactory.setVirtualHost(config.getVirtualHost());
71. }
72. }
73.
74. /**
75. * 注销程序
76. */
77. public synchronized void destroy() throws Exception {
78. if (!isStarted.get()) {
79. return;
80. }
81. msgListenerContainer.stop();
82. null;
83. null;
84. rabbitConnectionFactory.destroy();
85. }
86.
87. @Override
88. public void start() {
89. if (isStarted.get()) {
90. return;
91. }
92. Set<String> mapping = msgAdapterHandler.getAllBinding();
93. for (String relation : mapping) {
94. "\\|");
95. 1], relaArr[0]);
96. }
97. initMsgListenerAdapter();
98. true);
99. }
100.
101. /**
102. * 初始化消息监听器容器
103. */
104. private void initMsgListenerAdapter(){
105. new MessageListenerAdapter(msgAdapterHandler,serializerMessageConverter);
106. new SimpleMessageListenerContainer();
107. msgListenerContainer.setConnectionFactory(rabbitConnectionFactory);
108. msgListenerContainer.setAcknowledgeMode(AcknowledgeMode.AUTO);
109. msgListenerContainer.setMessageListener(listener);
110. new MessageErrorHandler());
111. // 设置每个消费者消息的预取值
112. msgListenerContainer.setConcurrentConsumers(config.getEventMsgProcessNum());
113. //设置有事务时处理的消息数
114. new Queue[queues.size()]));
115. msgListenerContainer.start();
116. }
117.
118. @Override
119. public EventTemplate getEopEventTemplate() {
120. return eventTemplate;
121. }
122.
123. @Override
124. public EventController add(String queueName, String exchangeName,EventProcesser eventProcesser) {
125. return add(queueName, exchangeName, eventProcesser, defaultCodecFactory);
126. }
127.
128. public EventController add(String queueName, String exchangeName,EventProcesser eventProcesser,CodecFactory codecFactory) {
129. msgAdapterHandler.add(queueName, exchangeName, eventProcesser, defaultCodecFactory);
130. if(isStarted.get()){
131. initMsgListenerAdapter();
132. }
133. return this;
134. }
135.
136. @Override
137. public EventController add(Map<String, String> bindings,
138. EventProcesser eventProcesser) {
139. return add(bindings, eventProcesser,defaultCodecFactory);
140. }
141.
142. public EventController add(Map<String, String> bindings,
143. EventProcesser eventProcesser, CodecFactory codecFactory) {
144. for(Map.Entry<String, String> item: bindings.entrySet())
145. msgAdapterHandler.add(item.getKey(),item.getValue(), eventProcesser,codecFactory);
146. return this;
147. }
148.
149. /**
150. * exchange和queue是否已经绑定
151. */
152. protected boolean beBinded(String exchangeName, String queueName) {
153. return binded.contains(exchangeName+"|"+queueName);
154. }
155.
156. /**
157. * 声明exchange和queue已经它们的绑定关系
158. */
159. protected synchronized void declareBinding(String exchangeName, String queueName) {
160. "|"+queueName;
161. if (binded.contains(bindRelation)) return;
162.
163. boolean needBinding = false;
164. DirectExchange directExchange = exchanges.get(exchangeName);
165. if(directExchange == null) {
166. new DirectExchange(exchangeName, true, false, null);
167. exchanges.put(exchangeName, directExchange);
168. //声明exchange
169. true;
170. }
171.
172. Queue queue = queues.get(queueName);
173. if(queue == null) {
174. new Queue(queueName, true, false, false);
175. queues.put(queueName, queue);
176. //声明queue
177. true;
178. }
179.
180. if(needBinding) {
181. //将queue绑定到exchange
182. //声明绑定关系
183. binded.add(bindRelation);
184. }
185. }
186.
187. }
搞定,现在可以将DefaultEventTemplate里的注释去掉了,接下来最后完成单元测试,为了测试传递对象,建立一个PO
[java]
1. @SuppressWarnings("serial")
2. public class People implements Serializable{
3. private int id;
4. private String name;
5. private boolean male;
6. private People spouse;
7. private List<People> friends;
8. public int getId() {
9. return id;
10. }
11. public void setId(int id) {
12. this.id = id;
13. }
14. public String getName() {
15. return name;
16. }
17. public void setName(String name) {
18. this.name = name;
19. }
20. public boolean isMale() {
21. return male;
22. }
23. public void setMale(boolean male) {
24. this.male = male;
25. }
26. public People getSpouse() {
27. return spouse;
28. }
29. public void setSpouse(People spouse) {
30. this.spouse = spouse;
31. }
32. public List<People> getFriends() {
33. return friends;
34. }
35. public void setFriends(List<People> friends) {
36. this.friends = friends;
37. }
38.
39. @Override
40. public String toString() {
41. // TODO Auto-generated method stub
42. return "People[id="+id+",name="+name+",male="+male+"]";
43. }
44. }
建立单元测试
1. public class RabbitMqTest{
2.
3. private String defaultHost = "127.0.0.1";
4.
5. private String defaultExchange = "EXCHANGE_DIRECT_TEST";
6.
7. private String defaultQueue = "QUEUE_TEST";
8.
9. private DefaultEventController controller;
10.
11. private EventTemplate eventTemplate;
12.
13. @Before
14. public void init() throws IOException{
15. new EventControlConfig(defaultHost);
16. controller = DefaultEventController.getInstance(config);
17. eventTemplate = controller.getEopEventTemplate();
18. new ApiProcessEventProcessor());
19. controller.start();
20. }
21.
22. @Test
23. public void sendString() throws SendRefuseException{
24. "hello world");
25. }
26.
27. @Test
28. public void sendObject() throws SendRefuseException{
29. eventTemplate.send(defaultQueue, defaultExchange, mockObj());
30. }
31.
32. @Test
33. public void sendTemp() throws SendRefuseException, InterruptedException{
34. "EXCHANGE_DIRECT_TEST_TEMP";//以前未声明的exchange
35. "QUEUE_TEST_TEMP";//以前未声明的queue
36. eventTemplate.send(tempQueue, tempExchange, mockObj());
37. //发送成功后此时不会接受到消息,还需要绑定对应的消费程序
38. new ApiProcessEventProcessor());
39. }
40.
41. @After
42. public void end() throws InterruptedException{
43. 2000);
44. }
45.
46. private People mockObj(){
47. new People();
48. 1);
49. "JACK");
50. true);
51.
52. new ArrayList<>();
53. friends.add(jack);
54. new People();
55. 1);
56. "韩梅梅");
57. false);
58. hanMeiMei.setFriends(friends);
59.
60. new People();
61. 2);
62. "李雷");
63. true);
64. liLei.setFriends(friends);
65. liLei.setSpouse(hanMeiMei);
66. hanMeiMei.setSpouse(liLei);
67. return hanMeiMei;
68. }
69.
70. class ApiProcessEventProcessor implements EventProcesser{
71. @Override
72. public void process(Object e) {//消费程序这里只是打印信息
73. Assert.assertNotNull(e);
74. System.out.println(e);
75. if(e instanceof People){
76. People people = (People)e;
77. System.out.println(people.getSpouse());
78. System.out.println(people.getFriends());
79. }
80. }
81. }
82. }
源码地址请点击这里