面向服务的消息队列
1. 遇到问题
在团队开发时,为了让团队合作更愉快,于是有了RPC(远程方法调用),可以让两个项目相互使用API时更加友好,因为RPC调用非本地API机制和本地调用一样,无需考虑具体实现。
RPC与MQ适合的场景
RPC适合使用在需要立即得到(远程方法)结果,在开发中和十分方便
但是在调用之后就要等待返回结果(当然也有异步RPC)是很费时间的
且访问量过高时RPC性能会拖慢服务器
什么时候适合使用MQ呢?
- 不希望发送端(消费者)受限于处理端(生产者)的速度时
- 可以很好处理服务器峰值访问量高的情况(请求会被一个一个的放进队列,而服务器会一个一个的完成,不会因为峰值过高而宕机)
2. 实现
(我的上一篇文章实现了简单的消息队列:传送门)
在整合网络服务上参考了RPC的实现,不过更简单
发送者只需要给服务器输出流写入消息
接收者只需要从服务器输出流得到消息
注意几点:1.处理访问空队列的情况,2.为队列上同步锁
- 发布服务端
Publisher.java:/** * 一个端口对应一个队列 * @param port */ public void publishQueue(int port) { MyMessageQueue queue = new MyMessageQueue(); // 创建一个新队列 System.out.println("Queue Published on " + port); try { ServerSocket ss = new ServerSocket(port); while (true) { Socket socket = ss.accept(); // 等待连接 ObjectInputStream ois = new ObjectInputStream(socket.getInputStream()); String methodName = ois.readUTF(); // 读取请求的方法名 Class[] types = (Class[]) ois.readObject(); // 读取类型 Object[] args = (Object[]) ois.readObject(); // 读取参数 Class clazz = MyMessageQueue.class; Method method = clazz.getMethod(methodName, types); Object ret = method.invoke(queue, args); // 执行方法 ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream()); oos.writeObject(ret); // 写入输出流 oos.flush(); ois.close(); oos.close(); socket.close(); } } catch(Exception e) { e.printStackTrace(); } }
- 客户端(发布消息或接收消息)
使用方要有接口,用于传递方法名等参数,来执行远端方法
Client.java:/** * * @param clazz 队列的接口 * @return */ public Object queueProxy(Class clazz) { return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz }, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Socket socket = new Socket(address, port); ObjectOutputStream oos = new ObjectOutputStream(socket .getOutputStream()); String methodName = method.getName(); Class[] types = method.getParameterTypes(); oos.writeUTF(methodName); // 写入方法名 oos.writeObject(types); // 写入参数类型 oos.writeObject(args); // 写入参数 oos.flush(); ObjectInputStream ois = new ObjectInputStream(socket.getInputStream()); Object ret = ois.readObject(); // 得到响应 oos.close(); ois.close(); socket.close(); return ret; } }); }
这里的过程类似RPC,这篇就不多赘述。
3. 测试
PublisherTest.java:
public void publishTest()
{
Publisher publisher = new Publisher();
publisher.publishQueue(9092); // 在9092端口发布队列服务
}
ClientTest.java:
static String address = "localhost";
static int port = 9092;
public void clientTest()
{
MessageQueue queue = (MessageQueue) queueProxy(MessageQueue.class);
String result = queue.post("drake 1");
String result1 = queue.post("drake 2");
System.out.println(result + " " + result1);
System.out.println(queue.size());
String msg = queue.nextMessage();
System.out.println("get: " + msg);
String msg1 = queue.nextMessage();
System.out.println("get: " + msg1);
String msg2 = queue.nextMessage();
System.out.println("get: " + msg2);
}
输出:
Queue Published on 9092
added:drake-1[strategy: None]
added:drake-2[strategy: None]
size: 2
get: drake-1
get: drake-2
get: null
这样就部署了一个消息队列服务的基本功能,接下来还有很多需要解决,比如:
- 服务器在高并发情况下的负载均衡(目前是单线程和悲观锁,在高并发下性能不好)
- 队列策略(比如按权值的优先队列)
- 延迟队列
这些是我暂时能想到的,所以架构之路还很长~~~