- 描述:为了知道在线用户数,计算客户端奔溃率(方便我们知道当前客户端的稳定性),在客户端加入统计功能模块。
- 声明:本人是第一次接触Redis,若有错误之处,还望各位大佬指教!
安装Redis以及编译hiredis.lib
- 安装Redis很简单的,去这下载之后可安装:点击这里
- 编译hiredis.lib:由于项目编译环境比较老,是VS2010,可在这里下载进行编译:Redis2.6,提取密码: wahn
- 个人心得:如果低版本的vs不要去折腾高版本的Redis,折腾不出什么结果;
思想(初学小白,多多指点)
- 在客户端加入统计功能,会使用到set和hash。
- 当天的用户创建以“2018-04-04”为例的key值,value是用户id。利用集合set的命令”sadd 2018-04-04 userid”,该key的生命周期设置为86400s。那么问什么会用到set呢,一天之后还要让它消逝?就是为了辅助判断,当前用户是否为当天第一次登录,不重复统计。因为一天之后,数据统计完整,这个key无需存在。
- 然后就是根据set辅助判断的返回值的真假再采用hash的命令hincrby key filed value该格式进行统计。我在这采用的方法是 以“2018-04”为例的key值,“04”为例的filed值,value呢 ?肯定就是1(因为当判断登录用户时第一次登录 就加一)。为什么要这样设计,因为前段展示曲线图的时候是按月为最小单位展示的,这样设计,只对redis访问一次就可以拿到数据。(ps:估计还有所欠缺,暂时没考虑到)
RedisServerDB.h代码:
#pragma once
#include <stdio.h>
#include <iostream>
#include "hiredis.h"
#define NO_QFORKIMPL //这一行必须加才能正常使用
#include <string>
#include <vector>
#define strcasecmp _stricmp
using namespace std;
#pragma comment(linker,"/NODEFAULTLIB:LIBCMT.lib")
#pragma comment (lib,"hiredis.lib")
/*添加socket链接库*/
#pragma comment(lib,"Ws2_32.lib")
//函数声明
redisContext* OnConnectRedisServer();
bool OnSetRedisAccountValue(string strKeyCurrDate, string strValueAccount);
bool OnCheckKey2ValueIsExist(string strKeyCurrDate, string strValueAccount);
bool OnCheckKeyIsExist(string strKeyCurrDate);
bool OnHincrbyRedisKeyValue(string strKeyCurrDate,string strValueAccount, string strHincrbyUserKeyDate,string strHincrbyUserKeyDay);
bool setKeyCurrDateExpire(string strKeyCurrDate, int expire, redisContext* context);
bool OnHincrbyAccountLength(string strCurrKeyMonth, string strKeyCurrDate);
CString StrongString2Cstring(string strData);
RedisServerDB.cpp代码:
#include "stdafx.h"
#include "RedisServerDB.h"
#define KEYEXPIRELEKEACCOUNT 86400 //一天
redisContext* OnConnectRedisServer()
{
//redis默认监听端口为6379 可以再配置文件中修改
redisContext* rConnect = redisConnect("127.0.0.1", 6379);
if (rConnect->err)
{
redisFree(rConnect);
XLogError(L"连接Redis服务器失败");
//exit(0);
return NULL;
}
XLogInfo(L"连接Redis服务器成功");
return rConnect;
}
//写入数据
bool OnSetRedisAccountValue(string strKeyCurrDate, string strValueAccount)
{
CString cstrKeyCurrDate, cstrValueAccount;
cstrKeyCurrDate = StrongString2Cstring(strKeyCurrDate);
cstrValueAccount = StrongString2Cstring(strValueAccount);
redisContext* rConnectRServer = OnConnectRedisServer();
if (rConnectRServer == NULL)
{
return false;
}
string command = "sadd " + strKeyCurrDate + " " + strValueAccount;
redisReply* rReply = (redisReply*)redisCommand(rConnectRServer, command.c_str());
CString cstrCommand = StrongString2Cstring(command);
if (NULL == rReply)
{
XLogError(L"在[%s]写入数据[%s]失败(Command返回的应答对象指针为空){ Command:%s }", cstrKeyCurrDate, cstrValueAccount, cstrCommand);
redisFree(rConnectRServer);
return false;
}
if (!(rReply->type == REDIS_REPLY_INTEGER && rReply->integer == 1))
{
XLogError(L"在[%s]写入数据[%s]失败{ Command:%s }", cstrKeyCurrDate, cstrValueAccount, cstrCommand);
freeReplyObject(rReply);
redisFree(rConnectRServer);
return false;
}
freeReplyObject(rReply);
redisFree(rConnectRServer);
XLogInfo(L"在[%s]写入数据[%s]成功{ Command:%s }", cstrKeyCurrDate, cstrValueAccount, cstrCommand);
return true;
}
//检查key是否存在于redis-server
bool OnCheckKeyIsExist(string strKeyCurrDate)
{
CString cstrKeyCurrDate;
cstrKeyCurrDate = StrongString2Cstring(strKeyCurrDate);
redisContext* rConnectRServer = OnConnectRedisServer();
if (rConnectRServer == NULL)
{
return false;
XLogError(L"OnCheckKeyIsExist()链接redis-server failed");
}
string cmdkeyisexist = "exists " + strKeyCurrDate;
redisReply* rReply = (redisReply*)redisCommand(rConnectRServer, cmdkeyisexist.c_str());
CString cstrCmdkeyisexist = StrongString2Cstring(cmdkeyisexist);
if (rReply == NULL)
{
XLogError(L"函数OnCheckKeyIsExist(),Command返回应答对象指针为空{Command:%s}", cstrCmdkeyisexist);
redisFree(rConnectRServer);
return false;
}
if (rReply->integer == 1)
{
freeReplyObject(rReply);
redisFree(rConnectRServer);
XLogInfo(L"[%s]存在 { Command:%s }", cstrKeyCurrDate, cstrCmdkeyisexist);
return true;
}
freeReplyObject(rReply);
redisFree(rConnectRServer);
XLogInfo(L"[%s]不存在 { Command:%s }", cstrKeyCurrDate, cstrCmdkeyisexist);
return false;
}
//检查当前key对应value是否存在(辅助判断当天是否登录过)
bool OnCheckKey2ValueIsExist(string strKeyCurrDate, string strValueAccount)
{
CString cstrKeyCurrDate, cstrValueAccount;
cstrKeyCurrDate = StrongString2Cstring(strKeyCurrDate);
cstrValueAccount = StrongString2Cstring(strValueAccount);
cstrValueAccount = StrongString2Cstring(strValueAccount);
redisContext* rConnectRServer = OnConnectRedisServer();
if (rConnectRServer == NULL)
{
return false;
}
string command = "sismember " + strKeyCurrDate + " " + strValueAccount;
redisReply* rReply = (redisReply*)redisCommand(rConnectRServer, command.c_str());
CString cstrCommand = StrongString2Cstring(command);
//查找不成功的情况:
if (rReply == NULL)
{
XLogError(L"Redis查询[%s]存在于集合[%s]中?Command返回应答对象指针为空{Command:%s}",cstrValueAccount, cstrKeyCurrDate, cstrCommand);
redisFree(rConnectRServer);
return false;
}
if (rReply->integer==1)
{
freeReplyObject(rReply);
redisFree(rConnectRServer);
XLogInfo(L"[%s]存在于集合[%s]中{ Command:%s }", cstrValueAccount, cstrKeyCurrDate, cstrCommand);
return true;
}
freeReplyObject(rReply);
redisFree(rConnectRServer);
XLogInfo(L"[%s]不存在于集合[%s]中{ Command:%s }", cstrValueAccount, cstrKeyCurrDate, cstrCommand);
return false;
}
bool OnHincrbyRedisKeyValue(string strKeyCurrDate, string strValueAccount ,string strHincrbyUserKeyDate, string strHincrbyUserKeyDay)
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 1), &wsaData);
redisContext* rConnectRServer = OnConnectRedisServer();
if (rConnectRServer == NULL)
{
return false;
}
if (!OnCheckKeyIsExist(strKeyCurrDate))
{
//设置周期
if (!SetKeyCurrDateExpire(strKeyCurrDate, KEYEXPIRELEKEACCOUNT, rConnectRServer))
{
redisFree(rConnectRServer);
return false;
}
}
bool bCheckValueIsExist = OnCheckKey2ValueIsExist(strKeyCurrDate, strValueAccount);
//若存在则不对redis进行set(sadd)
if (!bCheckValueIsExist)
{
//辅助检查当天是否为第一次登录
OnSetRedisAccountValue(strKeyCurrDate, strValueAccount);
//统计在线用户
OnHincrbyAccountLength(strHincrbyUserKeyDate, strHincrbyUserKeyDay);
}
return true;
}
//采用hincrby进行统计用户数
bool OnHincrbyAccountLength(string strHincrbyUserKeyDate,string strHincrbyUserKeyDay)
{
redisContext* rConnectRServer = OnConnectRedisServer();
if (rConnectRServer == NULL)
{
return false;
}
string command = "hincrby " + strHincrbyUserKeyDate + " " + strHincrbyUserKeyDay +" 1";
redisReply* rReply = (redisReply*)redisCommand(rConnectRServer, command.c_str());
CString cstrCommand = StrongString2Cstring(command);
if (NULL == rReply)
{
XLogError(L"Hincrby写入数据失败(Command返回的应答对象指针为空){ Command:%s }",cstrCommand);
redisFree(rConnectRServer);
return false;
}
if (rReply->type == REDIS_REPLY_ERROR)
{
XLogError(L"Hincrby写入数据失败{ Command:%s }", cstrCommand);
freeReplyObject(rReply);
redisFree(rConnectRServer);
return false;
}
freeReplyObject(rReply);
redisFree(rConnectRServer);
XLogInfo(L"Hincrby写入数据成功{ Command:%s }", cstrCommand);
return true;
}
//设置key的生命周期
bool SetKeyCurrDateExpire(string strKeyCurrDate, int expire, redisContext* context)
{
redisContext* rConnectRServer = context;
if (rConnectRServer == nullptr)
{
rConnectRServer = OnConnectRedisServer();
}
if (rConnectRServer == NULL)
{
return false;
}
char buf[10] = "";
itoa(expire, buf, 10);
string command = "expire " + strKeyCurrDate + " " + buf;
redisReply* rReply = (redisReply*)redisCommand(rConnectRServer, command.c_str());
if (rReply->type = REDIS_REPLY_INTEGER && rReply->integer == 1)
{
return true;
}
CString cstrKeyCurrDate = StrongString2Cstring(strKeyCurrDate);
CString cstrCommand = StrongString2Cstring(command);
XLogError(L"设置[%s]的生命周期[%d]失败{ Command:%s }", cstrKeyCurrDate, expire , cstrCommand);
return false;
}
//string转换为cstring
CString StrongString2Cstring(string strData)
{
#ifdef _UNICODE
//如果是unicode工程
USES_CONVERSION;
CString cstrData(strData.c_str());
return cstrData;
#else
//如果是多字节工程
//string 转 CString
CString cstrData;
cstrData.Format("%s", strData.c_str());
return cstrData;
#endif // _UNICODE
}