运用

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. 洛谷 P2615 神奇的幻方 题解

    每日一题系列day1 打卡 Analysis 水货模拟,不多说了 #include<iostream> #include<cstdio> #include<cstring ...

  2. Git 相关使用

    https://www.cnblogs.com/mengdd/p/3447464.html 删除本地 & 远程 的分支.   删除本地分支 命令行 : $ git branch -d < ...

  3. mybatis oracle 逆向工程

  4. HDU 6041 I Curse Myself ——(仙人掌图,tarjan,转化)

    题解见这个博客:http://blog.csdn.net/ME495/article/details/76165039. 复杂度不太会算..这个经典问题的解法需要注意,维护队列里面只有k个元素即可.另 ...

  5. Js 之cookie插件(jquery.cookie.js)

    一.代码 (function (factory) { if (typeof define === 'function' && define.amd) { // AMD define([ ...

  6. Java8函数式编程的宏观总结

    1.java8优势通过将行为进行抽象,java8提供了批量处理数据的并行类库,使得代码可以在多核CPU上高效运行. 2.函数式编程的核心使用不可变值和函数,函数对一个值进行处理,映射成另一个值. 3. ...

  7. bash常用的快捷键

    bash常用快捷键 快捷键 作用 CTRL+A 把光标移动到命令行开头.如果我们输入的命令过长,则在想要把光标移动到命令行开头时使用 CTRL+E 光标移动到命令行行尾 CTRL+C 强制中止当前命令 ...

  8. Link static data in sql source control

    You can link data that doesn't change very often to SQL Source Control. This lets you commit data ch ...

  9. android: requestLayout(), invalidate(), postInvalidate() 方法区别

    一.invalidate和postInvalidate 这两个方法都是在重绘当前控件的时候调用的.invalidate在UI线程中调用,postInvalidate在非UI线程中调用.因为androi ...

  10. word中英文双引号的样式区分与替换技巧

    https://jingyan.baidu.com/article/3f16e003147ea42590c10349.html 场景:一篇word文档中,想要全选更改字体,使得中文全部为“宋体”,英文 ...