从hiredis使用出core谈谈redis多线程的使用http://my.oschina.net/jungleliu0923/blog/202948

原文地址:http://my.oschina.net/jungleliu0923/blog/202948


摘要  hireedis多线程出core原因

目录[-]

  • 1、情景描述
  • 1.1 使用场景
  • 1.2 初步实现方案
  • 1.3 结果
  • 2、线下复现
  • 2.1 代码
  • 2.2 编译执行
  • 2.3 原因分析
  • 3. 终极解决方案
  •       在实际工作中,我需要使用redis的客户端去连接redis,于是选择了hiredis客户端(公司强推)。  hiRedis 是 Redis 官方指定的 C 语言客户端开发包,支持 Redis 完整的命令集、管线以及事件驱动编程。

    1、情景描述

    1.1 使用场景

          一个epool模型的服务器不断接受外界请求,这个服务器框架给用户预留一个回调函数(多线程),回调函数为用户自己去实现的业务逻辑,其中redis的使用就需要在这个回调函数内部实现。

    1.2 初步实现方案

          在程序启动的时候,我就初始化redis的连接,获得hiredis句柄。然后把hiredis句柄传入到线程函数里面。让其做相应的业务逻辑。

    1.3 结果

          很不幸,一次请求都没问题,做压力测试,同时开20个线程访问,程序立即出core。

          线上出core如下:

    01 (gdb) bt
    02 #0  0x000000302af2e2ed in raise () from /lib64/tls/libc.so.6
    03 #1  0x000000302af2fa3e in abort () from /lib64/tls/libc.so.6
    04 #2  0x000000302af62db1 in __libc_message () from /lib64/tls/libc.so.6
    05 #3  0x000000302af6888e in _int_free () from /lib64/tls/libc.so.6
    06 #4  0x000000302af6a12d in _int_realloc () from /lib64/tls/libc.so.6
    07 #5  0x000000302af6b39c in realloc () from /lib64/tls/libc.so.6
    08 #6  0x0000000000dc2269 in sdscatlen (s=Variable "s" is not available.
    09 ) at sds.c:97
    10 #7  0x0000000000dc1d40 in __redisAppendCommand (c=0x16fa1d0, cmd=Variable "cmd" is not available.
    11 ) at hiredis.c:1186
    12 #8  0x0000000000dc1d97 in redisvAppendCommand (c=0x16fa1d0, format=Variable "format" is not available.
    13 ) at hiredis.c:1206
    14 #9  0x0000000000dc1eed in redisvCommand (c=0x16fa1d0, format=Variable "format" is not available.
    15 ) at hiredis.c:1267
    16 #10 0x0000000000dc1fb6 in redisCommand (c=Variable "c" is not available.
    17 ) at hiredis.c:1276
    18 #11 0x0000002b1a8e6310 in Default_Handler::get_batch_redis (this=0x1ff41f0, redis_ins=0x175a7d0, dataid=6202, buf_num=12, res_num=6, key_sign=0x2bd67cb3c8,
    19     res_lens=0x2bd5f54208, res_buf=0x2bd5f54398 "") at default_handler.cpp:659
    20 #12 0x0000002b1a9134df in Default_Ms_Handler::get_digest (this=0x1ff41f0) at default_ms_handler.cpp:646
    21 #13 0x000000000092910c in do_proc () at gss_work.cpp:1107
    22 #14 0x000000000091c91f in thread_main () at gss_net.cpp:188
    23 #15 0x0000000000bc10e9 in default_native () at ubserver_app.cpp:283
    24 #16 0x0000000000bbc676 in eppool_consume (pool=0x2230b90, data=0x22188f0) at eppool.cpp:649
    25 #17 0x0000000000bbc4d1 in _eppool_workers (param=0x22188f0) at eppool.cpp:604
    26 #18 0x000000302b80610a in start_thread () from /lib64/tls/libpthread.so.0
    27 #19 0x000000302afc6003 in clone () from /lib64/tls/libc.so.6
    28 #20 0x0000000000000000 in ?? ()
          当时经过多次尝试。把连接放入到了每个线程中。那么就不会出core了。


    2、线下复现

          因为不方便公开公司代码,所以我写一个类似的代码来复现这个case。

    2.1 代码

          代码主要有testredis.cpp和Makefile(自己指定hiredis目录)。用法是 ./redis -n [num] -h [host] -p [port], n为host数目,多个host用"-"进行分割。

    testredis.cpp

    001 /***************************************************************************
    002  *
    003  * Copyright (c) 2014 Baidu.com, Inc. All Rights Reserved
    004  *
    005  **************************************************************************/
    006   
    007   
    008   
    009 /**
    010  * @file redistest.cpp
    011  * @author liujun05([email protected])
    012  * @date 2014/02/25 10:28:44
    013  * @brief
    014  
    015  **/
    016  
    017 #include<unistd.h>
    018 #include <stdio.h>
    019 #include <hiredis.h>
    020 #include <stdlib.h>
    021 #include <string.h>
    022 #include <pthread.h>
    023  
    024  
    025 #ifndef uint32
    026 #define uint32 unsigned int
    027 #endif
    028  
    029 #define MAX_REDIS_SERVER_CNT 10
    030 #define MAX_REDIS_IPS 1024
    031  
    032 typedef struct _redis_conf_t
    033 {
    034     uint32 redis_num;
    035     char redis_ips[MAX_REDIS_IPS];
    036     char redis_ip_array[MAX_REDIS_SERVER_CNT][MAX_REDIS_IPS];
    037     uint32 redis_port;
    038  
    039 } redis_conf;
    040  
    041 typedef struct _redis_data_t
    042 {
    043     uint32 redis_num;
    044     redisContext *rc[MAX_REDIS_SERVER_CNT];
    045 }redis_data;
    046  
    047 redis_conf g_cfg;
    048 redis_data g_data;
    049  
    050 void show_usage()
    051 {
    052     printf("usage: ./redis -n [num] -h [host] -p [port]\n");
    053 }
    054  
    055 /** 解析参数 */
    056 int main_parse_option(int argc, char **argv)
    057 {
    058     int c;
    059     //reset 获取参数的位置,多次调用时这个会出现问题
    060     while ((c = getopt(argc, argv, "h:p:n:")) != -1)
    061     {
    062         switch (c)
    063         {
    064         case 'h':
    065             sprintf(g_cfg.redis_ips, optarg);
    066             break;
    067         case 'p':
    068             g_cfg.redis_port = atoi(optarg);
    069             break;
    070         case 'n':
    071             g_cfg.redis_num = atoi(optarg);
    072             break;
    073         default:
    074             show_usage();
    075             fflush(stdout);
    076             return -1;
    077         }
    078     }
    079     return 0;
    080 }
    081  
    082 void* test_thread1(void* data)
    083 {
    084     redis_data* redis_ins = (redis_data*)data;
    085     redisReply *reply;
    086     for(int i=0; i<redis_ins->redis_num; i++)
    087     {
    088         reply = (redisReply *)redisCommand( redis_ins->rc[i] ,"SET %s %s""foo""hello world");
    089         freeReplyObject(reply);
    090     }
    091 }
    092  
    093 int init_data()
    094 {
    095     g_data.redis_num = 0;
    096     struct timeval timeout = { 1, 500000 }; // 1.5 seconds
    097  
    098     char *ptok = NULL;
    099     char *part = strtok_r(g_cfg.redis_ips, "-", &ptok);
    100     int num = 0;
    101     while(part)
    102     {
    103         strcpy(g_cfg.redis_ip_array[num++], part);
    104         part = strtok_r(NULL, "-", &ptok);
    105     }
    106  
    107     if(num != g_cfg.redis_num || num > MAX_REDIS_SERVER_CNT)
    108     {
    109         printf("ip num[%d] not equal redis_num[%d] or not vaild\n", num, g_cfg.redis_num);
    110     }
    111  
    112     g_data.redis_num = (num > MAX_REDIS_SERVER_CNT ) ? MAX_REDIS_SERVER_CNT : num;
    113     int i= 0;
    114  
    115     for(i=0; i<g_data.redis_num; i++)
    116     {
    117         g_data.rc[i] = redisConnectWithTimeout( g_cfg.redis_ip_array[i], g_cfg.redis_port , timeout);
    118         if( g_data.rc[i] == NULL || g_data.rc[i]->err)
    119         {
    120             printf("content to redis server[%s:%u], error[%s]\n",
    121                     g_cfg.redis_ip_array[i], g_cfg.redis_port, g_data.rc[i]->errstr
    122             );
    123             goto exit;
    124         }
    125     }
    126     return 0;
    127  
    128 exit:
    129    for(int j=0; j<i; j++)
    130    {
    131         if(g_data.rc[j] != NULL)
    132         {
    133             redisFree(g_data.rc[j]);
    134         }
    135    }
    136    return -1;
    137 }
    138  
    139  
    140 int destory_data()
    141 {
    142    for(int j=0; j<g_data.redis_num; j++)
    143    {
    144         if(g_data.rc[j] != NULL)
    145         {
    146             redisFree(g_data.rc[j]);
    147         }
    148    }
    149 }
    150  
    151 int main(int argc, char** argv)
    152 {
    153     g_cfg.redis_ips[0] = '\0';
    154     g_cfg.redis_port = 6379;
    155     g_cfg.redis_num = 0;
    156     if( 0 != main_parse_option(argc, argv) )
    157     {
    158         show_usage();
    159         return -1;
    160     }
    161  
    162     if(  0 == g_cfg.redis_num || g_cfg.redis_num >  MAX_REDIS_SERVER_CNT )
    163     {
    164         printf("the reids num[%u] is not vaild\n", g_cfg.redis_num);
    165         show_usage();
    166         return 0;
    167     }
    168  
    169     int ret = init_data();
    170     if( ret != 0)
    171     {
    172         printf("init num fail\n");
    173         return -1;
    174

    你可能感兴趣的:(从hiredis使用出core谈谈redis多线程的使用http://my.oschina.net/jungleliu0923/blog/202948)