RabbitMQ协议基础及C++和Java混合开发
目前面对大多数的需要在异构系统间进行消息传递技术路线,大多会选择socket或webservice。这两种技术的共同特点是耦合紧,调试依赖双方同步,但是效率高。除此以外,使用消息队列(MQ)的应用场景也偶尔能遇到。本文就将要从AMQP协议说起,重点介绍利用RabbitMQ实现C++和Java跨系统开发的实践。
一、AMQP是什么
AMQP又称为高级消息队列协议,是一种进程间进行异步消息的网络协议。它的出现是为了让各类消息中间件提供统一服务,以降低系统集成的开销。目前,完全准寻AMQP协议的消息中间件只有RabbitMQ。虽然各大中间件产品也都针对不同的语言推出了客户端。但是,无论是从业务适应性还是集成通用性上来说,比较推荐的还是RabbitMQ。不同的消息中间件在性能上的差异网上资料很多,这里不再赘述。
amqp协议和http协议一样都是建立在TCP/IP协议簇之上的应用层协议。不同于http协议的,它是一个二进制协议,具有多信道,异步,高效等特点。amqp协议规定了从消息发布者到消息接收者之间的消息传递方式,并且提出了交换机(Exchange)队列(Queue)以及他们之间的路由(Routing)。
作为一套标准协议,使用者甚至可以完全根据amqp的协议规范定制化的开发出客户端和RabbitMQ通信,这一特点也让RabbitMQ在业务通用性上具备了得天独厚的优势。标准的amqp协议格式如下:
amqp://<username>:<password>@<host>:<port>/<virtual>
username: 用户名
password: 登录密码
host: 服务所在主机地址
port: 服务端口号
virtual: 虚拟路径
AMQP协议最值得学习的地方在于,它定义了消息的发送和投递过程:
交换机(Exchange)负责接收消息,并根据提前指定的规则(Routing)投送消息到特定队列(Queue)。消费者监听队列,并处理消息。如果多个消费者监听同一个队列,消息一般会轮流的发送给它们。以实现负载均衡。此外,通过虚拟路径约束还允许在不同的虚拟路径下建立同命队列。
AMQP协议默认提供了四种类型的交换机:
直接交换机(Direct Exchange):根据路由键的不同将消息直接发送到不同队列,未匹配路由键的消息会被丢弃。


扇形交换机(Funout Exchange):扇形交换机是实现广播的基础,它能够同时将消息推送给多个队列。

主题交换机(Topic Exchange):交换机会根据路由键进行模糊匹配,从而完成消息投送。

头交换机(Header Exchange):它不依赖特定路由键,而是将投送目标写在消息头,支持字典类型,配置更加灵活。
二、C++开发指南
官网提供了其它常见语言的开发向导,对于C++个人推荐使用AMQP-CPP这套库。另外还需要一套网络库支持,个人也推荐libevent。编译方法可以参考github上的说明。发送方式区别于传统的socket,你不应该将一条消息分多个部分发送。因此推荐使用对象序列化模型直接转换为字节数组,同样受到tcp/ip传输的制约,你应该选择高效的序列化工具来进行。个人推荐使用protobuf,同样作为一种跨平台的支持。
下面以一套RPC调用为例进行说明:
#include <iostream>
#include "event2/event.h"
#include "amqpcpp.h"
#include "amqpcpp/libevent.h"
#include "amqp_msg.pb.h"
#include <string> using namespace std;
using namespace amqp; int main()
{
event_base *base = event_base_new(); // 通过libevent启动实践循环
AMQP::LibEventHandler handler(base);
AMQP::TcpConnection connection(&handler, AMQP::Address("localhost", 5672, AMQP::Login("guest", "guest"), "/"));
AMQP::TcpChannel channel(&connection); // 创建一条通道
channel.setQos(1);
// 监听login.rpc队列
channel.consume("login.rpc").onReceived([&](const AMQP::Message &message, uint64_t deliveryTag, bool redelivered)
{
cout << "login.rpc" << endl;
Login login;
login.ParseFromArray(message.body(), message.bodySize());
Response resp; // 创建应答对象
resp.set_status(Response_RespType_OK);
resp.set_session_id("acd");
char data[1024] = {0};
int data_size = resp.ByteSizeLong();
resp.SerializeToArray(data, data_size);
AMQP::Envelope env(data, data_size);
env.setCorrelationID(message.correlationID()); // 获取应答ID
channel.publish("", message.replyTo(), env); // 发送给应答队列
channel.ack(deliveryTag); // 向MQ发送确认
}).onSuccess([&](const std::string &consumertag)
{ }).onError([](const char *message)
{
event_base_loopbreak(base); // 发送错误中断事件循环
cout << message << endl;
});
// 监听logout.rpc队列
channel.consume("logout.rpc")
.onReceived([&channel](const AMQP::Message &message, uint64_t deliveryTag, bool)
{
Logout logout;
logout.ParseFromArray(message.body(), message.bodySize());
Response resp;
resp.set_status(Response_RespType_OK);
char data[1024] = {0};
int data_size = resp.ByteSizeLong();
resp.SerializeToArray(data, data_size);
AMQP::Envelope env(data, data_size);
env.setCorrelationID(message.correlationID());
channel.publish("", message.replyTo(), env);
channel.ack(deliveryTag);
}).onError([](const char *message)
{
event_base_loopbreak(base);
cout << message << endl;
});
event_base_dispatch(base); // 事件循环
event_base_free(base); // 释放
return 0;
}
AMQP-CPP库直接主动连接,或者你也可以在继承相应的Handler自己完成网络连接。此外,Connection 和 Channel的创建也都支持回调函数。如:
channel.onError([&base](const char* message)
{
std::cout << "Channel error: " << message << std::endl;
event_base_loopbreak(base);
});
channel.declareQueue("queueName", AMQP::passive)
.onSuccess([&](const string& name, uint32_t, uint32_t)
{
cout << "Queue Name:" << name << endl;
});
channel.declareExchange("logs", AMQP::fanout)
.onSuccess([&]() {})
三、Spring AMQP开发指南
与Spring整合的技巧,官网有很详细的指导意见。这里只给出与上文C++配合的请求端如何发送以及等待应答的核心代码:
@GetMapping("login")
public String loginRpc() throws InvalidProtocolBufferException {
AmqpMsg.Login login = AmqpMsg.Login.newBuilder()
.addParams(AmqpMsg.PairParams.newBuilder().setKey("username").setValue("admin").build())
.addParams(AmqpMsg.PairParams.newBuilder().setKey("password").setValue("admin").build())
.build();
byte[] resp = (byte[]) template.convertSendAndReceive(directExchange.getName(), "login.rpc", login.toByteArray());
AmqpMsg.Response response = AmqpMsg.Response.parseFrom(resp);
if (response.getStatus() == AmqpMsg.Response.RespType.OK) {
String sessionID = response.getSessionId();
System.out.println("登录成功 SessionID=" + sessionID);
return "SUCCESS";
}
return "ERROR";
}
@GetMapping("logout")
public String logoutRpc() throws InvalidProtocolBufferException {
AmqpMsg.Logout logout = AmqpMsg.Logout.newBuilder()
.setSessionId("123456").build();
byte[] resp = (byte[]) template.convertSendAndReceive(directExchange.getName(), "logout.rpc", logout.toByteArray());
AmqpMsg.Response response = AmqpMsg.Response.parseFrom(resp);
if(response.getStatus() == AmqpMsg.Response.RespType.OK) {
System.out.println("注销成功");
return "SUCCESS";
}
return "ERROR";
}
@Configuration
public class RPCRabbitConfig {
@Bean("simple")
public Queue simpleQueue() {
return new Queue("simple");
} @Bean("login.rpc")
public Queue loginRpcQueue() {
return new Queue("login.rpc");
} @Bean("logout.rpc")
public Queue logoutRpcQueue() {
return new Queue("logout.rpc");
} @Bean
public DirectExchange defaultExchange() {
return new DirectExchange("amq.direct");
} @Bean
public Binding loginRpcBinding(DirectExchange exchange, @Qualifier("login.rpc") Queue queue) {
return BindingBuilder.bind(queue).to(exchange).with("login.rpc");
} @Bean
public Binding logoutRpcBind(DirectExchange exchange, @Qualifier("logout.rpc") Queue queue) {
return BindingBuilder.bind(queue).to(exchange).with("logout.rpc");
}
}
后记:可能是由于工作上与架构的关系比较密切,目前在博客中提供的大多数解决方案都以跨平台应用为主。如果您对文章中介绍的知识点有任何的疑问也可以与我联系。
RabbitMQ协议基础及C++和Java混合开发的更多相关文章
- udp协议基础(转自疯狂java讲义)
第17章 网络编程 17.4 基于UDP协议的网络编程 UDP协议是一种不可靠的网络协议,它在通信实例的两端各建立一个Socket,但这两个Socket之间并没有虚拟链路,这两个Socket只是发 ...
- RabbitMQ的基础介绍
转自:http://blog.csdn.net/whycold/article/details/41119807 一.引言 你是否遇到过两个(多个)系统间需要通过定时任务来同步某些数据?你是否在为异构 ...
- Flutter基础系列之混合开发(二)
1.混合开发的场景 1.1作为独立页面加入 这是以页面级作为独立的模块加入,而不是页面的某个元素. 原生页面可以打开Flutter页面 Flutter页面可以打开原生页面 1.2作为页面的一部分嵌入 ...
- Atitit onvif协议获取rtsp地址播放java语言 attilx总结
Atitit onvif协议获取rtsp地址播放java语言 attilx总结 1.1. 获取rtsp地址的算法与流程1 1.2. Onvif摄像头的发现,ws的发现机制,使用xcf类库1 2. 调用 ...
- Java基础学习总结(70)——开发Java项目常用的工具汇总
要想全面了解java开发工具,我们首先需要先了解一下java程序的开发过程,通过这个过程我们能够了解到java开发都需要用到那些工具. 首先我们先了解完整项目开发过程,如图所示: 从上图中我们能看到一 ...
- RabbitMQ (基础知识)
AMQP简介 AMQP(Advanced Message Queue )即:高级消息队列协议:,是应用层协议的一个开放标准,为面向消息的中间件设计:高级消息队列协议使得遵从该规范的客户端应用和消息中间 ...
- 零基础如何系统学习Java Web
零基础如何系统学习Java Web? 我来给你说一说 你要下决心,我要转行做开发,这样你才能学成. 你要会打字,我公司原来有一个程序员,打字都是两个手一指禅,身为程序员你一指禅怎么写出的代码,半个 ...
- android混合开发,webview的java与js互操作
android原生应用,用webview加载应用中的网页,并且java代码与js代码可以互相操作. 这是混合开发的基石,最基本也最重要的东西,实验代码在这里. 概括说说—— java调js:调用web ...
- TCP/IP协议基础(转)
转自 http://www.chinaunix.net 作者:Bernardus160 发表于:2003-12-03 17:33:15 TCP/IP协议基础 -------------------- ...
- Jvm基础(2)-Java内存模型
Jvm基础(2)-Java内存模型 主内存和工作内存 Java内存模型包括主内存和工作内存两个部分:主内存用来存储线程之间的共享变量:而工作内存中存储每个线程的相关变量. 如下图所示: 需要注意的是: ...
随机推荐
- Hyperledger Fabric 2.x 环境搭建
一.说明 区块链网络的核心是分布式账本,在这个账本中记录了网络中发生的所有交易信息. Hyperledger Fabric是一个是开源的,企业级的,带权限的分布式账本解决方案的平台.Hyperledg ...
- Http、Https简介和Session、token的请求流程
Http Http (超文本输出协议) 是一种分布式.协作式和超媒体信息系统的应用层协议,它通常运行在TCP之上,因特网应用最广泛的便是Http协议,所有www都遵循这个标准.主要用于Web 浏览器与 ...
- 从.net开发做到云原生运维(六)——分布式应用运行时Dapr
1. 前言 上一篇文章我们讲了K8s的一些概念,K8s真的是带来了很多新玩法,就像我们今天这篇文章的主角Dapr一样,Dapr也能在K8s里以云原生的方式运行.当然它也可以和容器一起运行,或者是CLI ...
- centos 查看jdk 安装路径
命令行: which java 输出: /usr/bin/java 再次输入: ls -lr /usr/bin/java 输出: lrwxrwxrwx 1 root root 22 4月 26 17: ...
- [POI2015] MOD 题解
前言 题目链接:洛谷. 题意简述 给定一棵树,求断掉一条边再连上一条边所得的新树直径最小值和最大值,以及相应方案(你可以不进行任何操作,即断掉并连上同一条边). 题目分析 假设我们枚举断掉某一条边,得 ...
- CH07_指针
CH07_指针 指针的基本概念 作用:可以通过指针间接访问内存 描述: 内存编号是从0开始记录的,一般用十六进制数字表示 可以利用指针变量保存地址 指针变量的定义和使用 语法: 数据类型 * 变量名; ...
- 基于donetcore/CAP实现分布式事务一致性
官网:https://cap.dotnetcore.xyz 相关介绍 CAP 是一个EventBus,同时也是一个在微服务或者SOA系统中解决分布式事务问题的一个框架.它有助于创建可扩展,可靠并且易于 ...
- ios滚动列表白屏问题
移动端分页列表,在ios上滚动加载分页时候,使用scrollTop,会引起白屏问题. 看不少文章说是使用了-webkit-overflow-scrolling: touch;引起的硬件加速问题.亲测删 ...
- Qt 设置快捷键
Qt设置快捷键 本文仅供本人知识总结使用,所以内容会比较浅显,不喜勿喷. 文章目录 Qt设置快捷键 一.需要的类 QShortcut 函数: 二.设置快捷键 官方文档原文翻译: 我的理解: 一.需要的 ...
- armbian指令大全
修复缺失内容 使用以下命令修复安装时缺失的内容: sudo apt --fix-broken install 使用 aptitude 安装软件 aptitude 是 Debian 及其衍生系统中功能强 ...