池化技术,减少了资源创建次数,提高了程序响应性能,特别是在高并发场景下,当程序7*24小时运行,创建资源可能会出现耗时较长和失败等问题,池化技术,主要是程序初始化之前创建多个可用连接,集中管理起来,后续直接使用,使用完并归还。
线程池主要解决问题: 1、解决线程创建和消耗所浪费的资源和时间 2、控制线程数量,减少资源竞争
代码解读
一共由三个接口组成: 1、CreatePthreadPool创建工作线程 初始化线程池结构下的条件变量和互斥锁,创建多个线程通过头插法,插入到线程池结构worker成员中 2、thread_func线程入口函数 进入while循环,第一件事先拿锁,然后再去判断,任务队列中是否有数据,要是没有则进入循环中的条件等待,进入条件等待会先释放锁,所以初始化完成之后所有线程全部都会进入条件等待,然后一旦条件满足则会去拿锁。 3、threadPoolQueue将任务加入到任务链表中 先加锁,然后通过头插法插入到线程池结构中的wait_jobs成员中,通知条件变量,然后解锁 4、threadPoolShutdown回收线程池中线程 将工作线程中的标识位全部置为一,然后通知所有线程,然后等待回收所有线程并释放每个节点
总结
线程池就是一个很典型的生产者消费者模型,不断地向任务队列中投放任务,工作线程不断地去队伍队列中去取任务消费掉。
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#define LL_ADD(item, list) do { \
item->prev = NULL; \
item->next = list; \
list = item; \
} while(0)
#define LL_REMOVE(item,list)do{ \
if(item->prev != NULL) item->prev->next = item->next; \
if(item->next != NULL) item->next->prev = item->prev; \
if(list== item)list= item->next; \
item->prev = item->next = NULL; \
}while(0)
// 工作线程结构体
typedefstruct NWORKER{
pthread_t id;// 线程id
intterminate;// 终止条件
struct NTHREADPOLL *worqueue;
struct NWORKER *prev;// 前向指针
struct NWORKER *next;// 后项指针
}nWorker;
// 任务队列
typedefstruct NJOB{
void(*job_function)(struct NJOB* job);
void* user_data;
struct NJOB *prev;
struct NJOB *next;
}nJob;
typedefstruct NTHREADPOLL{
struct NWORKER *workers;
struct NJOB *wait_jobs;
pthread_cond_t cond;
pthread_mutex_t mtx;
}nThreadpoll;
// 操作线程池函数
#define MAX_PTHREAD 80
#define MAX_JOB 8
// 毁掉函数
voidjob_fun(nJob *job){
if(job ==NULL)return;
int*data =(int*)job->user_data;
printf("user_data:%d-thread_id:%ld,line:%d\n",*data,pthread_self(),__LINE__);
free(job->user_data);
job->user_data =NULL;
free(job);
job =NULL;
return;
}
// 线程入口函数
void*thread_func(void* data){
nWorker *worker =(nWorker*)data;
while(1)
{
pthread_mutex_lock(&(worker->worqueue->mtx));
while(worker->worqueue->wait_jobs ==NULL){
if(worker->terminate)break;
pthread_cond_wait(&(worker->worqueue->cond),&(worker->worqueue->mtx));
}
if(worker->terminate){
pthread_mutex_unlock(&(worker->worqueue->mtx));
break;
}
nJob *job = worker->worqueue->wait_jobs;
if(job !=NULL)
LL_REMOVE(job,worker->worqueue->wait_jobs);
pthread_mutex_unlock(&(worker->worqueue->mtx));
if(job ==NULL)continue;
job->job_function(job);
}
// free(worker);
// worker = NULL;
pthread_exit(0);
}
// 创造线程
intCreatePthreadPool(nThreadpoll* threadpoll,int max_pthread)
{
if(max_pthread <=0) max_pthread = MAX_PTHREAD;
memset(threadpoll,0x00,sizeof(nThreadpoll));
pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;
memcpy(&threadpoll->cond,&blank_cond,sizeof(threadpoll->cond));
pthread_mutex_t blank_mutex = PTHREAD_MUTEX_INITIALIZER;
memcpy(&threadpoll->mtx,&blank_mutex,sizeof(threadpoll->mtx));
int i;
for(i=0; i<max_pthread; i++){
nWorker *worker =(nWorker*)malloc(sizeof(nWorker));
if(worker ==NULL){
perror("threadpoll->workers");
return-1;
}
memset(worker,0x00,sizeof(nWorker));
worker->worqueue = threadpoll;
worker->terminate=0;
int ret = pthread_create(&(worker->id),NULL,thread_func,(void*)worker);
if(ret !=0){
perror("pthread_create");
free(worker);
worker =NULL;
return-1;
}
LL_ADD(worker,worker->worqueue->workers);
}
return0;
}
// 向线程中添加任务,唤醒一个线程去处理
voidthreadPoolQueue(nThreadpoll* threadpool,nJob *job){
pthread_mutex_lock(&(threadpool->mtx));
LL_ADD(job,threadpool->wait_jobs);
pthread_cond_signal(&(threadpool->cond));
pthread_mutex_unlock(&(threadpool->mtx));
return;
}
voidthreadPoolShutdown(nThreadpoll *workerqueue){
nWorker *woker =NULL;
for(woker = workerqueue->workers;woker!=NULL;woker = woker->next){
woker->terminate=1;
}
pthread_cond_broadcast(&workerqueue->cond);
struct NWORKER *tmp = workerqueue->workers;
while(tmp)
{
pthread_join(tmp->id,NULL);
if(tmp){
free(tmp);
tmp =NULL;
}
tmp = tmp->next;
}
pthread_cond_destroy(&workerqueue->cond);
pthread_mutex_destroy(&workerqueue->mtx);
workerqueue->workers =NULL;
workerqueue->wait_jobs =NULL;
}
intmain(){
nThreadpoll workerQueue;
if(CreatePthreadPool(&workerQueue,MAX_PTHREAD)!=0){
perror("CreatePthreadPool faile");
return-1;
}
int i;
for(i=0;i<MAX_JOB;i++){
nJob *job =(nJob*)malloc(sizeof(nJob));
if(job ==NULL){
perror("nJob malloc faile");
exit(-1);
}
job->job_function = job_fun;
job->user_data =(int*)malloc(sizeof(int));
*(int*)job->user_data = i;
threadPoolQueue(&workerQueue,job);
}
getchar();
return0;
}
当我们向数据库或者DNS服务器发送请求时,需要同步等待他的返回结果,这个过程是比较耗时的,所以衍生出异步发起请求,当发出请求之后,不等待请求的响应,而是通过一个线程去监听,主线程去做其他事情,当请求响应之后如何去做其他事情。
同步向DNS服务器发送请求
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <pthread.h>
#define DNS_SVR "114.114.114.114"
#define DNS_HOST 0x01
#define DNS_CNAME 0x05
structdns_header{
unsignedshort id;
unsignedshort flags;
unsignedshort qdcount;
unsignedshort ancount;
unsignedshort nscount;
unsignedshort arcount;
};
structdns_question{
int length;
unsignedshort qtype;
unsignedshort qclass;
char*qname;
};
structdns_item{
char*domain;
char*ip;
};
int dns_create_header(struct dns_header *header) {
if(header ==NULL)return-1;
memset(header,0,sizeof(struct dns_header));
srandom(time(NULL));
header->id =random();
header->flags |=htons(0x0100);
header->qdcount =htons(1);
return0;
}
int dns_create_question(struct dns_question *question, const char *hostname) {
if(question ==NULL)return-1;
memset(question,0,sizeof(struct dns_question));
question->qname =(char*)malloc(strlen(hostname)+2);
if(question->qname ==NULL)return-2;
question->length =strlen(hostname)+2;
question->qtype =htons(1);
question->qclass =htons(1);
constchar delim[2]=".";
char*hostname_dup =strdup(hostname);
char*token =strtok(hostname_dup, delim);
char*qname_p = question->qname;
while(token !=NULL){
size_t len =strlen(token);
*qname_p = len;
qname_p ++;
strncpy(qname_p, token, len+1);
qname_p += len;
token =strtok(NULL, delim);
}
free(hostname_dup);
return0;
}
int dns_build_request(struct dns_header *header, struct dns_question *question, char *request) {
int header_s =sizeof(struct dns_header);
int question_s = question->length +sizeof(question->qtype)+sizeof(question->qclass);
int length = question_s + header_s;
int offset =0;
memcpy(request+offset, header,sizeof(struct dns_header));
offset +=sizeof(struct dns_header);
memcpy(request+offset, question->qname, question->length);
offset += question->length;
memcpy(request+offset,&question->qtype,sizeof(question->qtype));
offset +=sizeof(question->qtype);
memcpy(request+offset,&question->qclass,sizeof(question->qclass));
return length;
}
static int is_pointer(int in) {
return((in &0xC0)==0xC0);
}
static void dns_parse_name(unsigned char *chunk, unsigned char *ptr, char *out, int *len) {
int flag =0, n =0, alen =0;
char*pos = out +(*len);
while(1){
flag =(int)ptr[0];
if(flag ==0)break;
if(is_pointer(flag)){
n =(int)ptr[1];
ptr = chunk + n;
dns_parse_name(chunk, ptr, out, len);
break;
}else{
ptr ++;
memcpy(pos, ptr, flag);
pos += flag;
ptr += flag;
*len += flag;
if((int)ptr[0]!=0){
memcpy(pos,".",1);
pos +=1;
(*len)+=1;
}
}
}
}
static int dns_parse_response(char *buffer, struct dns_item **domains) {
int i =0;
unsignedchar*ptr = buffer;
ptr +=4;
int querys =ntohs(*(unsignedshort*)ptr);
ptr +=2;
int answers =ntohs(*(unsignedshort*)ptr);
ptr +=6;
for(i =0;i < querys;i ++){
while(1){
int flag =(int)ptr[0];
ptr +=(flag +1);
if(flag ==0)break;
}
ptr +=4;
}
char cname[128], aname[128], ip[20], netip[4];
int len, type, ttl, datalen;
int cnt =0;
structdns_item*list=(struct dns_item*)calloc(answers,sizeof(struct dns_item));
if(list==NULL){
return-1;
}
for(i =0;i < answers;i ++){
bzero(aname,sizeof(aname));
len =0;
dns_parse_name(buffer, ptr, aname,&len);
ptr +=2;
type =htons(*(unsignedshort*)ptr);
ptr +=4;
ttl =htons(*(unsignedshort*)ptr);
ptr +=4;
datalen =ntohs(*(unsignedshort*)ptr);
ptr +=2;
if(type == DNS_CNAME){
bzero(cname,sizeof(cname));
len =0;
dns_parse_name(buffer, ptr, cname,&len);
ptr += datalen;
}elseif(type == DNS_HOST){
bzero(ip,sizeof(ip));
if(datalen ==4){
memcpy(netip, ptr, datalen);
inet_ntop(AF_INET , netip , ip ,sizeof(struct sockaddr));
printf("%s has address %s\n", aname, ip);
printf("\tTime to live: %d minutes , %d seconds\n", ttl /60, ttl %60);
list[cnt].domain =(char*)calloc(strlen(aname)+1,1);
memcpy(list[cnt].domain, aname,strlen(aname));
list[cnt].ip =(char*)calloc(strlen(ip)+1,1);
memcpy(list[cnt].ip, ip,strlen(ip));
cnt ++;
}
ptr += datalen;
}
}
*domains =list;
ptr +=2;
return cnt;
}
int dns_client_commit(const char *domain) {
int sockfd =socket(AF_INET, SOCK_DGRAM,0);
if(sockfd <0){
perror("create socket failed\n");
exit(-1);
}
printf("url:%s\n", domain);
structsockaddr_in dest;
bzero(&dest,sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port =htons(53);
dest.sin_addr.s_addr =inet_addr(DNS_SVR);
int ret =connect(sockfd,(struct sockaddr*)&dest,sizeof(dest));
printf("connect :%d\n", ret);
structdns_header header ={0};
dns_create_header(&header);
structdns_question question ={0};
dns_create_question(&question, domain);
char request[1024]={0};
int req_len =dns_build_request(&header,&question, request);
int slen =sendto(sockfd, request, req_len,0,(struct sockaddr*)&dest,sizeof(struct sockaddr));
char buffer[1024]={0};
structsockaddr_in addr;
size_t addr_len =sizeof(struct sockaddr_in);
int n =recvfrom(sockfd, buffer,sizeof(buffer),0,(struct sockaddr*)&addr,(socklen_t*)&addr_len);
printf("recvfrom n : %d\n", n);
structdns_item*domains =NULL;
dns_parse_response(buffer,&domains);
return0;
}
char*domain[]={
// "www.ntytcp.com",
"bojing.wang",
"",
"tieba.baidu.com",
"news.baidu.com",
"zhidao.baidu.com",
"music.baidu.com",
"image.baidu.com",
"v.baidu.com",
"map.baidu.com",
"baijiahao.baidu.com",
"xueshu.baidu.com",
"cloud.baidu.com",
"",
"open.163.com",
"auto.163.com",
"gov.163.com",
"money.163.com",
"sports.163.com",
"tech.163.com",
"edu.163.com",
"www.taobao.com",
"q.taobao.com",
"sf.taobao.com",
"yun.taobao.com",
"baoxian.taobao.com",
"www.tmall.com",
"suning.tmall.com",
"www.tencent.com",
"www.qq.com",
"",
"www.ctrip.com",
"hotels.ctrip.com",
"hotels.ctrip.com",
"vacations.ctrip.com",
"flights.ctrip.com",
"trains.ctrip.com",
"bus.ctrip.com",
"car.ctrip.com",
"piao.ctrip.com",
"tuan.ctrip.com",
"you.ctrip.com",
"g.ctrip.com",
"lipin.ctrip.com",
"ct.ctrip.com"
};
int main(int argc, char *argv[]) {
int count =sizeof(domain)/sizeof(domain[0]);
int i =0;
for(i =0;i < count;i ++){
dns_client_commit(domain[i]);
}
getchar();
}
异步发送请求
1、dns_async_client_init初始化函数 创建epoll、创建线程,用来监控dns的响应消息 2、dns_async_client_commit创建连接,发送请求,将fd加入到epoll中进行监听 3、dns_async_client_proc线程入口函数 通过epoll_wait进行监听,调用取出数据,调用对应的回调函数,epoll_ctl删除节点。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <pthread.h>
#define DNS_SVR "114.114.114.114"
#define DNS_HOST 0x01
#define DNS_CNAME 0x05
#define ASYNC_CLIENT_NUM 1024
structdns_header{
unsignedshort id;
unsignedshort flags;
unsignedshort qdcount;
unsignedshort ancount;
unsignedshort nscount;
unsignedshort arcount;
};
structdns_question{
int length;
unsignedshort qtype;
unsignedshort qclass;
char*qname;
};
structdns_item{
char*domain;
char*ip;
};
typedef void (*async_result_cb)(struct dns_item *list, int count);
structasync_context{
int epfd;
};
structep_arg{
int sockfd;
async_result_cb cb;
};
int dns_create_header(struct dns_header *header) {
if(header ==NULL)return-1;
memset(header,0,sizeof(struct dns_header));
srandom(time(NULL));
header->id =random();
header->flags |=htons(0x0100);
header->qdcount =htons(1);
return0;
}
int dns_create_question(struct dns_question *question, const char *hostname) {
if(question ==NULL)return-1;
memset(question,0,sizeof(struct dns_question));
question->qname =(char*)malloc(strlen(hostname)+2);
if(question->qname ==NULL)return-2;
question->length =strlen(hostname)+2;
question->qtype =htons(1);
question->qclass =htons(1);
constchar delim[2]=".";
char*hostname_dup =strdup(hostname);
char*token =strtok(hostname_dup, delim);
char*qname_p = question->qname;
while(token !=NULL){
size_t len =strlen(token);
*qname_p = len;
qname_p ++;
strncpy(qname_p, token, len+1);
qname_p += len;
token =strtok(NULL, delim);
}
free(hostname_dup);
return0;
}
int dns_build_request(struct dns_header *header, struct dns_question *question, char *request) {
int header_s =sizeof(struct dns_header);
int question_s = question->length +sizeof(question->qtype)+sizeof(question->qclass);
int length = question_s + header_s;
int offset =0;
memcpy(request+offset, header,sizeof(struct dns_header));
offset +=sizeof(struct dns_header);
memcpy(request+offset, question->qname, question->length);
offset += question->length;
memcpy(request+offset,&question->qtype,sizeof(question->qtype));
offset +=sizeof(question->qtype);
memcpy(request+offset,&question->qclass,sizeof(question->qclass));
return length;
}
static int is_pointer(int in) {
return((in &0xC0)==0xC0);
}
static int set_block(int fd, int block) {
int flags =fcntl(fd, F_GETFL,0);
if(flags <0)return flags;
if(block){
flags &=~O_NONBLOCK;
}else{
flags |= O_NONBLOCK;
}
if(fcntl(fd, F_SETFL, flags)<0)return-1;
return0;
}
static void dns_parse_name(unsigned char *chunk, unsigned char *ptr, char *out, int *len) {
int flag =0, n =0, alen =0;
char*pos = out +(*len);
while(1){
flag =(int)ptr[0];
if(flag ==0)break;
if(is_pointer(flag)){
n =(int)ptr[1];
ptr = chunk + n;
dns_parse_name(chunk, ptr, out, len);
break;
}else{
ptr ++;
memcpy(pos, ptr, flag);
pos += flag;
ptr += flag;
*len += flag;
if((int)ptr[0]!=0){
memcpy(pos,".",1);
pos +=1;
(*len)+=1;
}
}
}
}
static int dns_parse_response(char *buffer, struct dns_item **domains) {
int i =0;
unsignedchar*ptr = buffer;
ptr +=4;
int querys =ntohs(*(unsignedshort*)ptr);
ptr +=2;
int answers =ntohs(*(unsignedshort*)ptr);
ptr +=6;
for(i =0;i < querys;i ++){
while(1){
int flag =(int)ptr[0];
ptr +=(flag +1);
if(flag ==0)break;
}
ptr +=4;
}
char cname[128], aname[128], ip[20], netip[4];
int len, type, ttl, datalen;
int cnt =0;
structdns_item*list=(struct dns_item*)calloc(answers,sizeof(struct dns_item));
if(list==NULL){
return-1;
}
for(i =0;i < answers;i ++){
bzero(aname,sizeof(aname));
len =0;
dns_parse_name(buffer, ptr, aname,&len);
ptr +=2;
type =htons(*(unsignedshort*)ptr);
ptr +=4;
ttl =htons(*(unsignedshort*)ptr);
ptr +=4;
datalen =ntohs(*(unsignedshort*)ptr);
ptr +=2;
if(type == DNS_CNAME){
bzero(cname,sizeof(cname));
len =0;
dns_parse_name(buffer, ptr, cname,&len);
ptr += datalen;
}elseif(type == DNS_HOST){
bzero(ip,sizeof(ip));
if(datalen ==4){
memcpy(netip, ptr, datalen);
inet_ntop(AF_INET , netip , ip ,sizeof(struct sockaddr));
printf("%s has address %s\n", aname, ip);
printf("\tTime to live: %d minutes , %d seconds\n", ttl /60, ttl %60);
list[cnt].domain =(char*)calloc(strlen(aname)+1,1);
memcpy(list[cnt].domain, aname,strlen(aname));
list[cnt].ip =(char*)calloc(strlen(ip)+1,1);
memcpy(list[cnt].ip, ip,strlen(ip));
cnt ++;
}
ptr += datalen;
}
}
*domains =list;
ptr +=2;
return cnt;
}
int dns_client_commit(const char *domain) {
int sockfd =socket(AF_INET, SOCK_DGRAM,0);
if(sockfd <0){
perror("create socket failed\n");
exit(-1);
}
printf("url:%s\n", domain);
set_block(sockfd,0);//nonblock
structsockaddr_in dest;
bzero(&dest,sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port =htons(53);
dest.sin_addr.s_addr =inet_addr(DNS_SVR);
int ret =connect(sockfd,(struct sockaddr*)&dest,sizeof(dest));
//printf("connect :%d\n", ret);
structdns_header header ={0};
dns_create_header(&header);
structdns_question question ={0};
dns_create_question(&question, domain);
char request[1024]={0};
int req_len =dns_build_request(&header,&question, request);
int slen =sendto(sockfd, request, req_len,0,(struct sockaddr*)&dest,sizeof(struct sockaddr));
while(1){
char buffer[1024]={0};
structsockaddr_in addr;
size_t addr_len =sizeof(struct sockaddr_in);
int n =recvfrom(sockfd, buffer,sizeof(buffer),0,(struct sockaddr*)&addr,(socklen_t*)&addr_len);
if(n <=0)continue;
printf("recvfrom n : %d\n", n);
structdns_item*domains =NULL;
dns_parse_response(buffer,&domains);
break;
}
return0;
}
void dns_async_client_free_domains(struct dns_item *list, int count) {
int i =0;
for(i =0;i < count;i ++){
free(list[i].domain);
free(list[i].ip);
}
free(list);
}
//dns_async_client_proc()
//epoll_wait
//result callback
static void* dns_async_client_proc(void *arg) {
structasync_context*ctx =(struct async_context*)arg;
int epfd = ctx->epfd;
while(1){
structepoll_event events[ASYNC_CLIENT_NUM]={0};
int nready =epoll_wait(epfd, events, ASYNC_CLIENT_NUM,-1);
if(nready <0){
if(errno == EINTR || errno == EAGAIN){
continue;
}else{
break;
}
}elseif(nready ==0){
continue;
}
printf("nready:%d\n", nready);
int i =0;
for(i =0;i < nready;i ++){
structep_arg*data =(struct ep_arg*)events[i].data.ptr;
int sockfd = data->sockfd;
char buffer[1024]={0};
structsockaddr_in addr;
size_t addr_len =sizeof(struct sockaddr_in);
int n =recvfrom(sockfd, buffer,sizeof(buffer),0,(struct sockaddr*)&addr,(socklen_t*)&addr_len);
structdns_item*domain_list =NULL;
int count =dns_parse_response(buffer,&domain_list);
data->cb(domain_list, count);//call cb
int ret =epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd,NULL);
//printf("epoll_ctl DEL --> sockfd:%d\n", sockfd);
close(sockfd);/////
dns_async_client_free_domains(domain_list, count);
free(data);
}
}
}
//dns_async_client_init()
//epoll init
//thread init
structasync_context*dns_async_client_init(void){
int epfd =epoll_create(1);//
if(epfd <0)returnNULL;
structasync_context*ctx =calloc(1,sizeof(struct async_context));
if(ctx ==NULL){
close(epfd);
returnNULL;
}
ctx->epfd = epfd;
pthread_t thread_id;
int ret =pthread_create(&thread_id,NULL, dns_async_client_proc, ctx);
if(ret){
perror("pthread_create");
returnNULL;
}
usleep(1);//child go first
return ctx;
}
//dns_async_client_commit(ctx, domain)
//socket init
//dns_request
//sendto dns send
int dns_async_client_commit(struct async_context* ctx, const char *domain, async_result_cb cb) {
int sockfd =socket(AF_INET, SOCK_DGRAM,0);
if(sockfd <0){
perror("create socket failed\n");
exit(-1);
}
printf("url:%s\n", domain);
set_block(sockfd,0);//nonblock
structsockaddr_in dest;
bzero(&dest,sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port =htons(53);
dest.sin_addr.s_addr =inet_addr(DNS_SVR);
int ret =connect(sockfd,(struct sockaddr*)&dest,sizeof(dest));
//printf("connect :%d\n", ret);
structdns_header header ={0};
dns_create_header(&header);
structdns_question question ={0};
dns_create_question(&question, domain);
char request[1024]={0};
int req_len =dns_build_request(&header,&question, request);
int slen =sendto(sockfd, request, req_len,0,(struct sockaddr*)&dest,sizeof(struct sockaddr));
structep_arg*eparg =(struct ep_arg*)calloc(1,sizeof(struct ep_arg));
if(eparg ==NULL)return-1;
eparg->sockfd = sockfd;
eparg->cb = cb;
structepoll_event ev;
ev.data.ptr = eparg;
ev.events = EPOLLIN;
ret =epoll_ctl(ctx->epfd, EPOLL_CTL_ADD, sockfd,&ev);
//printf(" epoll_ctl ADD: sockfd->%d, ret:%d\n", sockfd, ret);
return ret;
}
char*domain[]={
"www.ntytcp.com",
"bojing.wang",
"",
"tieba.baidu.com",
"news.baidu.com",
"zhidao.baidu.com",
"music.baidu.com",
"image.baidu.com",
"v.baidu.com",
"map.baidu.com",
"baijiahao.baidu.com",
"xueshu.baidu.com",
"cloud.baidu.com",
"",
"open.163.com",
"auto.163.com",
"gov.163.com",
"money.163.com",
"sports.163.com",
"tech.163.com",
"edu.163.com",
"www.taobao.com",
"q.taobao.com",
"sf.taobao.com",
"yun.taobao.com",
"baoxian.taobao.com",
"www.tmall.com",
"suning.tmall.com",
"www.tencent.com",
"www.qq.com",
"",
"www.ctrip.com",
"hotels.ctrip.com",
"hotels.ctrip.com",
"vacations.ctrip.com",
"flights.ctrip.com",
"trains.ctrip.com",
"bus.ctrip.com",
"car.ctrip.com",
"piao.ctrip.com",
"tuan.ctrip.com",
"you.ctrip.com",
"g.ctrip.com",
"lipin.ctrip.com",
"ct.ctrip.com"
};
static void dns_async_client_result_callback(struct dns_item *list, int count) {
int i =0;
for(i =0;i < count;i ++){
printf("name:%s, ip:%s\n",list[i].domain,list[i].ip);
}
}
int main(int argc, char *argv[]) {
#if 0
dns_client_commit(argv[1]);
#else
structasync_context*ctx =dns_async_client_init();
if(ctx ==NULL)return-2;
int count =sizeof(domain)/sizeof(domain[0]);
int i =0;
for(i =0;i < count;i ++){
dns_async_client_commit(ctx, domain[i], dns_async_client_result_callback);
//sleep(2);
}
getchar();
#endif
}
内存池主要是解决,频繁开辟和释放内存,造成的内存碎片问题,大量的内存碎片,会导致我们分配内存失败。 内存池是通过,申请比较大的内存块,后面慢慢的去使用他,避免重复申请和释放内存带来的效率消耗和大量内存碎片
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#define MP_ALIGNMENT 32
#define MP_PAGE_SIZE 4096
#define MP_MAX_ALLOC_FROM_POOL (MP_PAGE_SIZE-1)
#define mp_align(n, alignment) (((n)+(alignment-1)) & ~(alignment-1))
#define mp_align_ptr(p, alignment) (void *)((((size_t)p)+(alignment-1)) & ~(alignment-1))
structmp_large_s{
structmp_large_s*next;
void*alloc;
};
structmp_node_s{
unsignedchar*last;
unsignedchar*end;
structmp_node_s*next;
size_t failed;
};
structmp_pool_s{
size_t max;
structmp_node_s*current;
structmp_large_s*large;
structmp_node_s head[0];
};
structmp_pool_s*mp_create_pool(size_t size);
void mp_destory_pool(struct mp_pool_s *pool);
void *mp_alloc(struct mp_pool_s *pool, size_t size);
void *mp_nalloc(struct mp_pool_s *pool, size_t size);
void *mp_calloc(struct mp_pool_s *pool, size_t size);
void mp_free(struct mp_pool_s *pool, void *p);
structmp_pool_s*mp_create_pool(size_t size){
structmp_pool_s*p;
int ret =posix_memalign((void**)&p, MP_ALIGNMENT, size +sizeof(struct mp_pool_s)+sizeof(struct mp_node_s));
if(ret){
returnNULL;
}
p->max =(size < MP_MAX_ALLOC_FROM_POOL)? size : MP_MAX_ALLOC_FROM_POOL;
p->current = p->head;
p->large =NULL;
p->head->last =(unsignedchar*)p +sizeof(struct mp_pool_s)+sizeof(struct mp_node_s);
p->head->end = p->head->last + size;
p->head->failed =0;
return p;
}
void mp_destory_pool(struct mp_pool_s *pool) {
structmp_node_s*h,*n;
structmp_large_s*l;
for(l = pool->large; l; l = l->next){
if(l->alloc){
free(l->alloc);
}
}
h = pool->head->next;
while(h){
n = h->next;
free(h);
h = n;
}
free(pool);
}
void mp_reset_pool(struct mp_pool_s *pool) {
structmp_node_s*h;
structmp_large_s*l;
for(l = pool->large; l; l = l->next){
if(l->alloc){
free(l->alloc);
}
}
pool->large =NULL;
for(h = pool->head; h; h = h->next){
h->last =(unsignedchar*)h +sizeof(struct mp_node_s);
}
}
static void *mp_alloc_block(struct mp_pool_s *pool, size_t size) {
unsignedchar*m;
structmp_node_s*h = pool->head;
size_t psize =(size_t)(h->end -(unsignedchar*)h);
int ret =posix_memalign((void**)&m, MP_ALIGNMENT, psize);
if(ret)returnNULL;
structmp_node_s*p,*new_node,*current;
new_node =(struct mp_node_s*)m;
new_node->end = m + psize;
new_node->next =NULL;
new_node->failed =0;
m +=sizeof(struct mp_node_s);
m =mp_align_ptr(m, MP_ALIGNMENT);
new_node->last = m + size;
current = pool->current;
for(p = current; p->next; p = p->next){
if(p->failed++>4){
current = p->next;
}
}
p->next = new_node;
pool->current = current ? current : new_node;
return m;
}
static void *mp_alloc_large(struct mp_pool_s *pool, size_t size) {
void*p =malloc(size);
if(p ==NULL)returnNULL;
size_t n =0;
structmp_large_s*large;
for(large = pool->large; large; large = large->next){
if(large->alloc ==NULL){
large->alloc = p;
return p;
}
if(n ++>3)break;
}
large =mp_alloc(pool,sizeof(struct mp_large_s));
if(large ==NULL){
free(p);
returnNULL;
}
large->alloc = p;
large->next = pool->large;
pool->large = large;
return p;
}
void *mp_memalign(struct mp_pool_s *pool, size_t size, size_t alignment) {
void*p;
int ret =posix_memalign(&p, alignment, size);
if(ret){
returnNULL;
}
structmp_large_s*large =mp_alloc(pool,sizeof(struct mp_large_s));
if(large ==NULL){
free(p);
returnNULL;
}
large->alloc = p;
large->next = pool->large;
pool->large = large;
return p;
}
void *mp_alloc(struct mp_pool_s *pool, size_t size) {
unsignedchar*m;
structmp_node_s*p;
if(size <= pool->max){
p = pool->current;
do{
m =mp_align_ptr(p->last, MP_ALIGNMENT);
if((size_t)(p->end - m)>= size){
p->last = m + size;
return m;
}
p = p->next;
}while(p);
returnmp_alloc_block(pool, size);
}
returnmp_alloc_large(pool, size);
}
void *mp_nalloc(struct mp_pool_s *pool, size_t size) {
unsignedchar*m;
structmp_node_s*p;
if(size <= pool->max){
p = pool->current;
do{
m = p->last;
if((size_t)(p->end - m)>= size){
p->last = m+size;
return m;
}
p = p->next;
}while(p);
returnmp_alloc_block(pool, size);
}
returnmp_alloc_large(pool, size);
}
void *mp_calloc(struct mp_pool_s *pool, size_t size) {
void*p =mp_alloc(pool, size);
if(p){
memset(p,0, size);
}
return p;
}
void mp_free(struct mp_pool_s *pool, void *p) {
structmp_large_s*l;
for(l = pool->large; l; l = l->next){
if(p == l->alloc){
free(l->alloc);
l->alloc =NULL;
return;
}
}
}
int main(int argc, char *argv[]) {
int size =1<<12;
structmp_pool_s*p =mp_create_pool(size);
int i =0;
for(i =0;i <10;i ++){
void*mp =mp_alloc(p,512);
// mp_free(mp);
}
//printf("mp_create_pool: %ld\n", p->max);
printf("mp_align(123, 32): %d, mp_align(17, 32): %d\n",mp_align(24,32),mp_align(17,32));
//printf("mp_align_ptr(p->current, 32): %lx, p->current: %lx, mp_align(p->large, 32): %lx, p->large: %lx\n", mp_align_ptr(p->current, 32), p->current, mp_align_ptr(p->large, 32), p->large);
int j =0;
for(i =0;i <5;i ++){
char*pp =mp_calloc(p,32);
for(j =0;j <32;j ++){
if(pp[j]){
printf("calloc wrong\n");
}
printf("calloc success\n");
}
}
//printf("mp_reset_pool\n");
for(i =0;i <5;i ++){
void*l =mp_alloc(p,8192);
mp_free(p, l);
}
mp_reset_pool(p);
//printf("mp_destory_pool\n");
for(i =0;i <58;i ++){
mp_alloc(p,256);
}
mp_destory_pool(p);
return0;
}
池化技术能够减少资源创建和释放次数,提高程序的响应性能,特别是在高并发下这种提高更加明显。 1、对象创建时间长。 2、对象创建需要大量资源。 3、对象创建后可被重复使用
数据库连接池,是程序启动建立足够多的数据库连接,并将这些连接组成一个连接池,由程序动态地申请和释放
线程池是主动执行任务,连接池被动操作,池对象被任务获取,任务执行完后归还。 连接池和线程池设置数量的关系: 1、一般线程池线程数量和连接池连接对象数量一致; 2、一般线程执行任务完毕的时候归还连接对象;
头文件DBPool.h
CDBConn类主要是操作mysql数据库: 1、Init从连接池类中获取mysql信息,进行数据库连接 2、ExecuteCreate执行sql语句 3、ExecuteDrop执行sql语句 4、ExecuteQuery执行查询语句 CDBPool类,主要是管理队列中的CDBConn类: 1、构造函数中初始化变量 2、Init创建最小连接数量CDBConn对象存放到队列中m_free_list 3、GetDBConn从连接池中获取一个连接 4、RelDBConn归还连接到队列中
#ifndef DBPOOL_H_
#define DBPOOL_H_
#include <iostream>
#include <list>
#include <mutex>
#include <condition_variable>
#include <map>
#include <stdint.h>
#include <mysql.h>
#define MAX_ESCAPE_STRING_LEN 10240
usingnamespacestd;
// 返回结果 select的时候用
class CResultSet {
public:
CResultSet(MYSQL_RES* res);
virtual~CResultSet();
boolNext();
intGetInt(const char* key);
char*GetString(const char* key);
private:
int_GetIndex(constchar* key);
MYSQL_RES* m_res;
MYSQL_ROW m_row;
map<string,int> m_key_map;
};
// 插入数据用
class CPrepareStatement {
public:
CPrepareStatement();
virtual~CPrepareStatement();
boolInit(MYSQL* mysql, string& sql);
voidSetParam(uint32_t index, int& value);
voidSetParam(uint32_t index, uint32_t& value);
voidSetParam(uint32_t index, string& value);
voidSetParam(uint32_t index, const string& value);
boolExecuteUpdate();
uint32_tGetInsertId();
private:
MYSQL_STMT* m_stmt;
MYSQL_BIND* m_param_bind;
uint32_t m_param_cnt;
};
class CDBPool;
class CDBConn {
public:
CDBConn(CDBPool* pDBPool);
virtual~CDBConn();
intInit();
// 创建表
boolExecuteCreate(const char* sql_query);
// 删除表
boolExecuteDrop(const char* sql_query);
// 查询
CResultSet*ExecuteQuery(const char* sql_query);
/**
* 执行DB更新,修改
*
* @param sql_query sql
* @param care_affected_rows 是否在意影响的行数,false:不在意;true:在意
*
* @return 成功返回true 失败返回false
*/
boolExecuteUpdate(const char* sql_query, bool care_affected_rows = true);
uint32_tGetInsertId();
// 开启事务
boolStartTransaction();
// 提交事务
boolCommit();
// 回滚事务
boolRollback();
// 获取连接池名
constchar*GetPoolName();
MYSQL*GetMysql(){return m_mysql;}
private:
CDBPool* m_pDBPool;// to get MySQL server information
MYSQL* m_mysql;// 对应一个连接
char m_escape_string[MAX_ESCAPE_STRING_LEN +1];
};
class CDBPool {// 只是负责管理连接CDBConn,真正干活的是CDBConn
public:
CDBPool(){}
CDBPool(constchar* pool_name,constchar* db_server_ip,uint16_t db_server_port,
constchar* username,constchar* password,constchar* db_name,
int max_conn_cnt);
virtual~CDBPool();
intInit();// 连接数据库,创建连接
CDBConn*GetDBConn(const int timeout_ms = 0);// 获取连接资源
voidRelDBConn(CDBConn* pConn);// 归还连接资源
constchar*GetPoolName(){return m_pool_name.c_str();}
constchar*GetDBServerIP(){return m_db_server_ip.c_str();}
uint16_tGetDBServerPort(){return m_db_server_port;}
constchar*GetUsername(){return m_username.c_str();}
constchar*GetPasswrod(){return m_password.c_str();}
constchar*GetDBName(){return m_db_name.c_str();}
private:
string m_pool_name;// 连接池名称
string m_db_server_ip;// 数据库ip
uint16_t m_db_server_port;// 数据库端口
string m_username;// 用户名
string m_password;// 用户密码
string m_db_name;// db名称
int m_db_cur_conn_cnt;// 当前启用的连接数量
int m_db_max_conn_cnt;// 最大连接数量
list<CDBConn*> m_free_list;// 空闲的连接
list<CDBConn*> m_used_list;// 记录已经被请求的连接
std::mutex m_mutex;
std::condition_variable m_cond_var;
bool m_abort_request =false;
// CThreadNotify m_free_notify; // 信号量
};
#endif /* DBPOOL_H_ */
cpp文件
#include "DBPool.h"
#include <string.h>
#define log_error printf
#define log_warn printf
#define log_info printf
#define MIN_DB_CONN_CNT 1
#define MAX_DB_CONN_FAIL_NUM 10
CResultSet::CResultSet(MYSQL_RES *res)
{
m_res = res;
// map table field key to index in the result array
int num_fields = mysql_num_fields(m_res);
MYSQL_FIELD *fields = mysql_fetch_fields(m_res);
for(int i =0; i < num_fields; i++)
{
// 多行
m_key_map.insert(make_pair(fields[i].name, i));
}
}
CResultSet::~CResultSet()
{
if(m_res)
{
mysql_free_result(m_res);
m_res =NULL;
}
}
boolCResultSet::Next()
{
m_row = mysql_fetch_row(m_res);
if(m_row)
{
returntrue;
}
else
{
returnfalse;
}
}
intCResultSet::_GetIndex(constchar*key)
{
map<string,int>::iterator it = m_key_map.find(key);
if(it == m_key_map.end())
{
return-1;
}
else
{
return it->second;
}
}
intCResultSet::GetInt(const char *key)
{
int idx =_GetIndex(key);
if(idx ==-1)
{
return0;
}
else
{
return atoi(m_row[idx]);// 有索引
}
}
char*CResultSet::GetString(const char *key)
{
int idx =_GetIndex(key);
if(idx ==-1)
{
returnNULL;
}
else
{
return m_row[idx];// 列
}
}
/////////////////////////////////////////
CPrepareStatement::CPrepareStatement()
{
m_stmt =NULL;
m_param_bind =NULL;
m_param_cnt =0;
}
CPrepareStatement::~CPrepareStatement()
{
if(m_stmt)
{
mysql_stmt_close(m_stmt);
m_stmt =NULL;
}
if(m_param_bind)
{
delete[] m_param_bind;
m_param_bind =NULL;
}
}
boolCPrepareStatement::Init(MYSQL *mysql, string &sql)
{
mysql_ping(mysql);// 当mysql连接丢失的时候,使用mysql_ping能够自动重连数据库
//g_master_conn_fail_num ++;
m_stmt = mysql_stmt_init(mysql);
if(!m_stmt)
{
log_error("mysql_stmt_init failed\n");
returnfalse;
}
if(mysql_stmt_prepare(m_stmt, sql.c_str(), sql.size()))
{
log_error("mysql_stmt_prepare failed: %s\n", mysql_stmt_error(m_stmt));
returnfalse;
}
m_param_cnt = mysql_stmt_param_count(m_stmt);
if(m_param_cnt >0)
{
m_param_bind =new MYSQL_BIND[m_param_cnt];
if(!m_param_bind)
{
log_error("new failed\n");
returnfalse;
}
memset(m_param_bind,0,sizeof(MYSQL_BIND)* m_param_cnt);
}
returntrue;
}
voidCPrepareStatement::SetParam(uint32_t index, int &value)
{
if(index >= m_param_cnt)
{
log_error("index too large: %d\n", index);
return;
}
m_param_bind[index].buffer_type = MYSQL_TYPE_LONG;
m_param_bind[index].buffer =&value;
}
voidCPrepareStatement::SetParam(uint32_t index, uint32_t &value)
{
if(index >= m_param_cnt)
{
log_error("index too large: %d\n", index);
return;
}
m_param_bind[index].buffer_type = MYSQL_TYPE_LONG;
m_param_bind[index].buffer =&value;
}
voidCPrepareStatement::SetParam(uint32_t index, string &value)
{
if(index >= m_param_cnt)
{
log_error("index too large: %d\n", index);
return;
}
m_param_bind[index].buffer_type = MYSQL_TYPE_STRING;
m_param_bind[index].buffer =(char*)value.c_str();
m_param_bind[index].buffer_length = value.size();
}
voidCPrepareStatement::SetParam(uint32_t index, const string &value)
{
if(index >= m_param_cnt)
{
log_error("index too large: %d\n", index);
return;
}
m_param_bind[index].buffer_type = MYSQL_TYPE_STRING;
m_param_bind[index].buffer =(char*)value.c_str();
m_param_bind[index].buffer_length = value.size();
}
boolCPrepareStatement::ExecuteUpdate()
{
if(!m_stmt)
{
log_error("no m_stmt\n");
returnfalse;
}
if(mysql_stmt_bind_param(m_stmt, m_param_bind))
{
log_error("mysql_stmt_bind_param failed: %s\n", mysql_stmt_error(m_stmt));
returnfalse;
}
if(mysql_stmt_execute(m_stmt))
{
log_error("mysql_stmt_execute failed: %s\n", mysql_stmt_error(m_stmt));
returnfalse;
}
if(mysql_stmt_affected_rows(m_stmt)==0)
{
log_error("ExecuteUpdate have no effect\n");
returnfalse;
}
returntrue;
}
uint32_tCPrepareStatement::GetInsertId()
{
return mysql_stmt_insert_id(m_stmt);
}
/////////////////////
CDBConn::CDBConn(CDBPool*pPool)
{
m_pDBPool = pPool;
m_mysql =NULL;
}
CDBConn::~CDBConn()
{
if(m_mysql)
{
mysql_close(m_mysql);
}
}
intCDBConn::Init()
{
m_mysql = mysql_init(NULL);// mysql_标准的mysql c client对应的api
if(!m_mysql)
{
log_error("mysql_init failed\n");
return1;
}
my_bool reconnect =true;
mysql_options(m_mysql, MYSQL_OPT_RECONNECT,&reconnect);// 配合mysql_ping实现自动重连
mysql_options(m_mysql, MYSQL_SET_CHARSET_NAME,"utf8mb4");// utf8mb4和utf8区别
// ip 端口 用户名 密码 数据库名
if(!mysql_real_connect(m_mysql, m_pDBPool->GetDBServerIP(), m_pDBPool->GetUsername(), m_pDBPool->GetPasswrod(),
m_pDBPool->GetDBName(), m_pDBPool->GetDBServerPort(),NULL,0))
{
log_error("mysql_real_connect failed: %s\n", mysql_error(m_mysql));
return2;
}
return0;
}
constchar*CDBConn::GetPoolName()
{
return m_pDBPool->GetPoolName();
}
boolCDBConn::ExecuteCreate(const char *sql_query)
{
mysql_ping(m_mysql);
// mysql_real_query 实际就是执行了SQL
if(mysql_real_query(m_mysql, sql_query,strlen(sql_query)))
{
log_error("mysql_real_query failed: %s, sql: start transaction\n", mysql_error(m_mysql));
returnfalse;
}
returntrue;
}
boolCDBConn::ExecuteDrop(const char *sql_query)
{
mysql_ping(m_mysql);// 如果端开了,能够自动重连
if(mysql_real_query(m_mysql, sql_query,strlen(sql_query)))
{
log_error("mysql_real_query failed: %s, sql: start transaction\n", mysql_error(m_mysql));
returnfalse;
}
returntrue;
}
CResultSet*CDBConn::ExecuteQuery(const char *sql_query)
{
mysql_ping(m_mysql);
if(mysql_real_query(m_mysql, sql_query,strlen(sql_query)))
{
log_error("mysql_real_query failed: %s, sql: %s\n", mysql_error(m_mysql), sql_query);
returnNULL;
}
// 返回结果
MYSQL_RES *res = mysql_store_result(m_mysql);// 返回结果
if(!res)
{
log_error("mysql_store_result failed: %s\n", mysql_error(m_mysql));
returnNULL;
}
CResultSet*result_set =newCResultSet(res);// 存储到CResultSet
return result_set;
}
/*
1.执行成功,则返回受影响的行的数目,如果最近一次查询失败的话,函数返回 -1
2.对于delete,将返回实际删除的行数.
3.对于update,如果更新的列值原值和新值一样,如update tables set col1=10 where id=1;
id=1该条记录原值就是10的话,则返回0。
mysql_affected_rows返回的是实际更新的行数,而不是匹配到的行数。
*/
boolCDBConn::ExecuteUpdate(const char *sql_query, bool care_affected_rows)
{
mysql_ping(m_mysql);
if(mysql_real_query(m_mysql, sql_query,strlen(sql_query)))
{
log_error("mysql_real_query failed: %s, sql: %s\n", mysql_error(m_mysql), sql_query);
//g_master_conn_fail_num ++;
returnfalse;
}
if(mysql_affected_rows(m_mysql)>0)
{
returntrue;
}
else
{// 影响的行数为0时
if(care_affected_rows)
{// 如果在意影响的行数时, 返回false, 否则返回true
log_error("mysql_real_query failed: %s, sql: %s\n\n", mysql_error(m_mysql), sql_query);
returnfalse;
}
else
{
log_warn("affected_rows=0, sql: %s\n\n", sql_query);
returntrue;
}
}
}
// bool CDBConn::ExecuteUpdate2(const char *sql_query, bool care_affected_rows)
// {
// again: // 不能这么写
// if (mysql_real_query(m_mysql, sql_query, strlen(sql_query)))
// {
// log_error("mysql_real_query failed: %s, sql: %s\n", mysql_error(m_mysql), sql_query);
// //g_master_conn_fail_num ++;
// return false;
// } else {
// mysql_ping(m_mysql);
// goto again;
// }
// if (mysql_affected_rows(m_mysql) > 0)
// {
// return true;
// }
// else
// { // 影响的行数为0时
// if (care_affected_rows)
// { // 如果在意影响的行数时, 返回false, 否则返回true
// log_error("mysql_real_query failed: %s, sql: %s\n\n", mysql_error(m_mysql), sql_query);
// return false;
// }
// else
// {
// log_warn("affected_rows=0, sql: %s\n\n", sql_query);
// return true;
// }
// }
// }
boolCDBConn::StartTransaction()
{
mysql_ping(m_mysql);
if(mysql_real_query(m_mysql,"start transaction\n",17))
{
log_error("mysql_real_query failed: %s, sql: start transaction\n", mysql_error(m_mysql));
returnfalse;
}
returntrue;
}
boolCDBConn::Rollback()
{
mysql_ping(m_mysql);
if(mysql_real_query(m_mysql,"rollback\n",8))
{
log_error("mysql_real_query failed: %s, sql: rollback\n", mysql_error(m_mysql));
returnfalse;
}
returntrue;
}
boolCDBConn::Commit()
{
mysql_ping(m_mysql);
if(mysql_real_query(m_mysql,"commit\n",6))
{
log_error("mysql_real_query failed: %s, sql: commit\n", mysql_error(m_mysql));
returnfalse;
}
returntrue;
}
uint32_tCDBConn::GetInsertId()
{
return(uint32_t)mysql_insert_id(m_mysql);
}
////////////////
CDBPool::CDBPool(constchar*pool_name,constchar*db_server_ip,uint16_t db_server_port,
constchar*username,constchar*password,constchar*db_name,int max_conn_cnt)
{
m_pool_name = pool_name;
m_db_server_ip = db_server_ip;
m_db_server_port = db_server_port;
m_username = username;
m_password = password;
m_db_name = db_name;
m_db_max_conn_cnt = max_conn_cnt;//
m_db_cur_conn_cnt = MIN_DB_CONN_CNT;// 最小连接数量
}
// 释放连接池
CDBPool::~CDBPool()
{
// lock_guard 构造函数加锁,析构函数解锁
std::lock_guard<std::mutex>lock(m_mutex);
m_abort_request =true;
m_cond_var.notify_all();// 通知所有在等待的
for(list<CDBConn*>::iterator it = m_free_list.begin(); it != m_free_list.end(); it++)
{
CDBConn*pConn =*it;
delete pConn;
}
m_free_list.clear();
}
intCDBPool::Init()
{
// 创建固定最小的连接数量
for(int i =0; i < m_db_cur_conn_cnt; i++)
{
CDBConn*pDBConn =newCDBConn(this);
int ret = pDBConn->Init();
if(ret)
{
delete pDBConn;
return ret;
}
m_free_list.push_back(pDBConn);
}
// log_info("db pool: %s, size: %d\n", m_pool_name.c_str(), (int)m_free_list.size());
return0;
}
/*
*TODO: 增加保护机制,把分配的连接加入另一个队列,这样获取连接时,如果没有空闲连接,
*TODO: 检查已经分配的连接多久没有返回,如果超过一定时间,则自动收回连接,放在用户忘了调用释放连接的接口
* timeout_ms默认为 0死等
* timeout_ms >0 则为等待的时间
*/
int wait_cout =0;
CDBConn*CDBPool::GetDBConn(const int timeout_ms)
{
std::unique_lock<std::mutex>lock(m_mutex);
if(m_abort_request)
{
log_warn("have aboort\n");
returnNULL;
}
if(m_free_list.empty())// 当没有连接可以用时
{
// 第一步先检测 当前连接数量是否达到最大的连接数量
if(m_db_cur_conn_cnt >= m_db_max_conn_cnt)
{
// 如果已经到达了,看看是否需要超时等待
if(timeout_ms <=0)// 死等,直到有连接可以用 或者 连接池要退出
{
log_info("wait ms:%d\n", timeout_ms);
m_cond_var.wait(lock,[this]
{
// log_info("wait:%d, size:%d\n", wait_cout++, m_free_list.size());
// 当前连接数量小于最大连接数量 或者请求释放连接池时退出
return(!m_free_list.empty())| m_abort_request;
});
}else{
// return如果返回 false,继续wait(或者超时), 如果返回true退出wait
// 1.m_free_list不为空
// 2.超时退出
// 3. m_abort_request被置为true,要释放整个连接池
m_cond_var.wait_for(lock,std::chrono::milliseconds(timeout_ms),[this]{
// log_info("wait_for:%d, size:%d\n", wait_cout++, m_free_list.size());
return(!m_free_list.empty())| m_abort_request;
});
// 带超时功能时还要判断是否为空
if(m_free_list.empty())// 如果连接池还是没有空闲则退出
{
returnNULL;
}
}
if(m_abort_request)
{
log_warn("have aboort\n");
returnNULL;
}
}
else// 还没有到最大连接则创建连接
{
CDBConn*pDBConn =newCDBConn(this);//新建连接
int ret = pDBConn->Init();
if(ret)
{
log_error("Init DBConnecton failed\n\n");
delete pDBConn;
returnNULL;
}
else
{
m_free_list.push_back(pDBConn);
m_db_cur_conn_cnt++;
// log_info("new db connection: %s, conn_cnt: %d\n", m_pool_name.c_str(), m_db_cur_conn_cnt);
}
}
}
CDBConn*pConn = m_free_list.front();// 获取连接
m_free_list.pop_front();// STL 吐出连接,从空闲队列删除
// pConn->setCurrentTime(); // 伪代码
m_used_list.push_back(pConn);//
return pConn;
}
voidCDBPool::RelDBConn(CDBConn *pConn)
{
std::lock_guard<std::mutex>lock(m_mutex);
list<CDBConn*>::iterator it = m_free_list.begin();
for(; it != m_free_list.end(); it++)// 避免重复归还
{
if(*it == pConn)
{
break;
}
}
if(it == m_free_list.end())
{
m_used_list.remove(pConn);
m_free_list.push_back(pConn);
m_cond_var.notify_one();// 通知取队列
}else
{
log_error("RelDBConn failed\n");
}
}
// 遍历检测是否超时未归还
// pConn->isTimeout(); // 当前时间 - 被请求的时间
// 强制回收 从m_used_list 放回 m_free_list
对比上面代码,耦合性更强一点,但是比较好理解一点: 1、init创建多个连接对象,并放入到队列中,初始化信号量为连接数的个数 2、GetConnection从连接池中返回一个连接,先信号量--,然后拿锁,推出链接 3、ReleaseConnection返回一个连接,先拿锁,然后归还连接,然后信号量++ 4、GetInstance当前类是一个单例类,用来获取对象
1、锁、条件变量、信号量封装
#ifndef LOCKER_H
#define LOCKER_H
#include <exception>
#include <pthread.h>
#include <semaphore.h>
class sem
{
public:
sem()
{
if(sem_init(&m_sem,0,0)!=0)
{
throwstd::exception();
}
}
sem(int num)
{
if(sem_init(&m_sem,0, num)!=0)
{
throwstd::exception();
}
}
~sem()
{
sem_destroy(&m_sem);
}
boolwait()
{
return sem_wait(&m_sem)==0;
}
boolpost()
{
return sem_post(&m_sem)==0;
}
private:
sem_t m_sem;
};
class locker
{
public:
locker()
{
if(pthread_mutex_init(&m_mutex,NULL)!=0)
{
throwstd::exception();
}
}
~locker()
{
pthread_mutex_destroy(&m_mutex);
}
boollock()
{
return pthread_mutex_lock(&m_mutex)==0;
}
boolunlock()
{
return pthread_mutex_unlock(&m_mutex)==0;
}
pthread_mutex_t*get()
{
return&m_mutex;
}
private:
pthread_mutex_t m_mutex;
};
class cond
{
public:
cond()
{
if(pthread_cond_init(&m_cond,NULL)!=0)
{
//pthread_mutex_destroy(&m_mutex);
throwstd::exception();
}
}
~cond()
{
pthread_cond_destroy(&m_cond);
}
boolwait(pthread_mutex_t *m_mutex)
{
int ret =0;
//pthread_mutex_lock(&m_mutex);
ret = pthread_cond_wait(&m_cond, m_mutex);
//pthread_mutex_unlock(&m_mutex);
return ret ==0;
}
booltimewait(pthread_mutex_t *m_mutex, struct timespec t)
{
int ret =0;
//pthread_mutex_lock(&m_mutex);
ret = pthread_cond_timedwait(&m_cond, m_mutex,&t);
//pthread_mutex_unlock(&m_mutex);
return ret ==0;
}
boolsignal()
{
return pthread_cond_signal(&m_cond)==0;
}
boolbroadcast()
{
return pthread_cond_broadcast(&m_cond)==0;
}
private:
//static pthread_mutex_t m_mutex;
pthread_cond_t m_cond;
};
#endif
2、头文件sql_connection_pool.h
#ifndef _CONNECTION_POOL_
#define _CONNECTION_POOL_
#include <stdio.h>
#include <list>
#include <mysql/mysql.h>
#include <error.h>
#include <string.h>
#include <iostream>
#include <string>
#include "locker.h"
#include "log.h"
usingnamespacestd;
class connection_pool
{
public:
MYSQL *GetConnection();//获取数据库连接
boolReleaseConnection(MYSQL *conn);//释放连接
intGetFreeConn();//获取连接
voidDestroyPool();//销毁所有连接
//单例模式
static connection_pool *GetInstance();
voidinit(string url, string User, string PassWord, string DataBaseName, int Port, int MaxConn, int close_log);
private:
connection_pool();
~connection_pool();
int m_MaxConn;//最大连接数
int m_CurConn;//当前已使用的连接数
int m_FreeConn;//当前空闲的连接数
locker lock;
list<MYSQL *> connList;//连接池
sem reserve;
public:
string m_url;//主机地址
string m_Port;//数据库端口号
string m_User;//登陆数据库用户名
string m_PassWord;//登陆数据库密码
string m_DatabaseName;//使用数据库名
int m_close_log;//日志开关
};
class connectionRAII{
public:
connectionRAII(MYSQL **con, connection_pool *connPool);
~connectionRAII();
private:
MYSQL *conRAII;
connection_pool *poolRAII;
};
#endif
3、cpp文件
#include <mysql/mysql.h>
#include <stdio.h>
#include <string>
#include <string.h>
#include <stdlib.h>
#include <list>
#include <pthread.h>
#include <iostream>
#include "sql_connection_pool.h"
usingnamespacestd;
connection_pool::connection_pool()
{
m_CurConn =0;
m_FreeConn =0;
}
connection_pool *connection_pool::GetInstance()
{
static connection_pool connPool;
return&connPool;
}
//构造初始化
voidconnection_pool::init(string url, string User, string PassWord, string DBName, int Port, int MaxConn, int close_log)
{
m_url = url;
m_Port =Port;
m_User =User;
m_PassWord =PassWord;
m_DatabaseName =DBName;
m_close_log = close_log;
for(int i =0; i <MaxConn; i++)
{
MYSQL *con =NULL;
con = mysql_init(con);
if(con ==NULL)
{
LOG_ERROR("MySQL Error");
exit(1);
}
con = mysql_real_connect(con, url.c_str(),User.c_str(),PassWord.c_str(),DBName.c_str(),Port,NULL,0);
if(con ==NULL)
{
LOG_ERROR("MySQL Error");
exit(1);
}
connList.push_back(con);
++m_FreeConn;
}
reserve = sem(m_FreeConn);
m_MaxConn = m_FreeConn;
}
//当有请求时,从数据库连接池中返回一个可用连接,更新使用和空闲连接数
MYSQL *connection_pool::GetConnection()
{
MYSQL *con =NULL;
if(0== connList.size())
returnNULL;
reserve.wait();
lock.lock();
con = connList.front();
connList.pop_front();
--m_FreeConn;
++m_CurConn;
lock.unlock();
return con;
}
//释放当前使用的连接
boolconnection_pool::ReleaseConnection(MYSQL *con)
{
if(NULL== con)
returnfalse;
lock.lock();
connList.push_back(con);
++m_FreeConn;
--m_CurConn;
lock.unlock();
reserve.post();
returntrue;
}
//销毁数据库连接池
voidconnection_pool::DestroyPool()
{
lock.lock();
if(connList.size()>0)
{
list<MYSQL *>::iterator it;
for(it = connList.begin(); it != connList.end();++it)
{
MYSQL *con =*it;
mysql_close(con);
}
m_CurConn =0;
m_FreeConn =0;
connList.clear();
}
lock.unlock();
}
//当前空闲的连接数
intconnection_pool::GetFreeConn()
{
returnthis->m_FreeConn;
}
connection_pool::~connection_pool()
{
DestroyPool();
}
connectionRAII::connectionRAII(MYSQL **SQL, connection_pool *connPool)
{
*SQL = connPool->GetConnection();
conRAII =*SQL;
poolRAII = connPool;
}
connectionRAII::~connectionRAII(){
poolRAII->ReleaseConnection(conRAII);
}
1、设置启用(当发现连接断开时的)自动重连 my_bool reconnect = true; mysql_options(m_mysql, MYSQL_OPT_RECONNECT, &reconnect); // 配合mysql_ping实现自动重连
2、检测连接是否正常 int STDCALL mysql_ping(MYSQL *mysql); 描述: 检查与服务端的连接是否正常。连接断开时,如果自动重新连接功能未被禁用,则尝试重新连接服务 器。该函数可被客户端用来检测闲置许久以后,与服务端的连接是否关闭,如有需要,则重新连接。 返回值: 连接正常,返回0;如有错误发生,则返回非0值。返回非0值并不意味着服务器本身关闭掉,也有可能 是网络原因导致网络不通。
连接数 = ((核心数 * 2) + 有效磁盘数) 按照这个公式,即是说你的服务器 CPU 是 4核 i7 的,那连接池连接数大小应该为 ((4*2)+1)=9 这里只是一个经验公式。还要和线程池数量以及具体业务结合在一起。 CPU总核数 = 物理CPU个数 * 每颗物理CPU的核数 总逻辑CPU数 = 物理CPU个数 * 每颗物理CPU的核数 * 超线程数
// 查看CPU信息(型号)
cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c
// 查看物理CPU个数
cat /proc/cpuinfo| grep "physical id"| sort| uniq| wc -l
// 查看每个物理CPU中core的个数(即核数)
cat /proc/cpuinfo| grep "cpu cores"| uniq
// 查看逻辑CPU的个数
cat /proc/cpuinfo| grep "processor"| wc -l
原文作者:讳疾忌医-note;如有侵权,请联系管理员删除。