文章目录
RPC
先说下什么是rpc,rpc(remote procedure call)即远程过程调用,相对的是本地过程调用,本地调用就像你在一个单体系统中的用户模块去调用订单的接口。而rpc就是把系统之间调用用户系统与订单系统互不影响,而订单系统开放了接口,用户系统通过某种通信协议去调用该接口。一般我们称调用方为客户端,提供接口的一方称为服务端。
当然,rpc的框架也有很多,比如spring cloud、阿里的dubbo、Facebook 的 Thrift、Google 的 gRPC、Twitter 的 Finagle等,此处不详细讲,知道这个意即可,仅为我们通过RabbitMQ实现rpc铺路。
RabbitMQ实现rpc流程
客户端发送请求到请求队列,这里有个correlationId,这个参数是用来标记这个完整的请求,因为如果没有这个参数,你从回复队列中获取的数据不知道是哪个请求回复的数据。而当客户端发送请求和服务端返回数据指定相同的correlationId,我们只要在客户端判断correlationId则知道是哪一个请求。
客户端代码:
@Slf4j
public class MyRpcClient {
private Connection conn= RabbitmqUtil.conn();
private Set<String> correlationIds=new HashSet<>();
public void sendAndReceive() {
try (Channel channel=conn.createChannel()){
//声明回复队列
channel.queueDeclare("replyQueue1",true,false,false,null);
//声明请求队列
channel.queueDeclare("rpcQ1",true,false,false,null);
String correlationId= UUID.randomUUID().toString();
correlationIds.add(correlationId);
channel.basicConsume("replyQueue1",false,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
log.info("收到服务端响应");
correlationIds.forEach(s->{
if (s.equals(properties.getCorrelationId())){
log.info("{}:{}","相应数据",new String(body));
}
});
channel.basicAck(envelope.getDeliveryTag(),false);
}
});
AMQP.BasicProperties basicProperties=new AMQP.BasicProperties().builder()
.contentType(MessageProperties.PERSISTENT_TEXT_PLAIN.getContentType())
.correlationId(correlationId)
.replyTo("replyQueue1")
.build();
//默认交换器"",是direct类型。且当声明队列的时候都会绑定到默认队列。so这里没有绑定的代码
channel.basicPublish("","rpcQ1",basicProperties,"我要你返回我hello".getBytes());
log.info("client发送请求");
Thread.sleep(60*1000);
}catch (Exception e){
e.printStackTrace();
}finally {
RabbitmqUtil.close(conn);
}
}
}
服务端代码:
public class MyRpcServer {
private Connection conn= RabbitmqUtil.conn();
public void receiveAndReply() {
try (Channel channel=conn.createChannel()){
//声明请求队列
channel.queueDeclare("rpcQ1",true,false,false,null);
channel.basicConsume("rpcQ1",false,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
AMQP.BasicProperties basicProperties=new AMQP.BasicProperties().builder()
.contentType(MessageProperties.PERSISTENT_TEXT_PLAIN.getContentType())
.correlationId(properties.getCorrelationId())
.build();
channel.basicPublish("",properties.getReplyTo(),basicProperties,"hello".getBytes());
channel.basicAck(envelope.getDeliveryTag(),false);
}
});
Thread.sleep(60*1000);
}catch (Exception e){
e.printStackTrace();
}finally {
RabbitmqUtil.close(conn);
}
}
}
测试代码:
@Test
private void test() {
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.submit(() -> {
new MyRpcServer().receiveAndReply();
});
executorService.submit(() -> {
new MyRpcClient().sendAndReceive();
});
try {
Thread.sleep(60*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
看下运行结果
后话
虽然我们使用RabbitMQ实现了rpc,但是在现实场景大多不会使用这种方式进行RPC,当我们要调用多个接口,甚至所调用的接口在去调用其他的接口,其实现的复杂度较高。