文章目录
  1. 1. server连接池:
  2. 2. client连接池:

server连接池:

我在 第一篇文章socket连接池SocketPool分析(一): 概述 当中说了,服务器端有成熟的one loop per thread模型来提供高并发,这点可以仔细阅读陈硕老师的书《Linux多线程服务端编程》,server端使用socket连接池是没有什么意义的。所以我在这里就不贴代码了,避免占用空间,有兴趣的请访问server连接池的github地址

client连接池:

池里面主要的无非就是选择存储数据的数据结构。
必须有的函数也就这5个:

  • 构造函数:初始化池,将数据全放入池当中(也有的设计把这个独立出一个函数)
  • 析构函数:将池中的数据依照次序释放给操作系统,最后将池清空
  • Dump函数:把池中的所有数据都打印出来
  • GetConnection函数:从池当中获取数据
  • ReleaseConnection函数:释放数据还给池

这里还加上了判断池是否为空的函数。
我的数据库连接池DBPool的设计也和这个是一样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include "SocketPool/client_connection_pool.h"
#include <iostream>
#include <stdlib.h>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/typeof/typeof.hpp>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>

using std::cout;
using std::endl;

ClientPool::ClientPool() {
//这里我使用boost来解析xml和json配置文件,也可以使用rapidxml或者rapidjson
// 从配置文件socket.xml当中读入mysql的ip, 用户, 密码, 数据库名称,
boost::property_tree::ptree pt;
const char* xml_path = "../config/socket.xml";
boost::property_tree::read_xml(xml_path, pt);

//client_map
BOOST_AUTO(childClient, pt.get_child("Config.Client"));
for (BOOST_AUTO(pos, childClient.begin()); pos!= childClient.end(); ++pos) {
BOOST_AUTO(nextchild, pos->second.get_child(""));
for (BOOST_AUTO(nextpos, nextchild.begin()); nextpos!= nextchild.end(); ++nextpos) {
if (nextpos->first == "IP") clientConnectHost_ = nextpos->second.data();
if (nextpos->first == "Port") clientConnectPort_ = boost::lexical_cast<int>(nextpos->second.data());
if (nextpos->first == "max_connections") clientPoolSize_ = boost::lexical_cast<int>(nextpos->second.data());
}
// 构造函数的作用就是根据poolSize的大小来构造多个映射
// 每个映射的连接都是同样的host,port,backlog

for (int i=0; i<clientPoolSize_; ++i) {
SocketObjPtr conn(new SocketObj(clientConnectHost_, clientConnectPort_));
//只有server启动了,client的connect才会成功
if (conn->Connect()) {
char stringPort[10];
snprintf(stringPort, sizeof(stringPort), "%d", clientConnectPort_);
string key = clientConnectHost_ + "###" + stringPort;
client_map.insert(make_pair(key, conn));
} else {
strErrorMessage_ = conn->ErrorMessage();
}
}
}

/**
* 由于json当中使用数组来保存Connection,这里不能通用代码
* 这段注释的代码是读取json配置文件的
const char* json_path = "../config/socket.json";
boost::property_tree::read_json(json_path, pt);

//client_map
BOOST_AUTO(childClient, pt.get_child("Config.Client"));
for (BOOST_AUTO(pos, childClient.begin()); pos!= childClient.end(); ++pos) {
BOOST_FOREACH(boost::property_tree::ptree::value_type &v, pos->second.get_child("")) {
BOOST_AUTO(nextchild, v.second.get_child(""));
for (BOOST_AUTO(nextpos, nextchild.begin()); nextpos!= nextchild.end(); ++nextpos) {
if (nextpos->first == "IP") clientConnectHost_ = nextpos->second.data();
if (nextpos->first == "Port") clientConnectPort_ = boost::lexical_cast<int>(nextpos->second.data());
if (nextpos->first == "max_connections") clientPoolSize_ = boost::lexical_cast<int>(nextpos->second.data());
}
for (int i=0; i<clientPoolSize_; ++i) {
SocketObjPtr conn(new SocketObj(clientConnectHost_, clientConnectPort_));
//只有server启动了,client的connect才会成功
if (conn->Connect()) {
char stringPort[10];
snprintf(stringPort, sizeof(stringPort), "%d", clientConnectPort_);
string key = clientConnectHost_ + "###" + stringPort;
client_map.insert(make_pair(key, conn));
} else {
strErrorMessage_ = conn->ErrorMessage();
}
}
}
}
*/
}

ClientPool::~ClientPool() {
//析构函数做的工作是轮询map,让每个连接都close掉
//再close掉client
for (multimap<string, SocketObjPtr>::iterator sIt = client_map.begin(); sIt != client_map.end(); ++sIt) {
sIt->second->Close();
}
}

/**
* Dump函数,专业debug30年!
*/
void ClientPool::Dump() const {
printf("\n=====ClientPool Dump START ========== \n");
printf("clientConnectHost_=%s ", clientConnectHost_.c_str());
printf("clientConnectPort_=%d ", clientConnectPort_);
printf("clientPoolSize_=%d ", clientPoolSize_);
printf("strErrorMessage_=%s\n ", strErrorMessage_.c_str());
int count = 0;
for (auto it = client_map.begin(); it!=client_map.end(); ++it) {
printf("count==%d ", count);
it->second->Dump();
++count;
}
printf("\n===ClientPool DUMP END ============\n");
}

bool ClientPool::Empty() const {
return client_map.empty();
}

/**
* 从map当中选取一个连接
* host和port是筛选的端口号
*/
SocketObjPtr ClientPool::GetConnection(string host, unsigned port) {
// get connection operation
unique_lock<mutex> lk(resource_mutex);
char stringPort[10];
snprintf(stringPort, sizeof(stringPort), "%d", port);
string key = host + "###" + stringPort;
multimap<string, SocketObjPtr>::iterator sIt = client_map.find(key);
if (sIt != client_map.end()) {
SocketObjPtr ret = sIt->second;
client_map.erase(sIt);
return ret;
}
}

/**
* 释放特定的连接,就是把SocketObjPtr放回到list当中
* 其实严格地来说这个函数名字不应该叫做释放,而是插入,因为除了插入从池当中取出来的连接之外
* 用户还能插入构造的连接,但是这样做没有意义
*/
bool ClientPool::ReleaseConnection(SocketObjPtr conn) {
unique_lock<mutex> lk(resource_mutex);
pair<string, int> peerPair= conn->GetPeer();
char stringPort[10];
snprintf(stringPort, sizeof(stringPort), "%d", peerPair.second);
string key = peerPair.first + "###" + stringPort;
client_map.insert(make_pair(key, conn));
return true;
}

/**
* 构造函数创建poolsize个连接错误时候用来打印错误信息
*/
string ClientPool::ErrorMessage() const {
return strErrorMessage_;
}
文章目录
  1. 1. server连接池:
  2. 2. client连接池: