运用

http://blog.csdn.net/xumaojun/article/details/51558237 中的redis_publisher.hredis_publisher.cpp
redis_subscriber.h redis_subscriber.cpp四个文件,做一个操作类进行测试.
头文件 Policy.h
#pragma once
#include "redis_publisher.h"
#include "redis_subscriber.h"
#include <iostream> using namespace std; class Policy
{
public:
Policy();
~Policy();
void publish();
static void recieve_message(const char *channel_name,const char *message, int len); CRedisPublisher publisher;
CRedisSubscriber subscriber;
};

代码文件 Policy.cpp

#include "Policy.h"  

Policy::Policy()
{
bool ret = publisher.init();
if (!ret) {
printf("Init failed.\n"); } ret = publisher.connect();
if (!ret) {
printf("connect failed."); } //***********************************
CRedisSubscriber::NotifyMessageFn fn =
bind(recieve_message, std::tr1::placeholders::_1,
std::tr1::placeholders::_2, std::tr1::placeholders::_3); bool ret1 = subscriber.init(fn);
if (!ret1) {
printf("Init failed.\n");
} ret1 = subscriber.connect();
if (!ret1) {
printf("Connect failed.\n");
}
subscriber.subscribe("test-channel");
} Policy::~Policy()
{
publisher.disconnect();
publisher.uninit(); subscriber.disconnect();
subscriber.uninit();
} void Policy::publish()
{
cout<<"Policy::publish()...\n"<<endl;
publisher.publish("test-channel", "Test message");
} void Policy::recieve_message(const char *channel_name,
const char *message, int len)
{
printf("Recieve message: channel name: %s message: %s\n",
channel_name, message);
}

测试文件 testPolicy.cpp
#include "Policy.h"
int main(int argc, char *argv[])
{
Policy m_policy;
int i=0;
while(i<8){
m_policy.publish();
sleep(2);
cout<<"main sleep...\n";
i++;
}
return 0;
}

Makefile
EXE=server_main client_main
CC=g++
FLAG=-lhiredis -levent -pthread
OBJ=redis_publisher.o redis_subscriber.o Policy.o testPolicy.o all:$(EXE) $(EXE):$(OBJ)
$(CC) -o testPolicy redis_publisher.o redis_subscriber.o Policy.o testPolicy.o $(FLAG) redis_publisher.o:redis_publisher.h
redis_subscriber.o:redis_subscriber.h Policy.o:Policy.h

这样编译之后,可以自发自收.
本例的用途在于可以简单地在项目中实现异构的数据收发.

附上原作代码:
redis_publisher.h
/*************************************************************************
> File Name: redis_publisher.h
> Author: chenzengba
> Mail: chenzengba@gmail.com
> Created Time: Sat 23 Apr 2016 10:15:09 PM CST
> Description: 封装hiredis,实现消息发布给redis功能
************************************************************************/ #ifndef REDIS_PUBLISHER_H
#define REDIS_PUBLISHER_H #include <stdlib.h>
#include <hiredis/async.h>
#include <hiredis/adapters/libevent.h>
#include <string>
#include <vector>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <boost/tr1/functional.hpp> class CRedisPublisher
{
public:
CRedisPublisher();
~CRedisPublisher(); bool init();
bool uninit();
bool connect();
bool disconnect(); bool publish(const std::string &channel_name,
const std::string &message); private:
// 下面三个回调函数供redis服务调用
// 连接回调
static void connect_callback(const redisAsyncContext *redis_context,
int status); // 断开连接的回调
static void disconnect_callback(const redisAsyncContext *redis_context,
int status); // 执行命令回调
static void command_callback(redisAsyncContext *redis_context,
void *reply, void *privdata); // 事件分发线程函数
static void *event_thread(void *data);
void *event_proc(); private:
// libevent事件对象
event_base *_event_base;
// 事件线程ID
pthread_t _event_thread;
// 事件线程的信号量
sem_t _event_sem;
// hiredis异步对象
redisAsyncContext *_redis_context;
};

redis_publisher.cpp
/*************************************************************************
> File Name: redis_publisher.cpp
> Author: chenzengba
> Mail: chenzengba@gmail.com
> Created Time: Sat 23 Apr 2016 10:15:09 PM CST
> Description:
************************************************************************/ #include <stddef.h>
#include <assert.h>
#include <string.h>
#include "redis_publisher.h" CRedisPublisher::CRedisPublisher():_event_base(0), _event_thread(0),
_redis_context(0)
{
} CRedisPublisher::~CRedisPublisher()
{
} bool CRedisPublisher::init()
{
// initialize the event
_event_base = event_base_new(); // 创建libevent对象
if (NULL == _event_base)
{
printf(": Create redis event failed.\n");
return false;
} memset(&_event_sem, 0, sizeof(_event_sem));
int ret = sem_init(&_event_sem, 0, 0);
if (ret != 0)
{
printf(": Init sem failed.\n");
return false;
} return true;
} bool CRedisPublisher::uninit()
{
_event_base = NULL; sem_destroy(&_event_sem);
return true;
} bool CRedisPublisher::connect()
{
// connect redis
_redis_context = redisAsyncConnect("127.0.0.1", 6379); // 异步连接到redis服务器上,使用默认端口
if (NULL == _redis_context)
{
printf(": Connect redis failed.\n");
return false;
} if (_redis_context->err)
{
printf(": Connect redis error: %d, %s\n",
_redis_context->err, _redis_context->errstr); // 输出错误信息
return false;
} // attach the event
redisLibeventAttach(_redis_context, _event_base); // 将事件绑定到redis context上,使设置给redis的回调跟事件关联 // 创建事件处理线程
int ret = pthread_create(&_event_thread, 0, &CRedisPublisher::event_thread, this);
if (ret != 0)
{
printf(": create event thread failed.\n");
disconnect();
return false;
} // 设置连接回调,当异步调用连接后,服务器处理连接请求结束后调用,通知调用者连接的状态
redisAsyncSetConnectCallback(_redis_context,
&CRedisPublisher::connect_callback); // 设置断开连接回调,当服务器断开连接后,通知调用者连接断开,调用者可以利用这个函数实现重连
redisAsyncSetDisconnectCallback(_redis_context,
&CRedisPublisher::disconnect_callback); // 启动事件线程
sem_post(&_event_sem);
return true;
} bool CRedisPublisher::disconnect()
{
if (_redis_context)
{
redisAsyncDisconnect(_redis_context);
redisAsyncFree(_redis_context);
_redis_context = NULL;
} return true;
} bool CRedisPublisher::publish(const std::string &channel_name,
const std::string &message)
{
int ret = redisAsyncCommand(_redis_context,
&CRedisPublisher::command_callback, this, "PUBLISH %s %s",
channel_name.c_str(), message.c_str());
if (REDIS_ERR == ret)
{
printf("Publish command failed: %d\n", ret);
return false;
} return true;
} void CRedisPublisher::connect_callback(const redisAsyncContext *redis_context,
int status)
{
if (status != REDIS_OK)
{
printf(": Error: %s\n", redis_context->errstr);
}
else
{
printf(": Redis connected!\n");
}
} void CRedisPublisher::disconnect_callback(
const redisAsyncContext *redis_context, int status)
{
if (status != REDIS_OK)
{
// 这里异常退出,可以尝试重连
printf(": Error: %s\n", redis_context->errstr);
}
} // 消息接收回调函数
void CRedisPublisher::command_callback(redisAsyncContext *redis_context,
void *reply, void *privdata)
{
printf("command callback.\n");
// 这里不执行任何操作
} void *CRedisPublisher::event_thread(void *data)
{
if (NULL == data)
{
printf(": Error!\n");
assert(false);
return NULL;
} CRedisPublisher *self_this = reinterpret_cast<CRedisPublisher *>(data);
return self_this->event_proc();
} void *CRedisPublisher::event_proc()
{
sem_wait(&_event_sem); // 开启事件分发,event_base_dispatch会阻塞
event_base_dispatch(_event_base); return NULL;
}


redis_subscriber.h
/*************************************************************************
> File Name: redis_subscriber.h
> Author: chenzengba
> Mail: chenzengba@gmail.com
> Created Time: Sat 23 Apr 2016 10:15:09 PM CST
> Description: 封装hiredis,实现消息订阅redis功能
************************************************************************/ #ifndef REDIS_SUBSCRIBER_H
#define REDIS_SUBSCRIBER_H #include <stdlib.h>
#include <hiredis/async.h>
#include <hiredis/adapters/libevent.h>
#include <string>
#include <vector>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <boost/tr1/functional.hpp> class CRedisSubscriber
{
public:
typedef std::tr1::function<void (const char *, const char *, int)> NotifyMessageFn; // 回调函数对象类型,当接收到消息后调用回调把消息发送出去 CRedisSubscriber();
~CRedisSubscriber(); bool init(const NotifyMessageFn &fn); // 传入回调对象
bool uninit();
bool connect();
bool disconnect(); // 可以多次调用,订阅多个频道
bool subscribe(const std::string &channel_name); private:
// 下面三个回调函数供redis服务调用
// 连接回调
static void connect_callback(const redisAsyncContext *redis_context,
int status); // 断开连接的回调
static void disconnect_callback(const redisAsyncContext *redis_context,
int status); // 执行命令回调
static void command_callback(redisAsyncContext *redis_context,
void *reply, void *privdata); // 事件分发线程函数
static void *event_thread(void *data);
void *event_proc(); private:
// libevent事件对象
event_base *_event_base;
// 事件线程ID
pthread_t _event_thread;
// 事件线程的信号量
sem_t _event_sem;
// hiredis异步对象
redisAsyncContext *_redis_context; // 通知外层的回调函数对象
NotifyMessageFn _notify_message_fn;
};



redis_subscriber.cpp
/*************************************************************************
> File Name: redis_subscriber.cpp
> Author: chenzengba
> Mail: chenzengba@gmail.com
> Created Time: Sat 23 Apr 2016 10:15:09 PM CST
> Description:
************************************************************************/ #include <stddef.h>
#include <assert.h>
#include <string.h>
#include "redis_subscriber.h" CRedisSubscriber::CRedisSubscriber():_event_base(0), _event_thread(0),
_redis_context(0)
{
} CRedisSubscriber::~CRedisSubscriber()
{
} bool CRedisSubscriber::init(const NotifyMessageFn &fn)
{
// initialize the event
_notify_message_fn = fn;
_event_base = event_base_new(); // 创建libevent对象
if (NULL == _event_base)
{
printf(": Create redis event failed.\n");
return false;
} memset(&_event_sem, 0, sizeof(_event_sem));
int ret = sem_init(&_event_sem, 0, 0);
if (ret != 0)
{
printf(": Init sem failed.\n");
return false;
} return true;
} bool CRedisSubscriber::uninit()
{
_event_base = NULL; sem_destroy(&_event_sem);
return true;
} bool CRedisSubscriber::connect()
{
// connect redis
_redis_context = redisAsyncConnect("127.0.0.1", 6379); // 异步连接到redis服务器上,使用默认端口
if (NULL == _redis_context)
{
printf(": Connect redis failed.\n");
return false;
} if (_redis_context->err)
{
printf(": Connect redis error: %d, %s\n",
_redis_context->err, _redis_context->errstr); // 输出错误信息
return false;
} // attach the event
redisLibeventAttach(_redis_context, _event_base); // 将事件绑定到redis context上,使设置给redis的回调跟事件关联 // 创建事件处理线程
int ret = pthread_create(&_event_thread, 0, &CRedisSubscriber::event_thread, this);
if (ret != 0)
{
printf(": create event thread failed.\n");
disconnect();
return false;
} // 设置连接回调,当异步调用连接后,服务器处理连接请求结束后调用,通知调用者连接的状态
redisAsyncSetConnectCallback(_redis_context,
&CRedisSubscriber::connect_callback); // 设置断开连接回调,当服务器断开连接后,通知调用者连接断开,调用者可以利用这个函数实现重连
redisAsyncSetDisconnectCallback(_redis_context,
&CRedisSubscriber::disconnect_callback); // 启动事件线程
sem_post(&_event_sem);
return true;
} bool CRedisSubscriber::disconnect()
{
if (_redis_context)
{
redisAsyncDisconnect(_redis_context);
redisAsyncFree(_redis_context);
_redis_context = NULL;
} return true;
} bool CRedisSubscriber::subscribe(const std::string &channel_name)
{
int ret = redisAsyncCommand(_redis_context,
&CRedisSubscriber::command_callback, this, "SUBSCRIBE %s",
channel_name.c_str());
if (REDIS_ERR == ret)
{
printf("Subscribe command failed: %d\n", ret);
return false;
} printf(": Subscribe success: %s\n", channel_name.c_str());
return true;
} void CRedisSubscriber::connect_callback(const redisAsyncContext *redis_context,
int status)
{
if (status != REDIS_OK)
{
printf(": Error: %s\n", redis_context->errstr);
}
else
{
printf(": Redis connected!");
}
} void CRedisSubscriber::disconnect_callback(
const redisAsyncContext *redis_context, int status)
{
if (status != REDIS_OK)
{
// 这里异常退出,可以尝试重连
printf(": Error: %s\n", redis_context->errstr);
}
} // 消息接收回调函数
void CRedisSubscriber::command_callback(redisAsyncContext *redis_context,
void *reply, void *privdata)
{
if (NULL == reply || NULL == privdata) {
return ;
} // 静态函数中,要使用类的成员变量,把当前的this指针传进来,用this指针间接访问
CRedisSubscriber *self_this = reinterpret_cast<CRedisSubscriber *>(privdata);
redisReply *redis_reply = reinterpret_cast<redisReply *>(reply); // 订阅接收到的消息是一个带三元素的数组
if (redis_reply->type == REDIS_REPLY_ARRAY &&
redis_reply->elements == 3)
{
printf(": Recieve message:%s:%d:%s:%d:%s:%d\n",
redis_reply->element[0]->str, redis_reply->element[0]->len,
redis_reply->element[1]->str, redis_reply->element[1]->len,
redis_reply->element[2]->str, redis_reply->element[2]->len); // 调用函数对象把消息通知给外层
self_this->_notify_message_fn(redis_reply->element[1]->str,
redis_reply->element[2]->str, redis_reply->element[2]->len);
}
} void *CRedisSubscriber::event_thread(void *data)
{
if (NULL == data)
{
printf(": Error!\n");
assert(false);
return NULL;
} CRedisSubscriber *self_this = reinterpret_cast<CRedisSubscriber *>(data);
return self_this->event_proc();
} void *CRedisSubscriber::event_proc()
{
sem_wait(&_event_sem); // 开启事件分发,event_base_dispatch会阻塞
event_base_dispatch(_event_base); return NULL;
}


【数据库开发】C++测试redis中的publish/subscribe的更多相关文章

  1. php redis pub/sub(Publish/Subscribe,发布/订阅的信息系统)之基本使用

    一.场景介绍 最近的一个项目需要用到发布/订阅的信息系统,以做到最新实时消息的通知.经查找后发现了redis pub/sub(发布/订阅的信息系统)可以满足我的开发需求,而且学习成本和使用成本也比较低 ...

  2. 【数据库开发】学习Redis从这里开始

    转载:http://www.epubit.com.cn/article/200 学习Redis从这里开始 本文主要内容 Redis与其他软件的相同之处和不同之处 Redis的用法 使用Python示例 ...

  3. python开发-实现redis中的发布订阅功能

    Python3学习(二十七):python实现Redis的订阅与发布(sub-pub机制) 先介绍一下redis的pub/sub功能: Pub/Sub功能(means Publish, Subscri ...

  4. 【Spring】Spring的数据库开发 - 2、Spring JdbcTemplate的常用方法(execute、update、query)

    Spring JdbcTemplate的常用方法 文章目录 Spring JdbcTemplate的常用方法 execute() update() query() 简单记录-Java EE企业级应用开 ...

  5. 【数据库开发】Redis key-value内存数据库介绍

    Redis是一个开源的,先进的 key-value 存储可用于构建高性能,可扩展的 Web 应用程序的解决方案.Redis官方网网站是:http://www.redis.io/,如下: Redis 有 ...

  6. Python开发【十一章】:数据库操作Memcache、Redis

    一.Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的 ...

  7. SQL Server中使用数据库快照的方式来完成测试环境中数据库的轻量级备份还原操作

    在开发或者测试环境的数据库中,经常会发现有开发或者测试人员误删除表或者数据的情况,对于开发或者测试库,一般都没有安排定时的备份任务去备份数据库,一方面是由于存储资源有限,不太可能给开发或者测试环境准备 ...

  8. Java微信公众号开发----定时获取access_token并保存到redis中

    本人原本是想做微信公众号菜单的创建修改删除等操作的,但是发现需要access_token,通过阅读文档,发现文档要求有以下几点: 1.access_token 获取后有效期是2小时 2.access_ ...

  9. 【Redis 向Redis中批量导入mysql中的数据(亲自测试)】

    转自:https://blog.csdn.net/kenianni/article/details/84910638 有改动,仅供个人学习 问题提出:缓存的冷启动问题 应用系统新版本上线,这时候 re ...

随机推荐

  1. post提交主订单数据(gateway)实现httpapi

    models.proto syntax = "proto3"; package services; import "google/protobuf/timestamp.p ...

  2. MongoDB 如何保证 oplog 顺序?

    MongoDB 复制集里,主备节点间通过 oplog 来同步数据,Priamry 上写入数据时,会记录一条oplog,Secondary 从 Primary 节点拉取 oplog并重放,以保证最终存储 ...

  3. P1608 路径统计

    题目描述 “RP餐厅”的员工素质就是不一般,在齐刷刷的算出同一个电话号码之后,就准备让HZH,TZY去送快餐了,他们将自己居住的城市画了一张地图,已知在他们的地图上,有N个地方,而且他们目前处在标注为 ...

  4. 【转】harbor仓库高可用集群部署说明

    之前介绍Harbor私有仓库的安装和使用,这里重点说下Harbor高可用集群方案的部署,目前主要有两种主流的Harbor高可用集群方案:1)双主复制:2)多harbor实例共享后端存储. 一.Harb ...

  5. jmeter+ant+jenkins构建自动化测试

    背景目的: 持续更新.... 参考文档:https://blog.csdn.net/cherish0123/article/details/79339732

  6. 30行左右代码实现一个类似httprunner的接口框架

    框架的最终归宿往往是领域语言+模板解析. 首先先约定一种所要执行操作的表述格式.然后通过模板解析将描述语言转化为代码进行执行.例如,我们可以使用以下yaml文件描述多个步骤并且需要关联的接口: api ...

  7. [bzoj 3534][Sdoi2014] 重建

    传送门 Description  T国有N个城市,用若干双向道路连接.一对城市之间至多存在一条道路. 在一次洪水之后,一些道路受损无法通行.虽然已经有人开始调查道路的损毁情况,但直到现在几乎没有消息传 ...

  8. MySQL表连接

    有3种: JOIN 按照功能大致分为如下三类: CROSS JOIN (交叉连接) INNER JOIN (内连接或等值连接). OUTER JOIN (外连接) 交叉连接CROSS JOIN 交叉连 ...

  9. ros资料记录,详细阅读

    ROS源码分析--子话题-catkin:https://blog.csdn.net/sukha/article/details/52460492 ROS源码分析:https://blog.csdn.n ...

  10. GO make&new区别

    自:http://www.cnblogs.com/ghj1976/archive/2013/02/12/2910384.html 1.make用于内建类型(map.slice 和channel)的内存 ...