RabbitMQ之RPC

来源:互联网 发布:淘宝鹊桥活动怎样 编辑:程序博客网 时间:2024/05/17 02:13

首先要弄明白,RPC是个什么东西。(RPC) Remote Procedure Call Protocol 远程过程调用协议
在一个大型的公司,系统由大大小小的服务构成,不同的团队维护不同的代码,部署在不同的机器。但是在做开发时候往往要用到其它团队的方法,因为已经有了实现。但是这些服务部署不同的机器上,想要调用就需要网络通信,这些代码繁琐且复杂,一不小心就会写的很低效。RPC协议定义了规划,其它的公司都给出了不同的实现。比如微软的wcf,以及现在火热的WebApi。

RPC工作流程:
1)、客户端启动时,创建了一个匿名的回调队列。
2)、在一个RPC请求中,客户端发送一个消息,它有两个属性:1.REPLYTO,用来设置回调队列名;2.correlationId,对于每个请求都被设置成唯一的值。
3)、请求被发送到rpc_queue队列.
4)、RPC工作者(又名:服务器)等待接收该队列的请求。当收到一个请求,它就会处理并把结果发送给客户端,使用的队列是replyTo字段指定的。
5)、客户端等待接收回调队列中的数据。当接到一个消息,它会检查它的correlationId属性。如果它和设置的相匹配,就会把响应返回给应用程序。

服务端

import com.rabbitmq.client.*;import com.ydh.util.ConnectionFactoryUtil;import java.io.IOException;//RPC调用服务端  public class RPCService {    private static final String RPC_QUEUE_NAME = "rpc_queue";    public static void main(String[] argv) {        Connection connection;        try {            connection = ConnectionFactoryUtil.getInstance();            Channel channel = connection.createChannel();            channel.queueDeclare(RPC_QUEUE_NAME, false, false, false, null);            channel.basicQos(1);            System.out.println(" [x] Awaiting RPC requests");            Consumer consumer = new DefaultConsumer(channel) {                @Override                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {                    AMQP.BasicProperties replyProps = new AMQP.BasicProperties                            .Builder()                            .correlationId(properties.getCorrelationId())                            .build();                    String response = "";                    try {                        String message = new String(body, "UTF-8");                        int n = Integer.parseInt(message);                        System.out.println(" [.] fib(" + message + ")");                        response += fib(n);                    } catch (RuntimeException e) {                        System.out.println(" [.] " + e.toString());                    } finally {                        channel.basicPublish("", properties.getReplyTo(), replyProps, response.getBytes("UTF-8"));                        channel.basicAck(envelope.getDeliveryTag(), false);                    }                }            };            channel.basicConsume(RPC_QUEUE_NAME, false, consumer);        } catch (Exception e) {            e.printStackTrace();        }    }    private static int fib(int n) {        if (n == 0) return 0;        if (n == 1) return 1;        return fib(n-1) + fib(n-2);    }}

客户端

import com.rabbitmq.client.*;import com.ydh.util.ConnectionFactoryUtil;import java.io.IOException;import java.util.UUID;import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.BlockingQueue;//RPC调用客户端  public class RPCClient {    private Connection connection;    private Channel channel;    private String requestQueueName = "rpc_queue";    private String replyQueueName;    public RPCClient() throws Exception {        // • 先建立一个连接和一个通道,并为回调声明一个唯一的'回调'队列        connection = ConnectionFactoryUtil.getInstance();        channel = connection.createChannel();        // • 注册'回调'队列,这样就可以收到RPC响应        replyQueueName = channel.queueDeclare().getQueue();    }    // 发送RPC请求    public String call(String message) throws IOException, InterruptedException {        String corrId = UUID.randomUUID().toString();        AMQP.BasicProperties props = new AMQP.BasicProperties                .Builder()                .correlationId(corrId)                .replyTo(replyQueueName)                .build();        channel.basicPublish("", requestQueueName, props, message.getBytes("UTF-8"));        final BlockingQueue<String> response = new ArrayBlockingQueue<>(1);        channel.basicConsume(replyQueueName, true, new DefaultConsumer(channel) {            @Override            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {                if (properties.getCorrelationId().equals(corrId)) {                    response.offer(new String(body, "UTF-8"));                }            }        });        return response.take();    }    public void close() throws IOException {        connection.close();    }}

客户端main主函数

public class RPCMain {    public static void main(String[] args) throws Exception {        RPCClient rpcClient = new RPCClient();        System.out.println(" [x] Requesting getMd5String(abc)");        String response = rpcClient.call("abc");        System.out.println(" [.] Got '" + response + "'");        rpcClient.close();    }}

先运行服务端,再运行RPCMain,发送消息调用RPC。
这里介绍的是该设计不是实现RPC服务的唯一可能,但它有一些重要的优点:
1)如果RPC服务器速度太慢,你可以通过运行多个RPC服务器。尝试在一个新的控制台上运行第二RPCServer。
2)RPC客户端只发送和接收一个消息。不需要queueDeclare那样要求同步调用。因此,RPC客户端只需要在一个网络上发送和接收为一个单一的RPC请求。

原创粉丝点击