游戏服务器之内存数据库redis客户端应用(下)

时间:2022-05-05
本文章向大家介绍游戏服务器之内存数据库redis客户端应用(下),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

(3)存储一个角色的基础信息(使用命令set)

存储结构:

key:BASE角色id ,value 角色基础信息

int playerId = player->get_player_base()->m_player_id;  
char tmpBuf[64];  
memset(tmpBuf,0,64);  
sprintf(tmpBuf,"BASE%d",playerId);  
string key(tmpBuf);  
CRWRedisClient redisClient;  
redis::client* tmpRedisClient = redisClient.get_redis_client();  
if(NULL == tmpRedisClient)  
{  
 return ;  
}  
try 
{  
    tmpRedisClient->set(key, value);  
}  
catch (redis::redis_error & e)  
{  
    cerr << "got exception: " << e.what() << endl << "FAIL" << endl;  
 return ;  
}  

4、读取redis的客户端应用

(1)一次获取一个玩家的装备包裹的所有道具(使用命令hgetall)

存储结构:

key : EQUIPMENTBAGplayerId frield: pos value:CBagItem

bool CRWRedisClientOperator::load_player_equipbag_from_redis(CGamePlayer* player)  
{  
 if(NULL == player)  
    {  
 return false;  
    }  
 //通过playerId得到key值 
 int playerId = player->get_player_base()->m_player_id;  
 if(0 == playerId)  
    {  
 return false;  
    }  
 
 char tmpBuf[64];  
    memset(tmpBuf,0,64);  
 //根据键(EQUIPMENTBAG%d",playerId),获取该玩家 所有装备道具的数据到vector<pair<string,string> > strPairVec 
 //使用到接口void hgetall( const string_type & key, string_pair_vector & out ) 
    sprintf(tmpBuf,"EQUIPMENTBAG%d",playerId);  
    string key(tmpBuf);  
 if(false == CRWRedisClientOperator::instance()->is_key_exist_in_redis(key))  
    {  
 return false;  
    }  
    vector<pair<string,string> > strPairVec;  
 //通过key值从redis取数据 
    CRWRedisClient redisClient;  
    redis::client* tmpRedisClient = redisClient.get_redis_client();  
 if(NULL == tmpRedisClient)  
    {  
 return false;  
    }  
 try 
    {  
        tmpRedisClient->hgetall(key,strPairVec);//获取一个玩家的所有装备背包道具 
    }  
 catch (redis::redis_error & e)  
    {  
        cerr << "got exception: " << e.what() << endl << "FAIL" << endl;  
 return false;  
    }  
 //得到背包 
    CPlayerBag* tmpBag = player->get_player_bag();  
 if(NULL == tmpBag)  
    {  
 return false;  
    }  
    vector<pair<string,string> >::iterator iter = strPairVec.begin();  
 for(;iter != strPairVec.end();iter++)  
    {  
        string frield = iter->first;  
 int pos = ACE_OS::atoi(frield.c_str());  
        string value = iter->second;  
 if(value.length() != sizeof(CBagItem))  
        {  
 return false;  
        }  
        CBagItem* equipItem = new CBagItem();  
 if(NULL == equipItem)  
        {  
 return false;  
        }  
        memcpy(equipItem,value.c_str(),value.length());  
 if(!tmpBag->insert_bagitem_equip(pos,equipItem))  
        {  
 return false;  
        }  
    }  
 return true;  
}  

(2)读取角色的基础信息(使用命令get)

存储结构:

key BASE角色id,value 角色基础信息

char tmpBuf[64];  
memset(tmpBuf,0,64);  
sprintf(tmpBuf,"BASE%d",player->get_player_base()->m_player_id);  
string key(tmpBuf);  
 
struct CPlayerBase playerMsg;  
memset(&playerMsg, 0, sizeof(CPlayerBase));  
string getValue;  
CRWRedisClient redisClient;  
redis::client* tmpRedisClient = redisClient.get_redis_client();  
 
if(NULL == tmpRedisClient)  
{  
 return false;  
}  
try 
{  
 if (false == tmpRedisClient->exists(key))  
    {  
        cout <<key <<" not exists !!!" << endl;  
 return false;  
    }  
    getValue = tmpRedisClient->get(key);  
}  
catch (redis::redis_error & e)//对于会抛出异常的接口,需要捕捉异常 
{  
    cerr << "got exception: " << e.what() << endl << "FAIL" << endl;  
 return false;  
}  
 
if(getValue.length() > sizeof(CPlayerBase))//如果比需要的长度要大,则是不合法的 
{  
 return false;  
}  
memcpy(&playerMsg, getValue.c_str(), getValue.length());//直接在redis,copy到player中 
//开始拷贝数据到角色指针的数据里 
player->get_player_base()->m_player_id = playerMsg.m_player_id;  
......  

5、redis客户端池

客户端池的初始化

bool CRedisClientPool::init_redis_client_pool()  
{  
    CRedisServer tmpRedisServer = CConfigManager::instance()->get_srv_config().get_redis_server_conf();  
    string redisIp = tmpRedisServer.get_ip();  
 int port = tmpRedisServer.get_port();  
 for(int i = 0; i < 5; i++)  
    {  
 if(false == init_redis_client(redisIp,port))  
        {  
 return false;  
        }  
    }  
 return true;  
}  

客户端连接初始化

bool CRedisClientPool::init_redis_client(string redisIp, int port)  
{  
 try 
    {  
        redis::client* m_redis_client = new redis::client(redisIp,port,"");  
 if(NULL != m_redis_client)  
        {  
            push_redis_client(m_redis_client);//压到redis客户端池列表 
        }  
    }  
 catch (redis::redis_error & e)  
    {  
        cerr << "got exception: " << e.what() << endl << "FAIL" << endl;  
 return false;  
    }  
 return true;  
}  

添加客户端连接到客户端池

void CRedisClientPool::push_redis_client(redis::client* redisClient)  
{  
 if(NULL == redisClient)  
    {  
 return ;  
    }  
    m_q_mutex.acquire();  
    m_redis_client_list.push_back(redisClient);  
    m_q_mutex.release();  
}  

6、第三方的库接口

(1)redis客户端对象

typedef base_client<default_hasher> client;  
struct default_hasher  
  {  
 inline size_t operator()(const std::string & key, const std::vector<connection_data> & connections)  
    {  
 return boost::hash<std::string>()(key) % connections.size();//每次操作是根据键哈希获取连接列表里的一个,这样每个键可以尽量使用不同的连接(可能是为了某些多线程场景的减少锁竞争) 
    }  
  };  
 
template<typename CONSISTENT_HASHER>  
 class base_client  
  {  
 private:  
 void init(connection_data & con)  
    {  
 char err[ANET_ERR_LEN];  
      con.socket = anetTcpConnect(err, const_cast<char*>(con.host.c_str()), con.port);//使用anet库做网络通信客户端接口 
 if (con.socket == ANET_ERR)  
      {  
        std::ostringstream os;  
        os << err << " (redis://" << con.host << ':' << con.port << ")";  
 throw connection_error( os.str() );  
      }  
      anetTcpNoDelay(NULL, con.socket);  
      select(con.dbindex, con);  
    }  
 
 
 public:  
   ...  
 explicit base_client(const string_type & host = "localhost",  
                    uint16_t port = 6379,const string_type &pwd ="",int_type dbindex = 0)  
    {  
      connection_data con;  
      con.host = host;  
      con.port = port;  
      con.dbindex = dbindex;  
      con.pwd = pwd;  
      init(con);  
      connections_.push_back(con);//初始化连接后放到连接列表里 
    }  

(2)接口函数

获取redis的哈希表的值

void hgetall( const string_type & key, string_pair_vector & out )  
    {  
 int socket = get_socket(key);  
      send_(socket, makecmd("HGETALL") << key);//命令 
      string_vector s;  
      recv_multi_bulk_reply_(socket, s);//获取多个返回的回应消息作为HGETALL 的结果 
 for(size_t i = 0; i < s.size(); i+=2)  
        out.push_back( make_pair(s[i], s[i+1]) );  
    }  

插入到redis的哈希表用到的

void hmset( const string_type & key, const string_pair_vector & field_value_pairs )  
    {  
 int socket = get_socket(key);  
      makecmd m("HMSET");//命令 
      m << key;  
 for(size_t i=0; i < field_value_pairs.size(); i++)  
        m << field_value_pairs[i].first << field_value_pairs[i].second;//把一个vector的键值发送过去设置 
      send_(socket, m);  
      recv_ok_reply_(socket);//接收应答结果 
    }  

get命令

string_type get(const string_type & key)  
    {  
 int socket = get_socket(key);  
      send_(socket, makecmd("GET") << key);  
 return recv_bulk_reply_(socket);  
    }  

读取网络数据

std::string recv_bulk_reply_(int socket)  
    {  
      int_type length = recv_bulk_reply_(socket, REDIS_PREFIX_SINGLE_BULK_REPLY );  
 
 if (length == -1)  
 return missing_value();  
 
      int_type real_length = length + 2;    // CRLF 
 
      std::string data = read_n(socket, real_length);  
 
#ifndef NDEBUG 
 //output_proto_debug(data.substr(0, data.length()-2)); 
#endif 
 
 if (data.empty())  
 throw protocol_error("invalid bulk reply data; empty");  
 
 if (data.length() != static_cast<std::string::size_type>(real_length))  
 throw protocol_error("invalid bulk reply data; data of unexpected length");  
 
      data.erase(data.size() - 2);  
 
 return data;  
    }  

(3)定义的异常

定义的redis异常的基础类

class redis_error : public std::exception  
  {  
 public:  
    redis_error(const std::string & err) : err_(err) {}  
 virtual ~redis_error() throw () {}  
    operator const std::string () const { return err_; }  
 virtual const char* what() const throw ()  
    {  
 return err_.c_str();  
    }  
 
 
 private:  
    std::string err_;  
  };