使用mysql_udf与curl库完成http_post通信模块示例

使用mysql_udf与curl库完成http_post通信模块(mysql_udf,multi_curl,http,post)

这个模块其目前主要用于xoyo江湖的sns与kingsoft_xoyo自主研发的TCSQL数据库做数据同步,当有feed插入sns数据库,使用触 发器调用该模块,向tcsql数据库发送同步数据。也可以使用该模块与其它使用socket接口的数据库或程序做转发与同步。
http_post模块主要使用mysql_udf接口,与curl库两部分技术。
mysql_udf是mysql为c语言提供的一个接口,通过这个接口,用户可以自定义mysql的函数,通过调用这些mysql函数,调用相应的c语言 模块来执行特定功能,实现mysql数据与外部应用的交互。curl库是一个比较常用的应用层网络协议库,主要用到的是其中的curl_multi异步通 信api,用来进行网络传输。
首先参考mysql官方提供的udf_example.c文件,建立3个主要的接口函数,分别是初始化函数,执行函数与析构函数。

代码如下:

//args是sql语句传回的参数,message是返回出错信息使用这些都是规定好的。
my_bool http_post_init(UDF_INIT *initid, UDF_ARGS *args, char *message);
//主函数体
longlong http_post(UDF_INIT *initid, UDF_ARGS *args, char *is_null,char *error);
//析构函数体
void http_post_deinit(UDF_INIT *initid);
//args是sql语句传回的参数,message是返回出错信息,使用这些都是规定好的。
//初始化函数体 my_bool http_post_init(UDF_INIT *initid, UDF_ARGS *args, char *message);
//主函数体 longlong http_post(UDF_INIT *initid, UDF_ARGS *args, char *is_null,char *error);
//析构函数体 void http_post_deinit(UDF_INIT *initid);

在mysql_udf接口中,主函数体中是不允许使用new或malloc动态分配内存,所以如果需要申请内存空间,必须用xxxx_init()函数申 请并将申请的地址赋给initid->ptr指针,然后在主函数体中使用,并在xxxx_deinit析构函数体中释放。另外对于 mysql_udf接口的调用好像当并发量超过一定程度,如果是使用动态分配内存,会出现double free的错误,为了避免这个错误,所以在我的程序里使用静态空间与动态申请空间相结合的方式,这样如果数据较小,并发量较大,不会出现double free错误。对于静态申请空间,最大约在160000~170000byte左右,我这里使用的160000,当mysql传送的数据大于这个数的时 候,才动态申请内存。初始化函数体如下:

代码如下:

my_bool http_post_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
    {
      if (args->arg_count != 2)
      {
        strcpy(message,"Wrong arguments to http_post; ");
        return 1;
      }

if(args->arg_count == 2 && args->args[1]!=NULL)
      {
            int flexibleLength = strlen(args->args[1]);

if(flexibleLength > 160000)
        {
            int allocLength = 200 + flexibleLength;
            if (!(initid->ptr=(char*) malloc(allocLength) ) )
            {
                    strcpy(message,"Couldn't allocate memory in http_post_init");
                    return 1;
            }
            return 0;
        }
        else
        {
            initid->ptr=NULL;
        }

}
       return 0;

}

其中http_post_init需要返回my_bool型。这个函数目的是给用户提供一个方式,检验由mysql参数传进来的数据是否正确,如果正确则 返回0,则mysql会自动调用定义的主函数,如果返回1,则mysql打印message信息退出,不会调用主函数。所以在设定返回值的时候一定注意。

主函数如下:

代码如下:

longlong http_post( UDF_INIT *initid, UDF_ARGS *args,
                char *is_null __attribute__((unused)),
                char *error __attribute__((unused)))
{
    char* sendBuffer=NULL;
    CURL *curl;
    CURLM *multi_handle;
    int still_running;
    int times=0;//try times if select false
        int TRY_TIMES=25;
    struct timeval timeout;//set a suitable timeout to play around with
    timeout.tv_sec = 0;
    timeout.tv_usec = 100000;

char sendArray[160000] = "\0";//can not move this into the if
    if(initid->ptr == NULL)
    {
        //char sendArray[160000] = "\0";//error
        sendBuffer=sendArray;
    }
    else
    {
        sendBuffer = initid->ptr;
        TRY_TIMES=100;
    }

strcpy(sendBuffer,args->args[1]);
    curl = curl_easy_init();
    multi_handle = curl_multi_init();
    if(curl && multi_handle)
    {
        /* what URL that receives this POST */
        curl_easy_setopt(curl, CURLOPT_URL,args->args[0]);
        curl_easy_setopt(curl, CURLOPT_HTTPPOST, 1);
        curl_easy_setopt(curl,CURLOPT_POSTFIELDS,sendBuffer);
        curl_multi_add_handle(multi_handle, curl);
        while(CURLM_CALL_MULTI_PERFORM == curl_multi_perform(multi_handle,\ &still_running));
        while(still_running && times< TRY_TIMES)
         {
              int rc;      //select() return code
              int maxfd;
              fd_set fdread;
              fd_set fdwrite;
              fd_set fdexcep;
              FD_ZERO(&fdread);
              FD_ZERO(&fdwrite);
              FD_ZERO(&fdexcep);   //get file descriptors from the transfers
             curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep,\ &maxfd);
             rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
             switch(rc)
            {
                case -1://select error
                      break;
                case 0:
                default:        // timeout
                     while(CURLM_CALL_MULTI_PERFORM !== curl_multi_perform(multi_handle, &still_running));
                     break;
             }
                times++;
         }//end while
       curl_multi_remove_handle(multi_handle,curl);
       curl_multi_cleanup(multi_handle);//always cleanup
       curl_easy_cleanup(curl);
       if(times>=TRY_TIMES)
       {
            return 1;
       }
        return 0;
  }//end if
  return 1;
}

在主函数中,主要使用curl库进行通信,curl库分成3部分,easy是同步模式,multi是异步模式,share模式是多线程共享数据的模式。

对于easy发送完数据后,会阻塞等待服务器的response,如果没 有返回,就会一直阻塞,当然可以设置一个timeout,但如果这个时间设小了,easy发送大数据的时候就会中断,设太大了影响时间效率,另外当接收端 不发送response的时候,easy库即使发送完了数据,也会阻塞等待,有些时候对于发送端来讲不需要等待接收端的respons,当发送完毕就可以 结束了,这个时候easy就不适用。所以最后选择multi库。

如程序所示,首先得初始化,并设置easy句柄为post模式,指定需要post的数据,如下:

curl = curl_easy_init();

multi_handle = curl_multi_init();

curl_easy_setopt(curl, CURLOPT_URL,args->args[0]);

curl_easy_setopt(curl, CURLOPT_HTTPPOST, 1);

curl_easy_setopt(curl,CURLOPT_POSTFIELDS,sendBuffer);

由于要使用multi模式,必须也要初始化一个easy模式,并将这个easy模式的句柄放入所谓的multi函数执行栈:

curl_multi_add_handle(multi_handle, curl);

使用curl_multi_perform(multi_handle, &still_running),来进行异步传输,但如果该函数返回的不是CURLM_CALL_MULTI_PERFORM,则需要重新执行。直到循环while(CURLM_CALL_MULTI_PERFORM == curl_multi_perform(multi_handle, &still_running));结束。此时如果刚才函数体中的still_running被置为1,表明连接建立,正在发送数据。需要配合select机制来进行数据发送。

函数 curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);会将最大的描述符写入maxfd,

然后用select进行等待:rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);

最后如果select返回值不为-1(error)0(timeout)时候再次进行异步传输,即执行curl_multi_perform函数,直到

still_running为0,程序结束退出。

这里设置了一个最大执行次数的限制,如果服务器出现了问题,不能发送response,则still_running不会变为0,程序会死循环,

所以,设置一个最大循环次数TRY_TIMES,防止这种情况发生。但是这个次数设小了,数据可能没有发送完,就退出了,如设置太大了,程序发送完了,服务器没有response就会多执行多余循环。所以这个TRY_TIMES需要根据数据的大小和网络状况来设置,比正常

传输数据的次数略长。这里我小数据的时候循环设次数25,大数据循环设为100.

最后是析构函数体:

代码如下:

void http_post_deinit(UDF_INIT *initid)
{
     if (initid!=NULL && initid->ptr!=NULL)
        {
            free(initid->ptr);
            initid->ptr = NULL;
        }
}

将初始化函数设置的内存释放。

编译执行过程如下:

将程序保存为http_post.c编译如下(请根据机器上的mysql路径进行调整):


代码如下:

gcc -wall -I/usr/local/webserver/mysql/include/mysql/ -shared http_post.c -o http_post.so -fPIC
//使用mysql提供的头文件生成动态链接库
cp -f http_post.so /usr/local/webserver/mysql/lib/mysql/plugin/http_post.so
//将生成的.so文件放入mysql的plugin文件夹下
//进入mysql对动态链接库中的函数进行安装
cd /usr/local/webserver/mysql/bin/mysql
./mysql
//在mysql命令行下输入如下命令:
mysql> DROP FUNCTION IF EXISTS http_post;
//其目的是如果系统内安装了同名函数先进性drop。
mysql> CREATE FUNCTION http_post RETURNS INTEGER SONAME ‘http_post.so';
//生成http_post函数,并指明调用来源是http_post.so。
//最后调用函数,其目的是向指定ip和端口发送post数据。调用前先打开指定ip主机上的网络调试助手,并监听3888端。
mysql> select http_post(‘testpost.com/index.php','sfasfa');

在网络助手中可以看到如下结果:

(0)

相关推荐

  • MySQL 的模块不能安装的解决方法

    我们最常用的 DBD::mysql 模块,我发现是难住很多人的地方.因为安装老是失败,下面我介绍一下解决方法,比如我使用 cpanm 安装,有时就出 /home/nue2501j/work/DBD-mysql-4.010/blib/arch/auto/DBD/mysql/mysql.so: undefined symbol: DBIc_TRACE_LEVEL at /usr/lib/perl5/5.8.5/i386-linux-thread-multi/DynaLoader.pm line 23

  • Nodejs使用mysql模块之获得更新和删除影响的行数的方法

    在mysql中直接进行这样的判断的方法是使用 row_count(), 这一条语句要紧跟着你执行的sql语句后面. 而Nodejs的i/o都是异步的于是这就产生了一个问题, 不太好判断 row_count()到底是哪句sql执行的结果. 粗略的扫了一眼文档, 文档中并没有描述这个问题. 本想函数嵌套来达到同步的效果的, 却无意发现在执行sql对应的异步函数中的参数中有  affectedRows字段, 经测试, 这货就是 row_count()的结果.实例: 复制代码 代码如下: var cmd

  • Python下的Mysql模块MySQLdb安装详解

    默认情况下,MySQLdb包是没有安装的,不信? 看到类似下面的代码你就信了. 复制代码 代码如下: -bash-3.2# /usr/local/python2.7.3/bin/python get_cnblogs_news.py Traceback (most recent call last):  File "get_cnblogs_news.py", line 9, in <module>    import MySQLdbImportError: No module

  • 在Python安装MySQL支持模块的方法

    刚安装Python的MySQL支持库,一口气遇到了一连串的问题,好在Google一下,均解决.遂记录下,备忘. 1.下载Python的MySQL支持库 地址:http://sourceforge.net/projects/mysql-python/ 2.解压编译 python setup.py install 遇到第一个问题: _mysql.c:44:23: error: my_config.h: No such file or directory 解决办法,编辑setup_posix.py文件

  • python中MySQLdb模块用法实例

    本文实例讲述了python中MySQLdb模块用法.分享给大家供大家参考.具体用法分析如下: MySQLdb其实有点像php或asp中连接数据库的一个模式了,只是MySQLdb是针对mysql连接了接口,我们可以在python中连接MySQLdb来实现数据的各种操作. python连接mysql的方案有oursql.PyMySQL. myconnpy.MySQL Connector 等,不过本篇要说的确是另外一个类库MySQLdb,MySQLdb 是用于Python链接Mysql数据库的接口,它

  • 使用mysql_udf与curl库完成http_post通信模块示例

    使用mysql_udf与curl库完成http_post通信模块(mysql_udf,multi_curl,http,post) 这个模块其目前主要用于xoyo江湖的sns与kingsoft_xoyo自主研发的TCSQL数据库做数据同步,当有feed插入sns数据库,使用触 发器调用该模块,向tcsql数据库发送同步数据.也可以使用该模块与其它使用socket接口的数据库或程序做转发与同步.http_post模块主要使用mysql_udf接口,与curl库两部分技术.mysql_udf是mysq

  • PHP的cURL库简介及使用示例

    使用PHP的cURL库可以简单和有效地去抓网页.你只需要运行一个脚本,然后分析一下你所抓取的网页,然后就可以以程序的方式得到你想要的数据了.无论是你想从从一个链接上取部分数据,或是取一个XML文件并把其导入数据库,那怕就是简单的获取网页内容,cURL 是一个功能强大的PHP库. PHP中的CURL函数库(Client URL Library Function) 复制代码 代码如下: curl_close - 关闭一个curl会话 curl_copy_handle - 拷贝一个curl连接资源的所

  • PHP的cURL库功能简介 抓取网页、POST数据及其他

    无论是你想从从一个链接上取部分数据,或是取一个XML文件并把其导入数据库,那怕就是简单的获取网页内容,反应釜cURL 是一个功能强大的PHP库.本文主要讲述如果使用这个PHP库. 启用 cURL 设置 首先,我们得先要确定我们的PHP是否开启了这个库,你可以通过使用php_info()函数来得到这一信息. 复制代码 代码如下: <?php phpinfo(); ?> 如果你可以在网页上看到下面的输出,那么表示cURL库已被开启. 如果你看到的话,那么你需要设置你的PHP并开启这个库.如果你是在

  • Python利用turtle库绘制彩虹代码示例

    语言:Python IDE:Python.IDE 需求 做出彩虹效果 颜色空间 RGB模型:光的三原色,共同决定色相 HSB/HSV模型:H色彩,S深浅,B饱和度,H决定色相 需要将HSB模型转换为RGB模型 代码示例: #-*- coding:utf-8 –*- from turtle import * def HSB2RGB(hues): hues = hues * 3.59 #100转成359范围 rgb=[0.0,0.0,0.0] i = int(hues/60)%6 f = hues/

  • Go语言非main包编译为静态库并使用的示例代码

    Go语言项目中基本上都是以源码的形式提供包,对公司或者企业而言,如果想要隐藏关键代码怎么办? 我们可以将Go的非main包编译为静态库提供给用户使用.下面以Windows为例,介绍一下如何将Go的非main包编译为静态库,用户又将如何使用. 一.环境 笔者使用的VSCode,在终端中查看Go的环境变量,主要看GOROOT以及GOPATH,笔者的GOROOT在C:\Program Files\Go. 二.常规工程 在任意地方新建一个目录,假设为test,然后使用VSCode打开.我们创建如下3个工

  • mysql远程跨库联合查询的示例

    目录 情况一2个库在同一台物理主机 情况二2个库不在同一台物理主机(即2个库分别在不同的物理主机) 注意:myemployees库和shoppingCart库在同一台物理主机,如果不在同一台物理主机该怎么办呢?下面我会介绍到. 情况一2个库在同一台物理主机 联合查询(不同的2个库,myemployees库和shoppingCart库),这2个库在同一台物理主机上,都在我本机. #联合查询(不同的2个库,myemployees库和shoppingCart库) SELECT emp01.`emplo

  • Python机器学习库scikit-learn入门开发示例

    目录 1.数据采集和标记 2.特征选择 3.数据清洗 4.模型选择 5.模型训练 6.模型测试 7.模型保存与加载 8.实例 数据采集和标记 特征选择 模型训练 模型测试 模型保存与加载 1.数据采集和标记 先采集数据,再对数据进行标记.其中采集数据要就有代表性,以确保最终训练出来模型的准确性. 2.特征选择 选择特征的直观方法:直接使用图片的每个像素点作为一个特征. 数据保存为样本个数×特征个数格式的array对象.scikit-learn使用Numpy的array对象来表示数据,所有的图片数

  • Vue axios库发送请求的示例介绍

    目录 1.什么是axios 2.axios请求图例 3.使用 async 和 await 配合 axios 发起请求 4.使用解构赋值 5.使用 axios.get() axios.post() 来简化请求过程 1.什么是axios Axios,是一个基于 promise 的网络请求库,作用于node.js和浏览器中,它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中). 使用方式如下: <script> //调用axios方法得到的返回值是 promise 对象

  • go语言csrf库使用实现原理示例解析

    目录 引言 csrf小档案 一.CSRF及其实现原理 CSRF攻击示例 二.如何预防 三.CSRF包的使用及实现原理 csrf包的安装 基本使用 使用net/http包启动的服务 echo框架下使用csrf包 gin框架下使用csrf包 beego框架下使用csrf包 实现原理 csrf结构体 csrf包的工作流程 为什么GET.HEAD.OPTIONS.TRACE的请求方法不需要token验证 总结 引言 今天给大家推荐的是web应用安全防护方面的一个包:csrf.该包为Go web应用中常见

  • Go编程库Sync.Pool用法示例详解

    目录 场景 用法 创建 GET & PUT 优化 Log 函数 性能测试 场景 go 如果频繁地创建.销毁对象(比如 http 服务的 json 对象,日志内容等),会对 GC 造成压力.比如下面的 Log 函数,在高并发情况下,需要频繁地创建和销毁 buffer. func Log(w io.Writer, key, val string) { b := new(bytes.Buffer) // 按一定的格式打印日志,这一段不是重点 b.WriteString(time.Now().UTC()

随机推荐