C++用户信息管理服务3.0 针对1.0的更新 附主要源码

问题

近日更新了C++用户信息管理服务3.0 修复了之前的一系列问题等等

0427更新 问题和解决方案简单陈述
    1、connect等public字段应该修改为private字段 并且改成写相关链接函数和析构函数来实现相关链接和断连接操作
    int32_t EstablishConnection()
    {
        struct timeval timeout = { 1, 500000 }; // 1.5 seconds
        
        //---------------链接mysql------------
        
        l_mysql_connect = mysql_init(NULL);  
        
        if(l_mysql_connect==NULL)
        {
            std::cout<<"初始化环境失败 "<<std::endl;
            return 406;//
        }

        l_mysql_connect=mysql_real_connect(l_mysql_connect, "192.168.100.160", "root", "200101", 
                                    "exam2", 3306, NULL, 0);
        if(l_mysql_connect == NULL)
        {
            std::cout<<"连接数据库服务器失败 "<<std::endl;
            return 407;//
        }
        //---------------链接mysql------------

        //---------------链接redis------------
        l_redis_connect = redisConnectWithTimeout("127.0.0.1", 6379, timeout);
        if (l_redis_connect == NULL || l_redis_connect->err) 
        {
            if (l_redis_connect) 
            {
            std::cout<<"connect error:"<<l_redis_connect->errstr<<std::endl;
            redisFree(l_redis_connect);	//释放redisConnect()所产生的连接。
            } 
            else 
            {
            std::cout<<"connect error: can't allocate redis context "<<std::endl;
            }
            return 301;
        }
    //---------------链接redis------------

    //---------------链接kafka------------

    char l_errstr[512]={};      // API错误报告缓冲区
    char l_buf[512]={};         // 消息值临时缓冲区

    l_conf=rd_kafka_conf_new();//创建kafka 客户端配置

    //配置各项参数 其中kafka port默认为9022号端口
    if(rd_kafka_conf_set(l_conf,"bootstrap.servers", l_brokers, l_errstr,sizeof(l_errstr)) != RD_KAFKA_CONF_OK) 
    {//若返回结果不等于这个enum类型 错误结果打印
        fprintf(stderr, "%s\n", l_errstr);
        rd_kafka_conf_destroy(l_conf);
        //
        return 502;
    }

    //设置发送回调函数 用以反馈信息发送的成败
    rd_kafka_conf_set_dr_msg_cb(l_conf, dr_msg_cb); //设置发送回调函数 一旦生产的每条消息被rd_kafka_produce函数接收,投递报告回调函数会被立即调用

    //初始化一个顶层对象 的基础容器 用于全局配置和共享状态
    //conf在rd_kafka_new调之后不能被再次使用·
    l_rk=rd_kafka_new(RD_KAFKA_PRODUCER, l_conf, l_errstr, sizeof(l_errstr));

    if(!l_rk)
    {
        fprintf(stderr, "%% Failed to create new producer: %s\n",l_errstr);
        rd_kafka_conf_destroy(l_conf);//
        return 503;
    }

    //如果分配成功 临时配置对象会在rd_kafka_new中被释放 此时无需再rd_kafka_conf_destroy
    l_conf=NULL;
    //---------------链接kafka------------


        return 0;
    }
    2、判断数量是不合适的,要用判断必须有的字段是否存在来替代 可以设置一个数组 每个下标代表一个字段是否存在
    memset(l_array_check,0,sizeof(l_array_check));//初始化这个数组 以判定这个redis是否满足这个条件
    ...
    if(strcmp(l_strs_temp.c_str(),l_str_row[j].c_str())==0)
    {
        l_flag_field=j;
        l_array_check[j]++;
        break;
    }
    ...
    for(int i=1;i<l_redis_size/2;i++)
    {
        if(l_array_check[i]==0)
        {
        _return.error_flag=-305;
        std::cout<<"有必要字段不存在..."<<" "<<i<<std::endl;
        }
    }
    3、调用mysql_free_result函数后,MYSQL_ROW指针 不能使用 因此将free后置
    4、字符串大小 100->300
    5、不需要的代码清理掉例如root2 count 信号量 l_buf
    6、pro2con的变量命名不符合规范
    7、client 先判断是否有错误,没有错误再使用数据
    if(l_msg_temp.error_flag==-404)
    {
    printf("用户id不存在,请重新键入数字...\n");
    return 1;
    }
    else if(l_msg_temp.usr.seqId>0)//这个id既合法也存在
    {
        
    }
    else 
    {
        printf("用户id输入不合法,请重新键入数字...\n");
        return 1;
    }
    8、变量命名 大小写
    9、client这部分校验代码是否可复用
    int l_change_num=0;//修改的数目
    int l_check_parameters=0;//输入的参数是否正确

    //name
    if(user.name.c_str()!=NULL)
    {
        if(user.name.size()<=20)
        {
            l_change_num++;
        }
        else 
        {
            printf("name输入太长,请重新输入!\n");
            l_check_parameters= 1;
        }
    }
    ...
    if(l_check_parameters)
    {

        std::cout<<"输入的参数错误...!"<<std::endl;
        return 1;
    }
    if(l_change_num<=0)
    {
        std::cout<<"修改字段数量小于1...!"<<std::endl;
        return 1;
    }
    10、client找到匹配的数据后循环是否没必要继续
    if(l_flag_field==0)
    {
        for(int j=1;j<=l_redis_size/2;j++)
        {
        if(strcmp(l_strs_temp.c_str(),l_str_row[j].c_str())==0)
        {
            l_flag_field=j;
            l_array_check[j]++;
            break;
        }
        }
    }
    11、更新数据应该是需要更新什么字段就带什么字段,不需要更新的字段就不设置
    主要代码:

    server的<upudata>中:

    int l_change_num=0;//修改的数目
    int l_check_parameters=0;//输入的参数是否正确

    //name
    if(!user.name.empty())
    {
      if(user.name.size()<=20)
      {
        l_change_num++;
      }
      else 
      {
        std::cout<<"\n传递过来的message数据name字段非法...\n"<<std::endl;
        l_check_parameters= -4;
      }
    }
    //sex
    if(user.sex!=-1)
    {
      if(user.sex==0||user.sex==1)
      {
        l_change_num++;
      }
      else 
      {
        std::cout<<"\n传递过来的message数据sex字段非法...\n"<<std::endl;
        l_check_parameters= -4;
      }
    }
    pthread_mutex_lock(&l_updata_mutex);//再加一个updata lock防止出现 有两个客户端同时更新一个用户的数据 如果这里只用mysql锁 那么下一行QueryUser会死锁
    //----得到一个待修改前完整的user 将未修改的字段也填上 后面存储过程updata无法更新单个或部分字段
    QueryUser(l_temp_query_msg,seqId);

    if(!user.name.empty())l_temp_query_msg.usr.name=user.name;
    if(user.sex!=-1)l_temp_query_msg.usr.sex=user.sex;//222222222
    if(user.age!=-1)l_temp_query_msg.usr.age=user.age;
    if(!user.tel.empty())l_temp_query_msg.usr.tel=user.tel;
    if(!user.email.empty())l_temp_query_msg.usr.email=user.email;
    if(!user.pers_desc.empty())l_temp_query_msg.usr.pers_desc=user.pers_desc;


    client<main>中
    l_msg.usr.sex=(std::stoi(argv[i+1])==-1?std::stoi(argv[i+1])-1:std::stoi(argv[i+1]));//若用户就输入-1 则-2

    12、如果没有必要,不要使用earliest,正常情况应当从group创建的时刻开始读取数据
    if(rd_kafka_conf_set(l_conf, "auto.offset.reset", "latest", l_errstr,
                              sizeof(l_errstr)) != RD_KAFKA_CONF_OK) {
                fprintf(stderr, "%s\n", l_errstr);
                rd_kafka_conf_destroy(l_conf);//
                return 4;
        }
    13、使用json要做好异常处理
    std::cout<<"新得到的数据如下"<<std::endl;
    if(root["ID"]!=Json::nullValue&&root["ID"].isInt())
    {
        std::cout<<"ID:"<<root["ID"]<<std::endl;
    }
    if(root["name"]!=Json::nullValue&&root["name"].isString())
    {
        std::cout<<"name:"<<root["name"]<<std::endl;
    }
    if(root["sex"]!=Json::nullValue&&root["sex"].isInt())
    {
        std::cout<<"sex:"<<root["sex"]<<std::endl;
    }
    if(root["age"]!=Json::nullValue&&root["age"].isInt())
    {
        std::cout<<"age:"<<root["age"]<<std::endl;
    }
    if(root["tel"]!=Json::nullValue&&root["tel"].isString())
    {
        std::cout<<"tel:"<<root["tel"]<<std::endl;
    }
    if(root["email"]!=Json::nullValue&&root["email"].isString())
    {
        std::cout<<"email:"<<root["email"]<<std::endl;
    }
    if(root["pers_desc"]!=Json::nullValue&&root["pers_desc"].isString())
    {
        std::cout<<"pers_desc:"<<root["pers_desc"]<<std::endl;
    }
    14、内存泄漏consumer中的内存泄漏
    if(reader.parse((const char *)l_rkm->payload,root)==0)
    {   
        std::cout<<"reader.parse赋值错误!"<<std::endl;
        rd_kafka_message_destroy(l_rkm);
        continue;
    }
    15、能在循环外定义的变量就定义循环外
    
    rd_kafka_message_t *l_rkm;
    Json::Reader l_reader(Json::Features::strictMode());
    Json::Value l_root; 
    while (1)


    
0424更新 问题和解决方案简单陈述
    1、表和存储过程的名称问题  
        test->t_test
        queryuser->pr_queryuser
        insertuser->pr_insertuser
        updatauser->pr_updatauser

    2、Select选取数量问题不要用*  
        queryuser:
        create procedure pr_queryuser (in in_id int)
        label_a:begin
        SELECT id,name,sex,age,tel,email,pers_desc from t_test where id=in_id limit 1;
    end;
    3、updata的存储过程要判断是否该成功  
        int l_affected_rows=mysql_affected_rows(l_mysql_connect);
        if(l_affected_rows!=1)
        {
            std::cout<<"修改错误 本应修改的行数为1,实际值为"<<l_affected_rows<<std::endl;
            return 401;
        }
    4、message传递信息要嵌套 再getuser里面 

        struct message  
        {  
        1:User1 usr;
        2:i32 error_flag;
        }  
    5、变量作用域的问题 不要使用全局变量  
        private:
        const char *hostname = "127.0.0.1";
        const int l_redis_size=12;
        const int l_redis_port=6379;
        pthread_mutex_t l_redis_mutex=PTHREAD_MUTEX_INITIALIZER;//static init mutex
        pthread_mutex_t l_mysql_mutex=PTHREAD_MUTEX_INITIALIZER;//static init mutex
        std::string l_str_row[7]={"ID","name","sex","age","tel","email","pers_desc"};
        
        public:
        redisContext *l_redis_connect=NULL;
        MYSQL* l_mysql_connect=NULL;
        rd_kafka_t *l_rk=NULL;        // 生产者实例句柄
        rd_kafka_conf_t *l_conf=NULL; // 临时配置对象
    6、redis可以存储null mysql 存储改为不用非得有默认值  以个人介绍为例子
        create table t_test(id int primary key AUTO_INCREMENT,name varchar(20) not null DEFAULT 'unknow',sex int not null DEFAULT -1, age int not null DEFAULT -1,  tel varchar(20) not null DEFAULT 'unknow', email varchar(30) not null DEFAULT 'unknow', pers_desc varchar(150) )
        if(strncmp(user.pers_desc.c_str(),"NULL",4))
        l_reply = (redisReply*)redisCommand(l_redis_connect,"hmset user%d name %s sex %d age %d tel %s email %s pers_desc %s",user.seqId,user.name.c_str(),user.sex,user.age,user.tel.c_str(),user.email.c_str(),user.pers_desc.c_str());
        else 
        l_reply = (redisReply*)redisCommand(l_redis_connect,"hmset user%d name %s sex %d age %d tel %s email %s",user.seqId,user.name.c_str(),user.sex,user.age,user.tel.c_str(),user.email.c_str());
        从redis提取数据给User时 防止有pers_desc为空的情况 _return.usr.pers_desc="NULL";//先赋值一个null
    7、redis输出顺序  不保证顺序 乱序处理情况
        //根据当前出现的redis字符串 设置l_flag_fileld 找到下一个element应该赋给的自变量
        l_flag_fileld=0;
        if(l_flag_fileld==0)
        {
            for(int j=1;j<=l_redis_size/2;j++)/
            {
                if(strcmp(l_strs_temp.c_str(),l_str_row[j].c_str())==0)//与各种字段title进行匹配
                {
                    l_flag_fileld=j;
                }
            }
        }
        else 
        {
            switch (l_flag_fileld)
            {
            case 1:
                _return.usr.name=l_strs_temp;
                break;
            case 2:
                _return.usr.sex=l_strs_temp[0]-'0';
                break;
            case 3:
                _return.usr.age=std::stoi(l_strs_temp.c_str());
            break;
            case 4:
                _return.usr.tel=l_strs_temp;
            break;
            case 5:
                _return.usr.email=l_strs_temp;
            break;
            case 6:
                _return.usr.pers_desc=l_strs_temp;
            break;
            default:
                break;
            }
            l_flag_fileld=0;
        }
    8、unlock解锁漏了
        if(l_result==NULL)
        {
        printf("mysql_store_result() 失败了 \n");
        //mysql_free_result(l_result);
        _return.error_flag=-402;
        pthread_mutex_unlock(&l_mysql_mutex);//解锁
        return ;
        }
    9、精简mysql中的清除result代码 代码复用
        
        MYSQL_ROW row=NULL;
        int l_num=0;
        int l_result_flag=0;
        if(l_result==NULL)
        {
            l_result_flag=1;
        }
        if(l_result!=NULL)
        {
            row = mysql_fetch_row(l_result);
            l_num=mysql_num_fields(l_result);
        }
        mysql_free_result(l_result);
        while (!mysql_next_result(l_mysql_connect)) {
            l_result = mysql_store_result(l_mysql_connect);
            mysql_free_result(l_result);
        }
        pthread_mutex_unlock(&l_mysql_mutex);
        if(l_result_flag)return -402;
        
    10、mysql注入错误字符的情况  写一个check函数 改正字符串可能错误情况 例如将 ‘ -> ‘‘ 再插入数据库
        std::string CheckStr(std::string str)
        {
            //std::string l_key[11] = { "%","/","union","|","&","^","#","/*","*/","'"," "};

            std::string l_str_tmp="'";
            int l_pos=str.find(l_str_tmp);
            for ( int i=0;i<str.size();i++)
            {
                if(str[i]=='\'')
                {
                str.insert(i,l_str_tmp);
                i++;
                }
            }
            std::cout<<str<<std::endl;
            return str;
        }
    11、_return直接赋值 而不是赋值给别的自变量 再赋值给_return 
        _return.error_flag=-302;
        return ;
    12、错误输出的细化 输出更多的内容更详细的内容
        if(user.name.size()>20)
        {
        std::cout<<"\n传递过来的message数据name字段非法...\n"<<std::endl;
        return -4;
        }
        else if(user.sex>1||user.sex<0)
        {
        std::cout<<"\n传递过来的message数据sex字段非法...\n"<<std::endl;
        return -4;
        }
        else if(user.age>150||user.age<0)
        {
        std::cout<<"\n传递过来的message数据age字段非法...\n"<<std::endl;
        return -4;
        }
        else if(user.tel.size()>20)
        {
        std::cout<<"\n传递过来的message数据tel字段非法...\n"<<std::endl;
        return -4;
        }
        else if(user.email.size()>30)
        {
        std::cout<<"\n传递过来的message数据email字段非法...\n"<<std::endl;
        return -4;
        }
        else if(user.pers_desc.size()>100)
        {
        std::cout<<"\n传递过来的message数据pers_desc字段非法...\n"<<std::endl;
        return -4;
        }
    13、kafka连接问题 将其带到server中 对实例l_rk上锁
        char l_errstr[512]={};      // API错误报告缓冲区
        char l_buf[512]={};         // 消息值临时缓冲区

        handler->l_conf=rd_kafka_conf_new();//创建kafka 客户端配置

        //配置各项参数 其中kafka port默认为9022号端口
        if(rd_kafka_conf_set(handler->l_conf,"bootstrap.servers", handler->l_brokers, l_errstr,sizeof(l_errstr)) != RD_KAFKA_CONF_OK) 
        {//若返回结果不等于这个enum类型 错误结果打印
            fprintf(stderr, "%s\n", l_errstr);
            rd_kafka_conf_destroy(handler->l_conf);
            //
            return 502;
        }

        //设置发送回调函数 用以反馈信息发送的成败
        rd_kafka_conf_set_dr_msg_cb(handler->l_conf, handler->dr_msg_cb); //设置发送回调函数 一旦生产的每条消息被rd_kafka_produce函数接收,投递报告回调函数会被立即调用

        //初始化一个顶层对象 的基础容器 用于全局配置和共享状态
        //conf在rd_kafka_new调之后不能被再次使用·
        handler->l_rk=rd_kafka_new(RD_KAFKA_PRODUCER, handler->l_conf, l_errstr, sizeof(l_errstr));

        if(!handler->l_rk)
        {
            fprintf(stderr, "%% Failed to create new producer: %s\n",l_errstr);
            rd_kafka_conf_destroy(handler->l_conf);//
            return 503;
        }

        //如果分配成功 临时配置对象会在rd_kafka_new中被释放 此时无需再rd_kafka_conf_destroy
        handler->l_conf=NULL;
    14、重写client,使其改成main的命令行输入 类似于svn 
        
        fprintf(stderr,"%% Usage: ""%s Find <ID> \n",argv[0]);
        fprintf(stderr,"%% Usage: ""%s Insert <name> <sex> <age> <tel> <email> (<pers_desc>)..\n",argv[0]);
        fprintf(stderr,"%% Usage: ""%s Updata <ID> <field1> <content> <field2> <content>... )\n",argv[0]);
    15、输出形式统一 不要c/c++混用
        目前暂时统一了服务器的输出格式为cout
    



0422更新:
    1将服务端中的CheckID和QueryUser融合为一个函数 其中返回为message
    2将客户端中的CheckID函数删除 检查ID操作移交给服务器Query全权负责
    3将服务端中的Mysql连接和Redis连接部分放在main函数中 并对连接上锁
    只有需要对连接使用时 开始对连接上锁 使用完毕立即将连接下锁 
    4对存储过程的修改和简化
        queryuser:
        create procedure pr_queryuser (in in_id int)
        label_a:begin
        SELECT id,name,sex,age,tel,email,pers_desc from t_test where id=in_id limit 1;
        end;

            call queryuser (1);



        insertuser:
        create procedure pr_insertuser (in in_name varchar(20),in in_sex bool,in in_age int,in in_tel varchar(20),in in_email varchar(30),in in_pers_desc varchar(150))
        label_a:begin
        insert into t_test (name,sex,age,tel,email,pers_desc) values (in_name,in_sex,in_age,in_tel,in_email,in_pers_desc);
        SELECT LAST_INSERT_ID();
        end;

        
        updatauser:
        create procedure pr_updatauser (in in_id int,in in_name varchar(20),in in_sex bool,in in_age int,in in_tel varchar(20),in in_email varchar(30),in in_pers_desc varchar(150))
        begin
        update t_test set name=in_name,sex=in_sex,age=in_age,tel=in_tel,email=in_email,pers_desc=in_pers_desc where id=in_id;
        end;

    5存储结构更改为id自增 并且age改为数字
    create table test(id int primary key AUTO_INCREMENT,name varchar(20) not null DEFAULT 'unknow',sex int not null DEFAULT -1, age int not null DEFAULT -1,  tel varchar(20) not null DEFAULT 'unknow', email varchar(30) not null DEFAULT 'unknow', pers_desc varchar(150) not null DEFAULT 'unknow')
    
    6调用存储过程后,没有完全释放资源: 
    在mysql_free_result之后需要循环mysql_next_result清除result否则无法进行新的query


C++用户信息管理服务1.0

server代码

// This autogenerated skeleton file illustrates how to build a server.
// You should copy it to another filename to avoid overwriting it.

#include "serDemo.h"
#include <thrift/concurrency/ThreadManager.h>
#include <thrift/concurrency/ThreadFactory.h>

#include <thrift/server/TThreadPoolServer.h>
#include <thrift/server/TThreadedServer.h>
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/server/TSimpleServer.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/transport/TBufferTransports.h>
#include <unistd.h>
#include <pthread.h>

#include <jsoncpp/json/json.h> 
#include "rdkafka.h"

#include <fstream>    
#include <sstream> 
#include <map>
#include <string>
#include <pthread.h>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <mysql8/mysql.h>
#include <cstdio>
#include <memory>   
#include <hiredis.h>
// 读: 读redis->没有,读mysql->把mysql数据写回redis,有的话直接从redis中取;
// 写: 写mysql->成功,再写redis;

using namespace ::apache::thrift;
using namespace ::apache::thrift::protocol;
using namespace ::apache::thrift::transport;
using namespace ::apache::thrift::server;
using namespace ::apache::thrift::concurrency;

//l_msg_temp.id =-1 getuser 返回了一个空值
// return -2没有该用户id 
// return -1id不合法
// return -101 服务端getuser失败
// return 1数据插入数据库失败
// return 2新数据修改数据库vec失败!
// return 3客户端输出失败
// return 4message传入的数值不合法
//return 5 修改的数量为0
//seqid为负数也代表相应含义
// return 301 redisContext链接失败
// return 302 reply 获取失败
// return 303 reply->type 类型不对
// return 304 reply 数值不对
// return 305 reply->elements 数值不对

// return 401 mysql语句返回错误
// return 402 l_result返回错误
// return 403 判断存储过程中的return是否出错
// return 404 数据库没有该id的数据
// return 405 l_row[0]为null 不该为null的为null
// return 406 初始化mysql失败
// return 407 mysql连接失败

// return 408 mysql affecct_row=0;
// return 501 反序列化失败
// return 502 rd_kafka_conf_set失败
// return 503 rd_kafka_new失败


class serDemoHandler : virtual public serDemoIf {
 private:
  const char *hostname = "127.0.0.1";
  const int l_redis_size=12;
  const int l_redis_port=6379;
  
  redisContext *l_redis_connect=NULL;
  MYSQL* l_mysql_connect=NULL;
  
  //-----kafka---------------
  char l_brokers[100]="192.168.100.160:9092";
  char l_topic[100]="TestTopicForDocker";
    
  rd_kafka_t *l_rk=NULL;        // 生产者实例句柄
  rd_kafka_conf_t *l_conf=NULL; // 临时配置对象

  //-----kafka---------------


  pthread_mutex_t l_redis_mutex=PTHREAD_MUTEX_INITIALIZER;//static init mutex
  pthread_mutex_t l_mysql_mutex=PTHREAD_MUTEX_INITIALIZER;//static init mutex
  pthread_mutex_t l_kafka_mutex=PTHREAD_MUTEX_INITIALIZER;//static init mutex
  pthread_mutex_t l_updata_mutex=PTHREAD_MUTEX_INITIALIZER;//static init mutex
  std::string l_str_row[7]={"ID","name","sex","age","tel","email","pers_desc"};
  
  std::string CheckStr(std::string str)
  {
    // std::string l_key[11] = { "%","/","union","|","&","^","#","/*","*/","'"," "};
    // for (int i = 0; i < 11; i++)
    // {
    //     if (str.find(l_key[i]) != std::string::npos)
    //     {
    //         return false;
    //     }
    // }
    // return true;
    std::string l_str_tmp="'";
    int l_pos=str.find(l_str_tmp);
    for(int i=0;i<str.size();i++)
    {
      if(str[i]=='\'')
      {
        str.insert(i,l_str_tmp);
        i++;
      }
    }
    std::cout<<str<<std::endl;
    return str;
  }
  int l_array_check[10]={0};
 public:


  serDemoHandler() {
   
    // Your initialization goes here 
  }

  ~serDemoHandler() {
    
    redisFree(l_redis_connect);	
    mysql_close(l_mysql_connect); 
    rd_kafka_destroy(l_rk);//万事俱备 销毁实例
  }

  int32_t EstablishConnection()
  {
    struct timeval timeout = { 1, 500000 }; // 1.5 seconds
      
    //---------------链接mysql------------
    
    l_mysql_connect = mysql_init(NULL);  
    
    if(l_mysql_connect==NULL)
    {
        std::cout<<"初始化环境失败 "<<std::endl;
        return 406;//
    }

    l_mysql_connect=mysql_real_connect(l_mysql_connect, "192.168.100.160", "root", "200101", 
                                "exam2", 3306, NULL, 0);
    if(l_mysql_connect == NULL)
    {
        std::cout<<"连接数据库服务器失败 "<<std::endl;
        return 407;//
    }
    //---------------链接mysql------------

    //---------------链接redis------------
    l_redis_connect = redisConnectWithTimeout("127.0.0.1", 6379, timeout);
    if (l_redis_connect == NULL || l_redis_connect->err) 
    {
        if (l_redis_connect) 
        {
          std::cout<<"connect error:"<<l_redis_connect->errstr<<std::endl;
          redisFree(l_redis_connect);	//释放redisConnect()所产生的连接。
        } 
        else 
        {
          std::cout<<"connect error: can't allocate redis context "<<std::endl;
        }
        return 301;
    }
  //---------------链接redis------------

  //---------------链接kafka------------

  char l_errstr[512]={};      // API错误报告缓冲区
  //char l_buf[512]={};         // 消息值临时缓冲区

  l_conf=rd_kafka_conf_new();//创建kafka 客户端配置

  //配置各项参数 其中kafka port默认为9022号端口
  if(rd_kafka_conf_set(l_conf,"bootstrap.servers", l_brokers, l_errstr,sizeof(l_errstr)) != RD_KAFKA_CONF_OK) 
  {//若返回结果不等于这个enum类型 错误结果打印
      fprintf(stderr, "%s\n", l_errstr);
      rd_kafka_conf_destroy(l_conf);
      //
      return 502;
  }

  //设置发送回调函数 用以反馈信息发送的成败
  rd_kafka_conf_set_dr_msg_cb(l_conf, dr_msg_cb); //设置发送回调函数 一旦生产的每条消息被rd_kafka_produce函数接收,投递报告回调函数会被立即调用

  //初始化一个顶层对象 的基础容器 用于全局配置和共享状态
  //conf在rd_kafka_new调之后不能被再次使用·
  l_rk=rd_kafka_new(RD_KAFKA_PRODUCER, l_conf, l_errstr, sizeof(l_errstr));

  if(!l_rk)
  {
      fprintf(stderr, "%% Failed to create new producer: %s\n",l_errstr);
      rd_kafka_conf_destroy(l_conf);//
      return 503;
  }

  //如果分配成功 临时配置对象会在rd_kafka_new中被释放 此时无需再rd_kafka_conf_destroy
  l_conf=NULL;
  //---------------链接kafka------------


    return 0;
  }



  void QueryUser(message& _return, const int32_t seqId) 
  {
    std::cout<<"\nQueryMysql..."<<std::endl;
    //message l_msg_temp;
    _return.error_flag=0;
    if(seqId<1||seqId>=0x7fffffff)
    {
      std::cout<<"用户id输入长度或数值不合法传入过来的ID是:"<<seqId<<std::endl;
      _return.error_flag=-1;
      return ;
    }
    //先找redis
    // //-------------------操作redis部分--------------------↓
    //
    //1链接测试部分
    std::cout<<"\n进行redis链接...先查询redis是否存在数据..."<<std::endl;
    redisReply *l_reply=NULL;
    //sleep(20);
    //2从redis中找数据部分

    pthread_mutex_lock(&l_redis_mutex);
    l_reply = (redisReply*)redisCommand(l_redis_connect,"hgetall user%d",seqId);

    //int q;
    //std::cin>>q;
    pthread_mutex_unlock(&l_redis_mutex);
    if(l_reply==NULL)
    {
        std::cout<<"reply获取失败"<<std::endl;
        _return.error_flag=-302;
        return ;
    }
    else if(l_reply->type!=REDIS_REPLY_ARRAY)
    {
        
        std::cout<<"reply->type 类型错误"<<std::endl;
          freeReplyObject(l_reply);
          _return.error_flag=-303;
          return ;
    }
    else if(l_reply->elements%2==0&&l_reply->elements!=0)//在redis中 可能找到了 要进行判定
    {
        memset(l_array_check,0,sizeof(l_array_check));//初始化这个数组 以判定这个redis是否满足这个条件

        //std::cout<<"queryuser函数中在redis中找到了"<<std::endl;
        std::cout<<"l_reply->elements"<<l_reply->elements<<std::endl;
        //_return.usr.pers_desc="NULL";//先赋值一个null
        int l_flag_field=0;
        for(int i=0;i<l_reply->elements;i++)
        {
            if(l_reply->element[i]==NULL)
            {
              std::cout<<"reply获取失败"<<std::endl;
              freeReplyObject(l_reply);
              _return.error_flag=-302;
              return ;
            }
            else if(l_reply->element[i]->type!=REDIS_REPLY_STRING)
            {   
              std::cout<<"数据展示出错2"<<std::endl;
              freeReplyObject(l_reply);
              _return.error_flag=-303;
              return ;
            }
            //1//输出一下
            std::string l_strs_temp(l_reply->element[i]->str,l_reply->element[i]->len);
            std::cout<<"index "<<i<<":"<<l_strs_temp.c_str()<<std::endl;

            

            //根据当前出现的redis字符串 设置l_flag_field 找到下一个element应该赋给的自变量
            if(l_flag_field==0)
            {
              for(int j=1;j<=l_redis_size/2;j++)
              {
                if(strcmp(l_strs_temp.c_str(),l_str_row[j].c_str())==0)
                {
                  l_flag_field=j;
                  l_array_check[j]++;
                  break;
                }
              }
            }
            else 
            {
              switch (l_flag_field)
              {
                case 1:
                  _return.usr.name=l_strs_temp;
                  break;
                case 2:
                  _return.usr.sex=std::stoi(l_strs_temp.c_str());
                  break;
                case 3:
                  _return.usr.age=std::stoi(l_strs_temp.c_str());
                break;
                case 4:
                  _return.usr.tel=l_strs_temp;
                break;
                case 5:
                  _return.usr.email=l_strs_temp;
                break;
                case 6:
                  _return.usr.pers_desc=l_strs_temp;
                break;
                default:
                  break;
              }
              l_flag_field=0;
            }

            
        }
        
        for(int i=1;i<l_redis_size/2;i++)
        {
          if(l_array_check[i]==0)
          {
            _return.error_flag=-305;
            std::cout<<"有必要字段不存在..."<<" "<<i<<std::endl;
            freeReplyObject(l_reply);
            return ;
          }
        }
        _return.usr.seqId=seqId;
        freeReplyObject(l_reply);
        std::cout<<"GetUser函数结束..."<<std::endl;
        return ;
    }
    //std::cout<<l_reply->type<<" "<<l_reply->elements<<std::endl;
    std::cout<<"查询redis完毕 没找到数据结束redis链接..."<<std::endl;
    freeReplyObject(l_reply);
    //redisFree(connect);
    //-------------------操作redis部分--------------------↑

    //再找数据库
    //-------------------操作数据库部分--------------------
    std::cout<<"开始查找数据库..."<<std::endl;
    //4调用过程//
    char l_call_cstr[300]={};
    //std::string l_call_str="";
    sprintf(l_call_cstr, "call pr_queryuser (%d);",seqId);
    //l_call_str=l_call_cstr;
    pthread_mutex_lock(&l_mysql_mutex);//上锁
    int l_ret1=mysql_query(l_mysql_connect,l_call_cstr);
    //cout<<l_ret1<<" "<<l_mysql_connect<<" "<<l_call_cstr<<endl;
    if(l_ret1!=0)
    {

        std::cout<<"操作数据库语句错误call_error,语句为:"<<l_call_cstr<<"\n mysql_query的返回值为:"<<l_ret1<<std::endl;
        _return.error_flag=-401;
        pthread_mutex_unlock(&l_mysql_mutex);//解锁
        return ;
    }

    //返回结果测试  
    MYSQL_RES* l_result=mysql_store_result(l_mysql_connect);
    if(l_result==NULL)
    {
      std::cout<<"mysql_store_result() 失败了,原因 l_result=NULL."<<std::endl;
      //mysql_free_result(l_result);
      _return.error_flag=-402;
      pthread_mutex_unlock(&l_mysql_mutex);//解锁
      return ;
    }

    //6展示
    MYSQL_ROW l_row;
    l_row = mysql_fetch_row(l_result);
    int l_num=mysql_num_fields(l_result);
    
    //std::string s_temp="404";
    if(l_row==NULL)//strncmp(l_row[0],"0",1)!=0
    {
      std::cout<<"MYSQL_ROW为NULL,当前查询的id 在数据库中查询不到"<<std::endl;
      _return.error_flag=-404;
      //return ;//没有该id
    }
    else if(l_num!=7)//这里mysql即使有字段为nuLL 数量也是7 除非没该id
    {
      
      std::cout<<l_num<<std::endl;
      std::cout<<"MYSQL_ROW不为NULL,但是查询到的字段值竟然不是7 一个预期之外的错误!"<<std::endl;
      _return.error_flag=-405;
      //return ;//判断存储过程中的return是否出错
    }
    else 
    {
      std::cout<<"存储过程返回值数据如下:"<<std::endl;
      for(int i=0;i<l_redis_size/2+1;i++)
      {
        if(l_row[i]!=NULL)
        {
          std::cout<<l_str_row[i]<<l_row[i]<<std::endl;
        }
        else 
        {
          std::cout<<l_str_row[i]<<"为null"<<std::endl;
          if(i!=6)
          {
            std::cout<<"一个不是可以为null的字段竟然为null,预期之外的错误!"<<std::endl;
            _return.error_flag=-405;
            //return ;
            break;
          }

        }
      }
      
      
      //释放mysql资源
      _return.usr.seqId=seqId;
      _return.usr.name=l_row[1];
      _return.usr.sex=std::stoi(l_row[2]);

      //std::string l_strs_temp_row3(l_row[3]);
      _return.usr.age=std::stoi(l_row[3]);//
      _return.usr.tel=l_row[4];
      _return.usr.email=l_row[5];
      _return.usr.pers_desc=l_row[6];
    }
    

    // 在row使用完毕之后 开始清除l_result 
    mysql_free_result(l_result);

    //循环清除结果集 对于存储过程必须如此 否则无法重复使用连接
    while (!mysql_next_result(l_mysql_connect)) {
        l_result = mysql_store_result(l_mysql_connect);
        mysql_free_result(l_result);
    }
    pthread_mutex_unlock(&l_mysql_mutex);//解锁
    l_row=NULL;
    if(_return.error_flag!=0)
    {
       std::cout<<"queryuser 函数在redis和数据库中都没找到相关数据..."<<std::endl;
       return ;
    }
    
    
    // 
    std::cout<<"queryuser 函数在数据库中找到相关数据..."<<std::endl;
    //-------------------操作数据库部分--------------------↑



    // //-------------------操作redis部分--------------------↓
    std::cout<<"mysql找到数据完毕...将数据插入到redis..."<<std::endl;



    pthread_mutex_lock(&l_redis_mutex);

    //2写入redis中数据部分
    if(_return.usr.pers_desc.c_str()!=NULL)//
      l_reply = (redisReply*)redisCommand(l_redis_connect,"hmset user%d name %s sex %d age %d tel %s email %s pers_desc %s",seqId,_return.usr.name.c_str(),_return.usr.sex,_return.usr.age,_return.usr.tel.c_str(),_return.usr.email.c_str(),_return.usr.pers_desc.c_str());
    else 
      l_reply = (redisReply*)redisCommand(l_redis_connect,"hmset user%d name %s sex %d age %d tel %s email %s",seqId,_return.usr.name.c_str(),_return.usr.sex,_return.usr.age,_return.usr.tel.c_str(),_return.usr.email.c_str());

    
    pthread_mutex_unlock(&l_redis_mutex);
    if(l_reply==NULL)
    {
      std::cout<<"reply获取失败"<<std::endl;
      _return.error_flag=302;
      return ;
    }
    else if(l_reply->type!=REDIS_REPLY_STATUS)
    {
        
      std::cout<<"reply->type 类型错误"<<std::endl;
      freeReplyObject(l_reply);
      _return.error_flag=303;
      return ;
    }
    else if(strncmp("OK",l_reply->str,2)!=0)
    {
        std::cout<<"数据插入失败"<<std::endl;
        freeReplyObject(l_reply);
        _return.error_flag=304;
        return ;
    }
        std::cout<<"query将数据插入redis完毕 ..."<<std::endl;

    //-------------------操作redis部分--------------------上

    //3释放redis资源
    freeReplyObject(l_reply);

    std::cout<<"QueryUser函数结束 ..."<<std::endl;

    return ;//该id存在
  }



  int32_t InsertUser(const User1& user)
  {

    std::cout<<"InsertUser..."<<std::endl;
    User1 l_temp_user=user;
    int32_t l_Query_return=0;
    int32_t l_seqId=0;

    if(user.name.size()>20)
    {
      std::cout<<"传递过来的message数据name字段非法..."<<std::endl;
      return -4;
    }
    else if(user.sex>1||user.sex<0)
    {
      std::cout<<"传递过来的message数据sex字段非法..."<<std::endl;
      return -4;
    }
    else if(user.age>150||user.age<0)
    {
      std::cout<<"传递过来的message数据age字段非法..."<<std::endl;
      return -4;
    }
    else if(user.tel.size()>20)
    {
      std::cout<<"传递过来的message数据tel字段非法..."<<std::endl;
      return -4;
    }
    else if(user.email.size()>30)
    {
      
      std::cout<<"传递过来的message数据email字段非法..."<<std::endl;
      return -4;
    }
    else if(user.pers_desc.size()>100)
    {
      std::cout<<"传递过来的message数据pers_desc字段非法..."<<std::endl;
      return -4;
    }
    
    //先插入mysql
    //-------------------操作数据库部分--------------------
    //mysql指针初始化
 
    //4调用过程

    char l_call_cstr[300]={};
    //std::string l_call_str="";
    //sprintf(l_call_cstr, "call insertuser ('111',1,1,'1','1','1');");

    sprintf(l_call_cstr, "call pr_insertuser ('%s', %d , %d , '%s','%s','%s');",CheckStr(user.name).c_str(),user.sex,user.age,CheckStr(user.tel).c_str(),CheckStr(user.email).c_str(),CheckStr(user.pers_desc).c_str());
    //l_call_str=l_call_cstr; 
    //printf("%s\n",l_call_cstr); 

    pthread_mutex_lock(&l_mysql_mutex);
    int32_t l_ret1=mysql_query(l_mysql_connect, l_call_cstr);
    MYSQL_ROW l_row=NULL;
    int l_num=0;
    int l_result_flag=0;
    if(l_ret1!=0)
    {
        std::cout<<"操作数据库语句错误call_error,语句为:"<<l_call_cstr<<"\n mysql_query的返回值为:"<<l_ret1<<std::endl;
        pthread_mutex_unlock(&l_mysql_mutex);
        return -401;
    }
   
    
    MYSQL_RES* l_result=mysql_store_result(l_mysql_connect);
    if(l_result==NULL)
    {
        l_result_flag=-402;
        std::cout<<"mysql_store_result() 失败了,l_result==NULL"<<std::endl;
    }
    else 
    {
      l_row = mysql_fetch_row(l_result);
      l_num = mysql_num_fields(l_result);
    }
    

    if(l_result_flag==0&&l_row==NULL)//strncmp(l_row[0],"0",1)!=0
    {
      std::cout<<"没有当前查询id..."<<std::endl;

      l_result_flag= -405;//没有该id
    }
    else if(l_result_flag==0&&(l_num!=1||l_row[0]==NULL))
    {
      std::cout<<"l_num_filed:"<<l_num<<std::endl;

      l_result_flag= -405;//判断存储过程中的return是否出错
    }
    else if(l_result_flag==0)
    {
      l_seqId=std::stoi(l_row[0]);//
    }
    
    // 返回结果测试  
    
    //清除掉相应的result 但是同时也要注意 不要将 row还在的时候就将其清除掉否则会导致row指针悬挂

    
    mysql_free_result(l_result);
    while (!mysql_next_result(l_mysql_connect)) {
        l_result = mysql_store_result(l_mysql_connect);
        mysql_free_result(l_result);
    }
    pthread_mutex_unlock(&l_mysql_mutex);


    if(l_result_flag!=0)
    {
       std::cout<<"queryuser 函数在redis和数据库中都没找到相关数据..."<<std::endl;
       return l_result_flag;
    }
    std::cout<<"成功插入进数据库..插入成功!..."<<std::endl;
    //-------------------操作数据库部分--------------------↑

    //然后再插入redis
    //-------------------操作redis部分--------------------↓

    
    redisReply *l_reply=NULL;

    struct timeval timeout = { 1, 500000 }; // 1.5 seconds
    
    //1链接测试部分


    //2写入redis中数据部分
    pthread_mutex_lock(&l_redis_mutex);
    if(strncmp(user.pers_desc.c_str(),"NULL",4))
      l_reply = (redisReply*)redisCommand(l_redis_connect,"hmset user%d name %s sex %d age %d tel %s email %s pers_desc %s",user.seqId,user.name.c_str(),user.sex,user.age,user.tel.c_str(),user.email.c_str(),user.pers_desc.c_str());
    else 
      l_reply = (redisReply*)redisCommand(l_redis_connect,"hmset user%d name %s sex %d age %d tel %s email %s",user.seqId,user.name.c_str(),user.sex,user.age,user.tel.c_str(),user.email.c_str());

    pthread_mutex_unlock(&l_redis_mutex);
    if(l_reply==NULL)
    {
        std::cout<<"reply获取失败"<<std::endl;
        //redisFree(connect);	//释放redisConnect()所产生的连接。
        return -302;
    }
    else if(l_reply->type!=REDIS_REPLY_STATUS)
    {
        
        std::cout<<"reply->type 类型错误"<<std::endl;
        freeReplyObject(l_reply);
        //redisFree(connect);	//释放redisConnect()所产生的连接。
        return -303;
    }
    else if(strncmp("OK",l_reply->str,2)!=0)
    {
        std::cout<<"数据插入失败"<<std::endl;
        freeReplyObject(l_reply);
        //redisFree(connect);	//释放redisConnect()所产生的连接。
        return -304;
    }
    std::cout<<"成功插入进redis..插入成功!..."<<std::endl;
    freeReplyObject(l_reply);
    //redisFree(connect);
    //-------------------操作redis部分--------------------↑

    int l_return_pro=Prod2Cons(user);
    //传递给生产者
    if(l_return_pro!=0)
    {
      std::cout<<"传递给生产者出错..."<<std::endl;
    }
    std::cout<<"InsertUser函数结束...\n"<<std::endl;
    return l_seqId;


  }



  int32_t UpdataUser(const int32_t seqId,const User1& user)
  {
    std::cout<<"\nUpdataUser...\n"<<std::endl;
    int l_change_num=0;//修改的数目
    int l_check_parameters=0;//输入的参数是否正确

    //name
    if(!user.name.empty())
    {
      if(user.name.size()<=20)
      {
        l_change_num++;
      }
      else 
      {
        std::cout<<"\n传递过来的message数据name字段非法...\n"<<std::endl;
        l_check_parameters= -4;
      }
    }

    //sex
    if(user.sex!=-1)
    {
      if(user.sex==0||user.sex==1)
      {
        l_change_num++;
      }
      else 
      {
        std::cout<<"\n传递过来的message数据sex字段非法...\n"<<std::endl;
        l_check_parameters= -4;
      }
    }

    //age user.age!=NULL
    if( user.age!=-1)
    {
      if(user.age<=150&&user.age>=0)
      {
        l_change_num++;
      }
      else 
      {
        std::cout<<"\n传递过来的message数据age字段非法...\n"<<std::endl;
        l_check_parameters = -4 ;
      }
    }

    //tel

    if(!user.tel.empty())
    {
      if(user.tel.size()>20)
      {
      
        printf("tel输入过长错误,请重新输入!\n");
        l_check_parameters = -4 ;
      }
      else 
      {
        for(int i=0;i<user.tel.size();i++)
        {
          if(user.tel[i]<'0'||user.tel[i]>'9')
          {
            std::cout<<"\n传递过来的message数据tel字段非法...\n"<<std::endl;
            l_check_parameters = -4 ;
            break;
          }
          if(i==user.tel.size()-1)
          {
            l_change_num++;
          }
        }
      }
    }
    //email

    if(!user.email.empty())
    {
      if(user.email.size()<=30)
      {
        l_change_num++;
      }
      else 
      {
        std::cout<<"\n传递过来的message数据email字段非法...\n"<<std::endl;
        l_check_parameters = -4 ;
      }
    }

    //pers_desc
    if(!user.pers_desc.empty())
    {
      if(user.pers_desc.size()<=150)
      {
        l_change_num++;
      }
      else 
      {
        std::cout<<"\n传递过来的message数据pers_desc字段非法...\n"<<std::endl;
        l_check_parameters = -4 ;
      }
    }
    if(l_check_parameters)return l_check_parameters;
    if(l_change_num==0)return -5;
    std::cout<<"l_change_num"<<l_change_num<<std::endl;
    //1 判断一下该id是否存在 
    // QueryUser(l_temp_query_user,seqId);
    // l_Query_return=l_temp_query_user.seqId;
    // if(l_Query_return==-404)
    // {
    //   printf("该id数据不存在,修改失败!\n");
    //   return l_Query_return;
    // }
    // else if(l_Query_return<=0)
    // {
    //   printf("QueryMysql出错 l_Query_return=%d\n",l_Query_return);
    //   return l_Query_return;
    // }



    //-------------------操作数据库部分--------------------
    //mysql指针初始化
   
    char l_call_cstr[300]={0};
    //std::string l_call_str="";
    message l_temp_query_msg;
    
    //l_call_str=l_call_cstr;
    
    
    pthread_mutex_lock(&l_updata_mutex);//再加一个updata lock防止出现 有两个客户端同时更新一个用户的数据 如果这里只用mysql锁 那么下一行QueryUser会死锁
    //----得到一个待修改前完整的user 将未修改的字段也填上 后面存储过程updata无法更新单个或部分字段
    QueryUser(l_temp_query_msg,seqId);
    if(l_temp_query_msg.error_flag!=0)
    {
      std::cout<<"插入时 在query函数中查询该id 未得到正确结果 返回错误代码为:"<<l_temp_query_msg.error_flag<<std::endl;
      pthread_mutex_unlock(&l_updata_mutex);
      return l_temp_query_msg.error_flag;
    }
    std::cout<<std::endl<<"---------------------"<<std::endl<<l_temp_query_msg.usr.email<<std::endl;
    //若有改变 就赋值给user 否则用原来的值
    if(!user.name.empty())l_temp_query_msg.usr.name=user.name;
    if(user.sex!=-1)l_temp_query_msg.usr.sex=user.sex;//222222222
    if(user.age!=-1)l_temp_query_msg.usr.age=user.age;
    if(!user.tel.empty())l_temp_query_msg.usr.tel=user.tel;
    if(!user.email.empty())l_temp_query_msg.usr.email=user.email;
    if(!user.pers_desc.empty())l_temp_query_msg.usr.pers_desc=user.pers_desc;
    
    std::cout<<std::endl<<"---------------------"<<std::endl<<l_temp_query_msg.usr.email<<std::endl;
    
    sprintf(l_call_cstr, "call pr_updatauser (%d,'%s',%d,'%d','%s','%s','%s');"
        ,seqId
        ,CheckStr(l_temp_query_msg.usr.name).c_str()//
        ,l_temp_query_msg.usr.sex
        ,l_temp_query_msg.usr.age
        ,CheckStr(l_temp_query_msg.usr.tel).c_str()
        ,CheckStr(l_temp_query_msg.usr.email).c_str()
        ,CheckStr(l_temp_query_msg.usr.pers_desc).c_str());
    //----得到一个待修改前完整的user 将未修改的字段也填上 后面存储过程updata无法更新单个或部分字段

    pthread_mutex_lock(&l_mysql_mutex);
    int32_t l_ret1=mysql_query(l_mysql_connect, l_call_cstr);
    if(l_ret1!=0)
    {
      std::cout<<"call_updatastudent_error 输入参数长度有问题"<<std::endl;
      pthread_mutex_unlock(&l_mysql_mutex);
      pthread_mutex_unlock(&l_updata_mutex);
      
      return 401;
    }
    int l_affected_rows=mysql_affected_rows(l_mysql_connect);//这里int型可以在free_result后使用

    MYSQL_RES* l_result=mysql_store_result(l_mysql_connect);
    mysql_free_result(l_result);
    while (!mysql_next_result(l_mysql_connect)) //
    {
      l_result = mysql_store_result(l_mysql_connect);
      mysql_free_result(l_result);
    }
    pthread_mutex_unlock(&l_mysql_mutex);
    pthread_mutex_unlock(&l_updata_mutex);
    if(l_affected_rows!=1)
    {
      std::cout<<"修改错误 本应修改的行数为1,实际值为"<<l_affected_rows<<std::endl;
      return 401;
    }
    // //返回结果测试  
    // MYSQL_RES* l_result=mysql_store_result(mysql);
    // if(l_result==NULL)
    // {
    //     printf("mysql_store_result() 失败了, 原因: %s\n", mysql_error(mysql));
    //     mysql_free_result(l_result);
    //     return 402;
    // }
    // mysql_free_result(l_result);


    std::cout<<"成功修改进数据库..插入成功!"<<std::endl;
    //-------------------操作数据库部分--------------------



    //-------------------操作redis部分--------------------
   
    redisReply *l_reply=NULL;
    struct timeval timeout = { 1, 500000 }; // 1.5 seconds
    


    //2写入redis中数据部分
    pthread_mutex_lock(&l_redis_mutex);
  
    if(!l_temp_query_msg.usr.pers_desc.empty())
      l_reply = (redisReply*)redisCommand(l_redis_connect,"hmset user%d name %s sex %d age %d tel %s email %s pers_desc %s"
        ,seqId
        ,l_temp_query_msg.usr.name.c_str()
        ,l_temp_query_msg.usr.sex
        ,l_temp_query_msg.usr.age
        ,l_temp_query_msg.usr.tel.c_str()
        ,l_temp_query_msg.usr.email.c_str()
        ,l_temp_query_msg.usr.pers_desc.c_str());
    else 
      l_reply = (redisReply*)redisCommand(l_redis_connect,"hmset user%d name %s sex %d age %d tel %s email %s"
      ,seqId
      ,l_temp_query_msg.usr.name.c_str()
      ,l_temp_query_msg.usr.sex
      ,l_temp_query_msg.usr.age
      ,l_temp_query_msg.usr.tel.c_str()
      ,l_temp_query_msg.usr.email.c_str());
    
    pthread_mutex_unlock(&l_redis_mutex);
    if(l_reply==NULL)
    {
        std::cout<<"reply获取失败"<<std::endl;
        //redisFree(connect);	//释放redisConnect()所产生的连接。
        return 302;
    }
    else if(l_reply->type!=REDIS_REPLY_STATUS)
    {
        
        std::cout<<"reply->type 类型错误"<<std::endl;
        freeReplyObject(l_reply);
        //redisFree(connect);	//释放redisConnect()所产生的连接。
        return 303; 
    }
    else if(strncmp("OK",l_reply->str,2)!=0)
    {
        std::cout<<"数据插入失败"<<std::endl;
        freeReplyObject(l_reply);
        //redisFree(connect);	//释放redisConnect()所产生的连接。
        return 304;
    }
    std::cout<<"成功修改进redis..修改成功!"<<std::endl;

    freeReplyObject(l_reply);
    //-------------------操作redis部分--------------------
    int l_return_pro=Prod2Cons(l_temp_query_msg.usr);
    //传递给生产者
    if(l_return_pro!=0)
    {
      std::cout<<"传递给生产者出错..."<<std::endl;

    }

    std::cout<<"Updata函数结束...\n"<<std::endl;

    return 0;

  }



//return 501 反序列化失败
//return 502 rd_kafka_conf_set失败
//return 503 rd_kafka_new失败


static void dr_msg_cb(rd_kafka_t *l_rk, const rd_kafka_message_t *l_rkmessage, void *l_opaque) 
{
  if (l_rkmessage->err)
  {
      fprintf(stderr,"%% Message delivery failed: %s\n",rd_kafka_err2str(l_rkmessage->err));
  }    
  else
  {
      fprintf(stderr,"%% Message delivered (%zd bytes, partition %" PRId32 ")\n",
      l_rkmessage->len, l_rkmessage->partition);
  }
  // l_rkmessage 能自动销毁by librdkafka 
}


int Prod2Cons(User1 usr)
{
    //-----------------------msg to json---------------
    Json::Value l_root;
    l_root["ID"]=usr.seqId;
    l_root["name"]=usr.name;
    l_root["sex"]=usr.sex;
    l_root["age"]=usr.age;
    l_root["tel"]=usr.tel;
    l_root["email"]=usr.email;
    l_root["pers_desc"]=usr.pers_desc;
    
    //-----------------------msg to json---------------

    
    //-----------------------json to str---------------
   
    Json::StreamWriterBuilder l_swb;   
    std::shared_ptr <Json::StreamWriter > l_sw(l_swb.newStreamWriter() ) ; //l_sw指向l_l_swb
    
    //通过l_sw的write将l_root序列化成Json字符串,并存储到str中    
    std::stringstream l_str;    
    l_sw->write(l_root,&l_str);   
    
    std::string l_str_serialization=l_str.str(); 
    char * l_trans_str=(char*)l_str_serialization.data();
    //char *str_char=l_str_serialization.c_str();
    if(l_trans_str==NULL)
    {
        std::cout<<"反序列化失败\n"<<std::endl;

        return 501;
    }
    //-----------------------json to str---------------



    //-----------------------producer模板---------------



    pthread_mutex_lock(&l_kafka_mutex);
    /* Signal handler for clean shutdown */

    fprintf(stderr,//打印...
            "%% Type some text and hit enter to produce message\n"
            "%% Or just hit enter to only serve delivery reports\n"
            "%% Press Ctrl-C or Ctrl-D to exit\n");
    rd_kafka_resp_err_t l_err;//错误代码提示
    
        // SendProduce message.
        // 这是一个异步调用,在成功的情况下,只会将消息排入内部producer队列,
        // 对broker的实际传递尝试由后台线程处理,之前注册的传递回调函数(dr_msg_cb)
        // 用于在消息传递成功或失败时向应用程序发回信号

    while(1)
    {
        l_err=rd_kafka_producev(
        l_rk,//顶层对象 的基础容器 用于全局配置和共享状态,之前通过rd_kafka_topic_new()生成
        RD_KAFKA_V_TOPIC(l_topic),//topic
        RD_KAFKA_V_MSGFLAGS(RD_KAFKA_MSG_F_COPY),//表示librdkafka 在信息发送前立即从 payload 做一份拷贝
        RD_KAFKA_V_VALUE(l_trans_str, l_str_serialization.size()),//消息和长度//传信息
        RD_KAFKA_V_OPAQUE(NULL),//可选的,应用程序为每个消息提供的无类型指针,提供给消息发送回调函数,用于应用程序引用。
        RD_KAFKA_V_END); /* End sentinel */

        if(l_err)
        {//若该值不是0则为一个错误码
            fprintf(stderr,"%% Failed to produce to topic %s: %s\n", l_topic, rd_kafka_err2str(l_err));

            if(l_err==RD_KAFKA_RESP_ERR__QUEUE_FULL) 
            {

            //如果内部队列已满,请等待消息传递,然后重试。
            //内部队列表示要发送的消息和已经发送或失败的消息,等待调用它们的传递报告回调。
            
                rd_kafka_poll(l_rk,1000 );//阻塞等待消息1000ms至发送完成或其它操作
                continue;//再传!
            }
        } 
        else 
        {//若该值为0 传送成功 打印出相应成果
            fprintf(stderr,"%% Enqueued message (%zd bytes) for topic %s\n",l_str_serialization.size(), l_topic);
            break;
        }
    }
            
    // producer应用程序应不断地通过以频繁的间隔调用rd_kafka_poll()来为
    // 传送报告队列提供服务。在没有生成消息以确定先前生成的消息已发送了其
    // 发送报告回调函数(和其他注册过的回调函数)期间,要确保rd_kafka_poll()
    // 仍然被调用
    rd_kafka_poll(l_rk, 0);
    


    //   rd_kafka_flush是rd_kafka_poll()的抽象化,
    //   等待所有未完成的produce请求完成,通常在销毁producer实例前完成
    //   以确保所有排列中和正在传输的produce请求在销毁前完成
    fprintf(stderr, "%% Flushing final messages..\n");
    rd_kafka_flush(l_rk,10*1000);

    //如果输出队列仍然不为空,则存在向集群生成消息的问题。
    if(rd_kafka_outq_len(l_rk)>0)
    {
        fprintf(stderr,"%% %d message(s) were not delivered\n",rd_kafka_outq_len(l_rk));
    }
    
    pthread_mutex_unlock(&l_kafka_mutex);
    
    
    //-----------------------producer模板---------------
    return 0;
}


};

int main() {
  int l_thrift_port = 9090;
  // ::std::shared_ptr<serDemoHandler> handler(new serDemoHandler());
  // ::std::shared_ptr<TProcessor> processor(new serDemoProcessor(handler));
  // ::std::shared_ptr<TServerTransport> serverTransport(new TServerSocket(l_thrift_port));
  // ::std::shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());
  // ::std::shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());

  // TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);

   // thread pool

 ::std::shared_ptr<serDemoHandler> handler(new serDemoHandler());
 ::std::shared_ptr<TProcessor> processor(new serDemoProcessor(handler));
 ::std::shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
 ::std::shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());
 ::std::shared_ptr<TServerTransport> serverTransport(new TServerSocket(9090));

// 指定15个线程
  
  ::std::shared_ptr<ThreadManager> threadManager = ThreadManager::newSimpleThreadManager(15);
  ::std::shared_ptr<ThreadFactory> threadFactory
  = ::std::shared_ptr<ThreadFactory>(new ThreadFactory());

  threadManager->threadFactory(threadFactory);
  threadManager->start();
  std::cout<<"start... "<<std::endl;
  TThreadPoolServer server(processor,
                           serverTransport,
                           transportFactory,
                           protocolFactory,
                           threadManager);


  int l_con_return=handler->EstablishConnection();
  if(l_con_return!=0)
  {
    return l_con_return;
  }
   
  server.serve();
  
  std::cout<<"end"<<std::endl;
  return 0;

  // MYSQL* mysql = mysql_init(NULL);  
  // cout<<1<<endl;
  // server.serve();
}



 Client

// 在同级目录下创建 client.cpp 文件
// ----------替换成自己的头文件----------
#include "serDemo.h"

// --------------------------------------
#include <thrift/transport/TSocket.h>
#include <iostream>
#include <thrift/transport/TBufferTransports.h>
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/server/TSimpleServer.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/transport/TBufferTransports.h>
#include <map>
#include <string>
#include <pthread.h>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <mysql8/mysql.h>
#include <cstdio>
#include <hiredis.h>

using namespace ::apache::thrift;
using namespace ::apache::thrift::protocol;
using namespace ::apache::thrift::transport;
// using boost::shared_ptr;   

// return -2没有该用户id 
// return -1id不合法
// return -101 服务端getuser失败
// return 1数据插入数据库失败
// return 2新数据修改数据库vec失败!
// return 3客户端输出失败
// return 4message传入的数值不合法

//seqid为负数也代表相应含义
// return 301 redisContext链接失败
// return 302 reply 获取失败
// return 303 reply->type 类型不对
// return 304 reply 数值不对
// return 401 mysql语句返回错误
// return 402 l_result返回错误
// return 403 判断存储过程中的return是否出错
// return 404 数据库没有该id的数据
// return 405 l_row[0]为null 不该为null的为null
// return 406 初始化mysql失败
// return 407 mysql连接失败

// return 408 mysql affecct_row=0;
// return 501 反序列化失败
// return 502 rd_kafka_conf_set失败
// return 503 rd_kafka_new失败
const int data_size=7;
int32_t OutPut(const User1& user) {
  printf("id:%d\nname:%s\nsex:%d\nage:%d\ntel:%s\nemail:%s\npers_desc:%s\n",
      user.seqId,user.name.c_str(),user.sex,user.age,user.tel.c_str(),user.email.c_str(),user.pers_desc.c_str());
  return 0;
}
int32_t FindUser(serDemoClient client,int32_t l_tmp_seqId)
{
  

  message l_msg_temp;  
  // Your implementation goes here
  printf("正在查询用户...\n");
  printf("请输入你想查询的用户id,请键入数字...\n");

    
  if(l_tmp_seqId<0||l_tmp_seqId>=0x7fffffff)
  {

    printf("用户id不数字非法,请重新键入数字...\n");
    return 1;
  }
  client.QueryUser(l_msg_temp,l_tmp_seqId);
  if(l_msg_temp.error_flag==-404)
  {
    printf("用户id不存在,请重新键入数字...\n");
    return 1;
  }
  else if(l_msg_temp.usr.seqId>0)//这个id既合法也存在
  {
    
  }
  else 
  {
    printf("用户id输入不合法,请重新键入数字...\n");
    return 1;
  }

  //得到该用户
  
  std::cout<<"客户端展示用户数据中..."<<std::endl;
  if(OutPut(l_msg_temp.usr))
  {
    std::cout<<"客户端展示用户数据失败"<<std::endl;
    return 3;
  }
  std::cout<<"查找用户成功!"<<std::endl;

  return 0;
  
}
int32_t CreateUser(serDemoClient client,User1 user )//
{
  //printf("正在创建用户...\n");


  int l_check_parameters=0;
  
  //name
  if(user.name.size()>20)
  {
    l_check_parameters=1;
    printf("name输入太长,请重新输入!\n");
    //printf("用户name输入成功!\n");
  }
  //sex
  if(user.sex!=0&&user.sex!=1)
  {
    l_check_parameters=1;
    printf("sex输入错误,请重新输入!\n");
  }

  //age
  if(user.age>150||user.age<0)
  {
    printf("age输入错误,请重新输入!\n");
    l_check_parameters=1;  
  }
   
  //tel
  for(int i=0;i<user.tel.size();i++)
  {
    if(user.tel[i]<'0'||user.tel[i]>'9')
    {
      printf("tel输入非法字符,请重新输入!\n");
      
      l_check_parameters=1;  
      break;
    }
  }
  if(user.tel.size()>20)
  {
    printf("tel输入过长错误,请重新输入!\n");
    l_check_parameters=1;
  }
  
  //email
  if(user.email.size()>30)
  {

    printf("email输入过长错误,请重新输入!\n");
    l_check_parameters=1;

  }
  
  //pers_desc
  // getchar();
  // getline(std::cin,l_desc_temp);
  if(user.pers_desc.c_str()!=NULL&&user.pers_desc.size()>150)//2222222222
  {
    printf("personal_describe输入过长错误,请重新输入!\n");
    l_check_parameters=1;
  }
    
  if(l_check_parameters)
  {
    return 1;
  }

  int32_t l_createuser_return=client.InsertUser(user);
  //将数据插入数据库 

  if(l_createuser_return<=0)
  {
    //插入数据失败!
    std::cout<<"数据插入数据库失败!"<<std::endl;
    return 1;
  }

  std::cout<<"创建用户成功!你的用户id是:"<<l_createuser_return<<std::endl;

  return 0;

}
int32_t UpdataUser(serDemoClient client,User1 user)
{
  int l_change_num=0;//修改的数目
  int l_check_parameters=0;//输入的参数是否正确

  //name
  if(!user.name.empty())
  {
    if(user.name.size()<=20)
    {
      l_change_num++;
    }
    else 
    {
      printf("name输入太长,请重新输入!\n");
      l_check_parameters= 1;
    }
  }
  
  //sex
  if(user.sex!=-1)///2222222222222222222222222222222222
  {
    if(user.sex==0||user.sex==1)
    {
      l_change_num++;
    }
    else 
    {
      printf("sex输入错误,请重新输入!\n");
      l_check_parameters= 1;
    }
  }

  //age

  if(user.age!=-1)//222222222222222222
  {
    if(user.age<=150&&user.age>=0)
    {
      l_change_num++;
    }
    else 
    {
      printf("age输入错误,请重新输入!\n");
      l_check_parameters = 1 ;
    }
  }

  //tel

  if(!user.tel.empty())
  {
    if(user.tel.size()>20)
    {
    
      printf("tel输入过长错误,请重新输入!\n");
      l_check_parameters = 1 ;
    }
    else 
    {
      for(int i=0;i<user.tel.size();i++)
      {
        if(user.tel[i]<'0'||user.tel[i]>'9')
        {
          printf("tel输入非法字符,请重新输入!\n");
          l_check_parameters = 1 ;
          break;
        }
        if(i==user.tel.size()-1)
        {
          l_change_num++;
        }
      }
    }
  }

  
  //email

  if(!user.email.empty())
  {
    if(user.email.size()<=30)
    {
      l_change_num++;
    }
    else 
    {
      printf("email输入过长错误,请重新输入!\n");
      l_check_parameters = 1 ;
    }
  }
  
  //pers_desc

  // getchar();
  // getline(std::cin,l_desc_temp);

  if(!user.pers_desc.empty())
  {
    if(user.pers_desc.size()<=150)
    {
      l_change_num++;
    }
    else 
    {
      printf("personal_describe输入过长错误,请重新输入!\n");
      l_check_parameters = 1 ;
    }
  }
  if(l_check_parameters)
  {

    std::cout<<"输入的参数错误...!"<<std::endl;
    return 1;
  }
  if(l_change_num<=0)
  {
    std::cout<<"修改字段数量小于1...!"<<std::endl;
    return 1;
  }
  int32_t l_updatauser_return=client.UpdataUser(user.seqId,user);
  //将数据插入数据库 

  if(l_updatauser_return!=0)
  {
    //插入数据失败!
    std::cout<<"数据更新进数据库失败!"<<std::endl;
    return 1;
  }

  std::cout<<"更新用户成功"<<std::endl;

  return 0;
}
int main(int argc,char **argv) 
{   
  
  std::shared_ptr<TSocket> socket(new TSocket("192.168.48.2", 9090));
  std::shared_ptr<TTransport> transport(new TBufferedTransport(socket));   
  std::shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));

  serDemoClient client(protocol);   
   
  transport->open();   

  char *l_operate=NULL;
  char *l_id=NULL;
  char **l_fields=NULL;
  int l_field_nums=0;
  User1 user;
  std::string l_str_row[7]={"ID","name","sex","age","tel","email","pers_desc"};
  if (argc<3) 
  {
    fprintf(stderr,"%% Usage: ""%s Find <ID> \n",argv[0]);
    fprintf(stderr,"%% Usage: ""%s Insert <name> <sex> <age> <tel> <email> (<pers_desc>)..\n",argv[0]);
    fprintf(stderr,"%% Usage: ""%s Updata <ID> <field1> <content> <field2> <content>... )\n",argv[0]);
    printf("退出系统\n");
    transport->close();   
    return 1;
  }
  l_operate=argv[1];
  //--------------find----------
  if(strcmp(l_operate,"Find")==0)//find
  {
    if (argc!=3)
    {
      fprintf(stderr,"%% Usage: ""%s Find <ID> \n",argv[0]);
      printf("退出系统\n");
      transport->close();  
      return 1;
    }
    l_id=argv[2];
    if(FindUser(client,std::stoi(l_id)))
    {
      fprintf(stderr,"%% Usage: ""%s Find <ID> \n",argv[0]); 
      printf("退出系统\n");
      transport->close();  
      return 1;
    }

  }
  //----------------insert--------
  else if(strcmp(l_operate,"Insert")==0)//insert
  {
    if (argc!=7&&argc!=8)
    {
      //输入数量错误
      fprintf(stderr,"%% Usage: ""%s Insert <name> <sex> <age> <tel> <email> (<pers_desc>)..\n",argv[0]);
      std::cout<<"argc数量错误数量为:"<<argc<<"...\n退出系统\n"<<std::endl;
      transport->close();  
      
      return 1;
    }
    user.name=argv[2];
    user.sex=std::stoi(argv[3]);
    user.age=std::stoi(argv[4]);
    user.tel=argv[5];
    user.email=argv[6];
    if(argc==8)
    {
      user.pers_desc=argv[7];
    }
    else 
    {
      //user.pers_desc="NULL";
    }
    if(CreateUser(client,user))
    {
      fprintf(stderr,"%% Usage: ""%s Insert <name> <sex> <age> <tel> <email> (<pers_desc>)..\n",argv[0]);
      printf("CreateUser 失败...\n退出系统\n");
      transport->close();  
      return 1;
    }
    
  }
  //------------updata-------
  else if(strcmp(l_operate,"Updata")==0)//updata
  {
    if (argc%2==0||argc>3+(data_size-1)*2)
    {
      //输入数量错误
      fprintf(stderr,"%% Usage: ""%s Updata <ID> <field1> <content> <field2> <content>..\n",argv[0]);
      printf("退出系统\n");
      transport->close();  
      return 1;
    }

    message l_msg;
    int l_flag_field=0;
    // client.QueryUser(l_msg,std::stoi(argv[2]));//
    // if(l_msg.error_flag<0)
    // {
    //   std::cout<<"ID查找错误,错误代码:"<<l_msg.error_flag<<std::endl;
    //   printf("退出系统\n");
    //   transport->close();  
    //   return 1;
    // }

    l_msg.usr.seqId=std::stoi(argv[2]);
    l_msg.usr.age=-1;
    l_msg.usr.sex=-1;
    //给user更新
    for(int i=3;i<argc;i+=2)
    {
      l_flag_field=0;
      for(int j=1;j<=data_size-1;j++)
      {
        if(strcmp(argv[i],l_str_row[j].c_str())==0)
        {
          l_flag_field=j;
          break;
        }
      }

      if(l_flag_field==0)
      {
        std::cout<<"输入字段名错误: 请输入如下字段<name> <sex> <age> <tel> <email> (<pers_desc>)"<<argv[i]<<std::endl;
        printf("退出系统\n");
        transport->close();  
        return 1;
      }
      switch (l_flag_field)
      {
        case 1:
          l_msg.usr.name=argv[i+1];
          break;
        case 2:
          l_msg.usr.sex=(std::stoi(argv[i+1])==-1?std::stoi(argv[i+1])-1:std::stoi(argv[i+1]));//若用户就输入-1 则-2
          break;
        case 3:
          l_msg.usr.age=(std::stoi(argv[i+1])==-1?std::stoi(argv[i+1])-1:std::stoi(argv[i+1]));//若用户就输入-1 则-2
        break;
        case 4:
          l_msg.usr.tel=argv[i+1];
        break;
        case 5:
          l_msg.usr.email=argv[i+1];
        break;
        case 6:
          l_msg.usr.pers_desc=argv[i+1];
        break;
        default:
          break;
      }
    }
    if(UpdataUser(client,l_msg.usr))
    {
      printf("退出系统\n");
      transport->close();  
      return 1;
    }

  }
  else //else
  {
    fprintf(stderr,"%% Usage: ""%s Find <ID> \n",argv[0]);
    fprintf(stderr,"%% Usage: ""%s Insert <name> <sex> <age> <tel> <email> (<pers_desc>)..\n",argv[0]);
    fprintf(stderr,"%% Usage: ""%s Updata <ID> <field1> <content> <field2> <content>..\n",argv[0]);
  }


  

  printf("退出系统\n");
  transport->close();   
  
  
  return 0;   
}  

consumer

#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <iostream>
#include <cstdio>
#include <ctype.h>
#include "rdkafka.h"
#include<jsoncpp/json/json.h> 

// volatile sig_atomic_t g_run = 1;

// void stop(int sig) {
//         g_run = 0;
// }


int is_printable(void *buf1, size_t size)
{
        size_t i;
        char *l_buf=(char *)buf1;//
        for(i=0;i<size;i++)
            if(!isprint((int)l_buf[i]))
                return 0;

        return 1;
}


int main(int argc, char **argv) {
        rd_kafka_t *l_rk=NULL;          //生产者实例句柄
        rd_kafka_conf_t *l_conf=NULL;   //临时配置对象
        rd_kafka_resp_err_t l_err; // librdkafka API 错误代码
        char l_errstr[512]={0};        // librdkafka API 错误缓冲区内容
        char *l_brokers=NULL;       //  broker list 
        char *l_groupid=NULL;       //组id
        char **l_topics=NULL;       //要订阅的 topic 
        int l_topic_cnt=0;          //要订阅的topic数量
        rd_kafka_topic_partition_list_t *l_subscription=NULL; 
        int i=0;

        if (argc<4) 
        {
            fprintf(stderr,"%% Usage: ""%s <broker> <group.id> <topic1> <topic2>..\n",argv[0]);
            return 1;
        }

        l_brokers=argv[1];
        l_groupid=argv[2];
        l_topics=&argv[3];
        l_topic_cnt=argc-3;


        
        l_conf = rd_kafka_conf_new();

        //配置设置ip,端口 默认9092
        if(rd_kafka_conf_set(l_conf, "bootstrap.servers", l_brokers, l_errstr,sizeof(l_errstr)) != RD_KAFKA_CONF_OK)
        {
            fprintf(stderr, "%s\n", l_errstr);
            //销毁l_conf
            rd_kafka_conf_destroy(l_conf);
            return 2;
        }

        
        //配置消费者组id
         //共享相同组id的所有消费者将加入同一个组,订阅的topic'partitions将根据partition.assignment.strategy(消费者配置属性)分配给组中的消费者。
        if(rd_kafka_conf_set(l_conf, "group.id", l_groupid, l_errstr,sizeof(l_errstr)) != RD_KAFKA_CONF_OK)
        {
            fprintf(stderr, "%s\n", l_errstr);
            rd_kafka_conf_destroy(l_conf);
            return 3;
        }
        
        // 如果一个分区之前没有提交偏移量,则auto.offset.reset策略将用于决定在分区的哪个位置开始获取消息。
        // 通过将此设置为最早,如果之前没有提交偏移量,消费者将读取分区中的所有消息。

        //earliest -> latest 
        if(rd_kafka_conf_set(l_conf, "auto.offset.reset", "latest", l_errstr,
                              sizeof(l_errstr)) != RD_KAFKA_CONF_OK) {
                fprintf(stderr, "%s\n", l_errstr);
                rd_kafka_conf_destroy(l_conf);//
                return 4;
        }

        // 创建使用者实例。实例化一个顶级对象rd_kafka_t作为基础容器,提供全局配置和共享状态
        // 注意:rd_kafka_new()拥有conf对象的所有权,在调用后应用程序不能再次引用它。
        l_rk = rd_kafka_new(RD_KAFKA_CONSUMER, l_conf, l_errstr, sizeof(l_errstr));
        
        //在分配成功的情况下 这里l_conf的配置临时配置对象其实已经被rd_kafka_new释放
        
        
        if(!l_rk)
        {
            fprintf(stderr, "%% Failed to create new consumer: %s\n",l_errstr);
            
            //没分配成功的需要 再手动释放
            rd_kafka_conf_destroy(l_conf);
            return 5;
        }
        
        //rd_kafka_conf_destroy(l_conf);这里不需要再销毁配置对象 
        l_conf=NULL; //配置对象现在由rd_kafka_t实例拥有和释放

         //重定向 rd_kafka_poll()队列到consumer_poll()队列
        rd_kafka_poll_set_consumer(l_rk);

        //将所有topic list转化为 适合kafka的格式
        l_subscription=rd_kafka_topic_partition_list_new(l_topic_cnt);
        for (i=0;i<l_topic_cnt;i++)
                rd_kafka_topic_partition_list_add(l_subscription, l_topics[i],
                                                  /* the partition is ignored
                                                   * by subscribe() */RD_KAFKA_PARTITION_UA);

        // 订阅 l_topics list
        l_err=rd_kafka_subscribe(l_rk, l_subscription);
        if (l_err)
        {//订阅出错
                
            fprintf(stderr, "%% Failed to subscribe to %d l_topics: %s\n",l_subscription->cnt,rd_kafka_err2str(l_err));
            rd_kafka_topic_partition_list_destroy(l_subscription);
            rd_kafka_destroy(l_rk);
            return 6;
        }

        fprintf(stderr,"%% Subscribed to %d topic(s),waiting for rebalance and messages...\n",l_subscription->cnt);

        rd_kafka_topic_partition_list_destroy(l_subscription);//释放l_topics list使用的所有资源和它自己

        // /* Signal handler for clean shutdown */
        // signal(SIGINT, stop);

        rd_kafka_message_t *l_rkm;
        Json::Reader l_reader(Json::Features::strictMode());
        Json::Value l_root; 
        while (1)
        {
            
            l_rkm = rd_kafka_consumer_poll(l_rk, 100);//从consumer_poll读取信息
            if (!l_rkm)
                continue; //超时就重新尝试
            //返回错误的信息
            if (l_rkm->err) 
            {
                fprintf(stderr, "%% Consumer error: %s\n",
                        rd_kafka_message_errstr(l_rkm));
                rd_kafka_message_destroy(l_rkm);
                continue;
            }


            //正确就如下
            //打印 字段 分区 offset等信息
            printf("Message on %s [%" PRId32 "] at offset %" PRId64 ":\n",
                rd_kafka_topic_name(l_rkm->rkt), l_rkm->partition,
                l_rkm->offset);

            // /* Print the message key. */
            // if (l_rkm->key && is_printable(l_rkm->key, l_rkm->key_len))
            //         printf(" Key: %.*s\n", (int)l_rkm->key_len,
            //                (const char *)l_rkm->key);
            // else if (l_rkm->key)
            //         printf(" Key: (%d bytes)\n", (int)l_rkm->key_len);
            //打印信息字段

            
            //--------------进行反序列化操作 ---------------

            
            if(l_reader.parse((const char *)l_rkm->payload,l_root)==0)
            {   
                std::cout<<"reader.parse赋值错误!"<<std::endl;
                rd_kafka_message_destroy(l_rkm);
                continue;
            }
            std::cout<<"新得到的数据如下"<<std::endl;
            if(l_root["ID"]!=Json::nullValue&&l_root["ID"].isInt())
            {
                std::cout<<"ID:"<<l_root["ID"]<<std::endl;
            }
            if(l_root["name"]!=Json::nullValue&&l_root["name"].isString())
            {
                std::cout<<"name:"<<l_root["name"]<<std::endl;
            }
            if(l_root["sex"]!=Json::nullValue&&l_root["sex"].isInt())
            {
                std::cout<<"sex:"<<l_root["sex"]<<std::endl;
            }
            if(l_root["age"]!=Json::nullValue&&l_root["age"].isInt())
            {
                std::cout<<"age:"<<l_root["age"]<<std::endl;
            }
            if(l_root["tel"]!=Json::nullValue&&l_root["tel"].isString())
            {
                std::cout<<"tel:"<<l_root["tel"]<<std::endl;
            }
            if(l_root["email"]!=Json::nullValue&&l_root["email"].isString())
            {
                std::cout<<"email:"<<l_root["email"]<<std::endl;
            }
            if(l_root["pers_desc"]!=Json::nullValue&&l_root["pers_desc"].isString())
            {
                std::cout<<"pers_desc:"<<l_root["pers_desc"]<<std::endl;
            }
            

            // printf("ID:%d\nID:%d\n")
            //--------------进行反序列化操作 ---------------


            if (l_rkm->payload && is_printable(l_rkm->payload, l_rkm->len))
                printf(" Value: %.*s\n", (int)l_rkm->len,(const char *)l_rkm->payload);
            else if (l_rkm->payload)
                printf(" Value: (%d bytes)\n", (int)l_rkm->len);

            rd_kafka_message_destroy(l_rkm);
        }


        //关闭消费者 离开组
        fprintf(stderr, "%% Closing consumer\n");
        rd_kafka_consumer_close(l_rk);

        //销毁消费者
        rd_kafka_destroy(l_rk);

        return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值