mysql的c++连接池 xPool
来自: http://my.oschina.net/u/1179554/blog/149560
最近待业,闲的蛋疼,发现C++下连mysql连接池这么基础的东西都没用过,身为一名计算机毕业生,深感愧疚,遂生一想法,写个或者找个连接池,以增强本人垃圾一样的专业修养。
没想到写出来第一版就存在内存管理问题导致的coredump,查了半天,发现是调用了空指针的成员函数,彻底晕倒,其间为了调试gdb core中bt出来的libmysqlclient.so的mysql_real_connect调用,我下了mysql的5.5.31源代码来编了若干遍,想编个debug版调试最后也是没有成功。发现问题还是看网上帖子讲segfault11这种问题多半都是空指针搞的,反过头加了些指针的打印语句才意识到低级的错误,后来debug过程中还发现了更低级的逻辑错误,略去不表,还是自己太菜,连个空指针这个白痴的内存问题都不预先防范,搞得一个300+行的东西,弄了三天才算稳定了,大大的罪过,浪费自己和社会的生产力。
过程中想到自己既然写不好,干脆找个好用的,结果发现了个libzdb,支持好些个开源db的连接池,随便下了个破代码测了一下,也没仔细搞,debug中参考了一下它的源码,还算是有点启发。
目前这个源码用valgrind自己测,有74KB左右的内存泄露,明白人能改改的话最好,作为一个菜鸟我只能帮自己到这了,当然这个泄露不会随时间线性的增长,这个我还是测过了的,有人能用到生产环境的话,麻烦给我提点改进意见,72小时的作品,太经不起考验了,目前本机上只能支持最多 152条连接,设成153就会报连接失败,牛逼的前辈们狠狠的批这份代码好了,自己也感觉自己水平太残废了,不知道是mysql设置还是系统设置的问题,总之无论是建议还是问题尽管扔过来吧,作为一个傻逼二货我照单全收。
上代码
去Github搞代码看这里 https://github.com/githcx/xPool
pool.h (1 in 3)
01 | #include <mysql.h> |
02 | #include <stdio.h> |
03 | #include <string.h> |
04 | #include <map> |
05 | #include <vector> |
06 | #include <string> |
07 | #include <iostream> |
08 | #include <pthread.h> |
09 | |
10 | using std::string; |
11 | using std::vector; |
12 | |
13 | class Connection; |
14 | |
15 | class some{ |
16 | public : |
17 | some(){} |
18 | Connection * first; |
19 | bool second; |
20 | }; |
21 | |
22 | class Pool{ |
23 | public : |
24 | static int initPool(string host,string user,string pass,string dbname, int poolSize); |
25 | static void destroyPool(); |
26 | |
27 | static Connection* getConnection(); |
28 | static int releaseConnection(Connection*); |
29 | static void lock(); |
30 | static void unlock(); |
31 | |
32 | static void locke(); |
33 | static void unlocke(); |
34 | |
35 | static void locki(); |
36 | static void unlocki(); |
37 | |
38 | static void lockl(); |
39 | static void unlockl(); |
40 | |
41 | |
42 | private : |
43 | |
44 | static pthread_mutex_t mutex; |
45 | static pthread_mutex_t execmutex; |
46 | static pthread_mutex_t initmutex; |
47 | static pthread_mutex_t logmutex; |
48 | static Pool* pool_; |
49 | |
50 | private : |
51 | some* vec; // need an instance to init |
52 | |
53 | public : |
54 | static string host_ ; |
55 | static string user_ ; |
56 | static string pass_ ; |
57 | static string dbname_; |
58 | static int poolSize_ ; |
59 | |
60 | public : |
61 | |
62 | Pool(string host,string user,string pass,string dbname, int poolSize); |
63 | virtual ~Pool(); |
64 | }; |
65 | |
66 | class QueryResult; |
67 | |
68 | class Connection{ |
69 | public : |
70 | Connection(string host,string user,string pass,string dbname); |
71 | virtual ~Connection(); |
72 | |
73 | QueryResult executeQuery(string statement); |
74 | private : |
75 | // MYSQL代表了一条TCP连接 |
76 | MYSQL* conn; |
77 | }; |
78 | |
79 | class QueryResult{ |
80 | public : |
81 | int getRowCount(){ return string_table.size();} |
82 | int getColumnCount(){ return string_table[0].size();}; |
83 | string getElement( int row, int column){ return string_table[row][column];} |
84 | void addRow( const vector<string>& row){string_table.push_back(row);} |
85 | private : |
86 | vector<vector<string> > string_table; |
87 | }; |
pool.h (2 in 3)
001 | #include "pool.h" |
002 | #include <stdlib.h> |
003 | |
004 | // for DEBUG |
005 | using std::cout; |
006 | using std::endl; |
007 | |
008 | |
009 | // static field init |
010 | string Pool::host_ = "" ; |
011 | string Pool::user_ = "" ; |
012 | string Pool::pass_ = "" ; |
013 | string Pool::dbname_ = "" ; |
014 | int Pool::poolSize_ = 0; |
015 | Pool* Pool::pool_ = NULL; |
016 | pthread_mutex_t Pool::mutex; |
017 | pthread_mutex_t Pool::execmutex; |
018 | pthread_mutex_t Pool::initmutex; |
019 | pthread_mutex_t Pool::logmutex; |
020 | |
021 | void Pool::lock(){ |
022 | pthread_mutex_lock(&mutex); |
023 | } |
024 | |
025 | void Pool::unlock(){ |
026 | pthread_mutex_unlock(&mutex); |
027 | } |
028 | |
029 | void Pool::locke(){ |
030 | pthread_mutex_lock(&execmutex); |
031 | } |
032 | |
033 | void Pool::unlocke(){ |
034 | pthread_mutex_unlock(&execmutex); |
035 | } |
036 | |
037 | void Pool::locki(){ |
038 | pthread_mutex_lock(&initmutex); |
039 | } |
040 | |
041 | void Pool::unlocki(){ |
042 | pthread_mutex_unlock(&initmutex); |
043 | } |
044 | |
045 | void Pool::lockl(){ |
046 | pthread_mutex_lock(&logmutex); |
047 | } |
048 | |
049 | void Pool::unlockl(){ |
050 | pthread_mutex_unlock(&logmutex); |
051 | } |
052 | |
053 | |
054 | |
055 | Pool::Pool(string host,string user,string pass,string dbname, int poolSize){ |
056 | |
057 | vec = new some[Pool::poolSize_]; |
058 | |
059 | |
060 | for ( int i=0; i<poolSize_; i++){ |
061 | Connection* conn = new Connection(Pool::host_,Pool::user_,Pool::pass_,Pool::dbname_); |
062 | //std::cout << conn << std::endl; |
063 | //std::cout << host << " " << user << " " << pass << " " << dbname << " " << poolSize << std::endl; |
064 | if (!conn){ |
065 | cout << "xPool: new Connection Operation failed" << endl; |
066 | exit (-1); |
067 | } |
068 | vec[i].first = conn; |
069 | vec[i].second = false ; |
070 | } |
071 | |
072 | |
073 | } |
074 | |
075 | Pool::~Pool(){ |
076 | for ( int i=0;i<poolSize_;i++){ |
077 | delete vec[i].first; |
078 | } |
079 | delete [] vec; |
080 | //mysql_library_end(); |
081 | } |
082 | |
083 | int Pool::initPool(string host,string user,string pass,string dbname, int poolSize){ |
084 | host_ = host; |
085 | user_ = user; |
086 | pass_ = pass; |
087 | dbname_ = dbname; |
088 | poolSize_ = poolSize; |
089 | |
090 | return 0; |
091 | } |
092 | |
093 | void Pool::destroyPool(){ |
094 | if (pool_){ |
095 | delete pool_; |
096 | pool_ = NULL; |
097 | } |
098 | } |
099 | |
100 | Connection* Pool::getConnection(){ |
101 | |
102 | // init pool, open connections |
103 | Pool::locki(); |
104 | if (pool_ == NULL){ |
105 | pthread_mutex_init(&mutex,0); |
106 | pthread_mutex_init(&execmutex,0); |
107 | |
108 | //mysql_library_init(0,NULL,NULL); |
109 | |
110 | pool_ = new Pool(host_,user_,pass_,dbname_,poolSize_); |
111 | } |
112 | Pool::unlocki(); |
113 | |
114 | //get connection operation |
115 | Connection* ret = NULL; |
116 | while ( true ){ |
117 | Pool::lock(); |
118 | bool flag = false ; |
119 | for ( int i=0;i<poolSize_;i++){ |
120 | if (pool_->vec[i].second == false ){ |
121 | pool_->vec[i].second = true ; |
122 | ret = pool_->vec[i].first; |
123 | flag = true ; |
124 | break ; |
125 | } |
126 | } |
127 | if (flag == true ){ |
128 | Pool::unlock(); |
129 | break ; |
130 | } |
131 | else { |
132 | //cout << "wait" << endl; |
133 | Pool::unlock(); |
134 | usleep(1000); |
135 | continue ; |
136 | } |
137 | } |
138 | return ret; |
139 | } |
140 | |
141 | int Pool::releaseConnection(Connection* conn){ |
142 | lock(); |
143 | for ( int i=0;i<poolSize_;i++){ |
144 | if (pool_->vec[i].first == conn ){ |
145 | pool_->vec[i].second = false ; |
146 | break ; |
147 | } |
148 | } |
149 | unlock(); |
150 | return 1; |
151 | } |
152 | |
153 | |
154 | Connection::Connection(string host,string user,string pass,string dbname){ |
155 | |
156 | static int connectionCount = 0; |
157 | |
158 | //cout << "C#:" << connectionCount++ << endl; |
159 | |
160 | // 初始化连接 |
161 | conn = mysql_init(NULL); |
162 | |
163 | //cout << "conn:" << conn << endl; |
164 | |
165 | // 执行物理的tcp连接动作,完成三次握手 |
166 | if (!mysql_real_connect(conn, "localhost" , "root" , "jiqim1ma" , "hcx" ,0,NULL,0)){ |
167 | printf ( "xPool: Error connecting to database: %s\n" ,mysql_error(conn)); |
168 | exit (-1); |
169 | } |
170 | else { |
171 | } |
172 | } |
173 | |
174 | Connection::~Connection(){ |
175 | |
176 | //mysql_thread_end(); |
177 | |
178 | // 关闭TCP连接,四次挥手 |
179 | mysql_close(conn); |
180 | } |
181 | |
182 | QueryResult Connection::executeQuery(string statement){ |
183 | |
184 | //Pool::locke(); |
185 | |
186 | //cout << "0.start query" << endl; |
187 | |
188 | const char * query = statement.c_str(); |
189 | |
190 | //cout << "1.before mysql_real_query" << endl; |
191 | |
192 | //cout << "strlen=[" << strlen(query) << "]" << endl; |
193 | |
194 | unsigned int len = (unsigned int ) strlen (query); |
195 | |
196 | char q[100]; |
197 | strncpy (q,query,len); |
198 | q[len]=0; |
199 | |
200 | int status = mysql_real_query(conn,q,len); |
201 | |
202 | //cout << "1.after mysql_real_query" << endl; |
203 | |
204 | if (status){ |
205 | printf ( "Error making query: %s\n" , |
206 | mysql_error(conn)); |
207 | } |
208 | else { |
209 | //printf("[%s] made...\n", query); |
210 | } |
211 | |
212 | MYSQL_RES* resultSet; |
213 | |
214 | //cout << "2.before mysql_store_result" << endl; |
215 | |
216 | resultSet = mysql_store_result(conn); |
217 | |
218 | //cout << "2.after mysql_store_result" << endl; |
219 | |
220 | //cout << "3.before mysql_fetch_row" << endl; |
221 | |
222 | QueryResult queryResult; |
223 | while ( true ){ |
224 | |
225 | MYSQL_ROW row; |
226 | if (!(row = mysql_fetch_row(resultSet))){ |
227 | break ; |
228 | } |
229 | |
230 | vector<string> string_row; |
231 | for ( int i=0; i < mysql_num_fields(resultSet); i++){ |
232 | string_row.push_back(row[i]); |
233 | } |
234 | |
235 | queryResult.addRow(string_row); |
236 | } |
237 | |
238 | //cout << "3.after mysql_fetch_row" << endl; |
239 | |
240 | //cout << "4.before mysql_free_result" << endl; |
241 | |
242 | mysql_free_result(resultSet); //free result after you get the result |
243 | |
244 | //cout << "4.after mysql_free_result" << endl; |
245 | |
246 | |
247 | //cout << "0.finish query" << endl; |
248 | |
249 | //Pool::unlocke(); |
250 | |
251 | return queryResult; |
252 | } |
mine.cpp ( 3 in 3 )
01 | #include "pool.h" |
02 | #include <unistd.h> |
03 | #include <stdlib.h> |
04 | using std::cout; |
05 | using std::endl; |
06 | |
07 | void * handler( void * arg){ |
08 | long tid = ( long )arg; |
09 | |
10 | //cout << "tid =[" << tid << "]" << endl; |
11 | Connection* conn = Pool::getConnection(); |
12 | |
13 | if (!conn){ |
14 | cout << "getConnection NULL pointer" << endl; |
15 | exit (-1); |
16 | } |
17 | |
18 | //cout << "Connection.this:" << conn << endl; |
19 | |
20 | const char * query; |
21 | query = "select * from student;" ; |
22 | |
23 | QueryResult queryResult = conn->executeQuery(query); |
24 | |
25 | Pool::releaseConnection(conn); |
26 | |
27 | //for(int i=0;i<queryResult.getRowCount();i++){ |
28 | // for(int j=0;j<queryResult.getColumnCount();j++){ |
29 | // cout << queryResult.getElement(i,j) << " "; |
30 | // } |
31 | // cout << endl; |
32 | //} |
33 | } |
34 | |
35 | |
36 | int main( int argc, char * argv[]){ |
37 | string host = "localhost" ; |
38 | string user = "root" ; |
39 | string pass = "jiqim1ma" ; |
40 | string dbname = "hcx" ; |
41 | int poolSize = 152; |
42 | |
43 | Pool::initPool(host,user,pass,dbname,poolSize); |
44 | |
45 | ///std::cin.get(); |
46 | |
47 | unsigned int count = -1; |
48 | if (argc>1){ |
49 | count = atoi (argv[1]); |
50 | } |
51 | |
52 | for ( int i=0;i < count;i++){ |
53 | |
54 | const int THREAD_COUNT = 250; |
55 | pthread_t threads[THREAD_COUNT]; |
56 | for ( long i=0;i<THREAD_COUNT;i++){ |
57 | pthread_create(&threads[i],NULL,handler,( void *)i); |
58 | //sleep(1); |
59 | } |
60 | |
61 | for ( int i=0;i<THREAD_COUNT;i++){ |
62 | pthread_join(threads[i],0); |
63 | } |
64 | |
65 | cout << "==============================LOOPBACK=================================" << endl; |
66 | } |
67 | |
68 | Pool::destroyPool(); |
69 | |
70 | |
71 | |
72 | return 0; |
73 | } |
Makefile
1 | all: sbpool |
2 | |
3 | sbpool: mine.cpp pool.cpp |
4 | g++ -g -o sbpool mine.cpp pool.cpp -lmysqlclient -lpthread |
5 | |
6 | clean: |
7 | rm -f *.o accessdb sbpool core* |