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个主要的接口函数,分别是初始化函数,执行函数与析构函数。

  1. //args是sql语句传回的参数,message是返回出错信息使用这些都是规定好的。
  2. my_bool http_post_init(UDF_INIT *initid, UDF_ARGS *args, char *message);
  3. //主函数体
  4. longlong http_post(UDF_INIT *initid, UDF_ARGS *args, char *is_null,char *error);
  5. //析构函数体
  6. 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或melloc动态分配内存,所以如果需要申请内存空间,必须用xxxx_init()函数申 请并将申请的地址赋给initid->ptr指针,然后在主函数体中使用,并在xxxx_deinit析构函数体中释放。另外对于 mysql_udf接口的调用好像当并发量超过一定程度,如果是使用动态分配内存,会出现double free的错误,为了避免这个错误,所以在我的程序里使用静态空间与动态申请空间相结合的方式,这样如果数据较小,并发量较大,不会出现double free错误。对于静态申请空间,最大约在160000~170000byte左右,我这里使用的160000,当mysql传送的数据大于这个数的时 候,才动态申请内存。初始化函数体如下:

  1. my_bool http_post_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
  2. {
  3. if (args->arg_count != 2)
  4. {
  5. strcpy(message,"Wrong arguments to http_post; ");
  6. return 1;
  7. }
  8. if(args->arg_count == 2 && args->args[1]!=NULL)
  9. {
  10. int flexibleLength = strlen(args->args[1]);
  11. if(flexibleLength > 160000)
  12. {
  13. int allocLength = 200 + flexibleLength;
  14. if (!(initid->ptr=(char*) malloc(allocLength) ) )
  15. {
  16. strcpy(message,"Couldn't allocate memory in http_post_init");
  17. return 1;
  18. }
  19. return 0;
  20. }
  21. else
  22. {
  23. initid->ptr=NULL;
  24. }
  25. }
  26. return 0;
  27. }

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

主函数如下:

  1. longlong http_post( UDF_INIT *initid, UDF_ARGS *args,
  2. char *is_null __attribute__((unused)),
  3. char *error __attribute__((unused)))
  4. {
  5. char* sendBuffer=NULL;
  6. CURL *curl;
  7. CURLM *multi_handle;
  8. int still_running;
  9. int times=0;//try times if select false
  10. int TRY_TIMES=25;
  11. struct timeval timeout;//set a suitable timeout to play around with
  12. timeout.tv_sec = 0;
  13. timeout.tv_usec = 100000;
  14. char sendArray[160000] = "\0";//can not move this into the if
  15. if(initid->ptr == NULL)
  16. {
  17. //char sendArray[160000] = "\0";//error
  18. sendBuffer=sendArray;
  19. }
  20. else
  21. {
  22. sendBuffer = initid->ptr;
  23. TRY_TIMES=100;
  24. }
  25. strcpy(sendBuffer,args->args[1]);
  26. curl = curl_easy_init();
  27. multi_handle = curl_multi_init();
  28. if(curl && multi_handle)
  29. {
  30. /* what URL that receives this POST */
  31. curl_easy_setopt(curl, CURLOPT_URL,args->args[0]);
  32. curl_easy_setopt(curl, CURLOPT_HTTPPOST, 1);
  33. curl_easy_setopt(curl,CURLOPT_POSTFIELDS,sendBuffer);
  34. curl_multi_add_handle(multi_handle, curl);
  35. while(CURLM_CALL_MULTI_PERFORM == curl_multi_perform(multi_handle,\ &still_running));
  36. while(still_running && times< TRY_TIMES)
  37. {
  38. int rc;      //select() return code
  39. int maxfd;
  40. fd_set fdread;
  41. fd_set fdwrite;
  42. fd_set fdexcep;
  43. FD_ZERO(&fdread);
  44. FD_ZERO(&fdwrite);
  45. FD_ZERO(&fdexcep);
  46. //get file descriptors from the transfers
  47. curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep,\ &maxfd);
  48. rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
  49. switch(rc)
  50. {
  51. case -1://select error
  52. break;
  53. case 0:
  54. default:        // timeout
  55. while(CURLM_CALL_MULTI_PERFORM ==\ curl_multi_perform(multi_handle, &still_running));
  56. break;
  57. }
  58. times++;
  59. }//end while
  60. curl_multi_remove_handle(multi_handle,curl);
  61. curl_multi_cleanup(multi_handle);//always cleanup
  62. curl_easy_cleanup(curl);
  63. if(times>=TRY_TIMES)
  64. {
  65. return 1;
  66. }
  67. return 0;
  68. }//end if
  69. return 1;
  70. }

在主函数中,主要使用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.

最后是析构函数体:

  1. void http_post_deinit(UDF_INIT *initid)
  2. {
  3. if (initid!=NULL && initid->ptr!=NULL)
  4. {
  5. free(initid->ptr);
  6. initid->ptr = NULL;
  7. }
  8. }

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

编译执行过程如下:

/将程序保存为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');

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

HTTP_POST———使用mysql_udf与curl库完成http_post通信模块(mysql_udf,multi_curl,http,post)...相关推荐

  1. PHP 的 cURL库快速入门文档

    作者:Burak Guzel 原文链接:http://net.tutsplus.com/tutorials/php/techniques-and-resources-for-mastering-cur ...

  2. 采用curl库在PHP程序之间传递数组

    最近在工作中遇到一个问题:a.php程序需要将接收到的数据同时写到"线上运行的正式数据库"和"进行开发调试的测试数据库".而测试数据库可能经 常会面临对表结构. ...

  3. php中的CURL库

    CURL是利用URL语法在命令行方式下工作的开源文件传输工具. PHP通过默认支持CURL扩展库,可以实现开发中常见的传输功能: 实现远程获取和采集内容 实现PHP网页版的FTP上传和下载 实现模拟登 ...

  4. php curl 请求失败,PHP CURL库之GET、POST数据大小限制导致请求失败解决方案

    背景概述: 我有一个脚本,里面定义了一个方法,方法里面是一个CURL GET的封装.在使用过程中一起正常.突然发现有一天一直报失败警报.于是,我去查看代码,发现没有任何问题.最后通过百度,发现这个PH ...

  5. linux 6.5升级nss,centos6.5 - centos 6.5系统PHP环境下的CURL库的SSL Version默认为NSS,怎么变更为OpenSSL?...

    现在要求PHP的环境支持TSL1.2和SHA-256,php的CURL库升级到curl 7.35.0,openssl升级到OpenSSL/1.0.1f,但是通过配资文件查看curl的SSL Versi ...

  6. linux链接curl库,Linux利用curl库快速开发http应用

    熟悉Linux系统的人不可能不知道curl的鼎鼎大名吧?curl是将http请求封装的相当好的库,详见http://curl.haxx.se/,我们可以利用curl实现快速http请求的开发. 在Li ...

  7. mac下编译curl库(处理https的问题)

    一.下载curl的编译源码 下载链接: https://github.com/curl/curl 点击此处 下载该release版本 解压后命令行进入该页面 执行如下命令 ./configure -- ...

  8. iOS编译cURL库并链接darwinssl,zlib,c-ares库操作步骤

    官方更新文档(原代码里change文件) - Secure Transport: no more "darwinssl"Everyone calls it Secure Trans ...

  9. windows下C语言使用curl库访问HTTP下载文件

    一.前言 cURL是一个利用URL语法在命令行下工作的文件传输工具,1997年首次发行.它支持文件上传和下载,所以是综合传输工具,但按传统,习惯称cURL为下载工具.cURL还包含了用于程序开发的li ...

最新文章

  1. 分享笔趣阁、宜搜等小说免费API接口
  2. jsp的内置对象Exception
  3. 用golang完成tcp协议传输
  4. mysql定期删除数据_mysql数据库如何实现定期删除数据库一些东西
  5. python怎么爬虎牙_使用python爬虫框架scrapy抓取虎牙主播数据
  6. kettle 内存设置_【转】kettle 的内存设置及输出日志的时间类型
  7. 2017-2018年Scrum状态调查报告
  8. SpringMVC响应使用案例(带数据页面跳转,快捷访问路径,返回json数据)
  9. 打破硬件边界,华为EMUI分布式技术如何连接万物
  10. 微信支付宝神仙打架,谁家健康码能一统天下?
  11. Altium designer原理图检查(编译检查)
  12. anki填空题卡片模板
  13. LeetCode 108. 将有序数组转换为二叉搜索树
  14. 内存泄露和LeakCanary的故事
  15. aardio - 利用bitLock快速读写图片颜色值
  16. zk选举机制和分布式一致性原理
  17. 高中知识复习与拓展——数列的求和
  18. 数控木雕机器雕工艺品
  19. ‘今年找工作太难了,真的是卷到我想哭!’,一个疫情就业季下的毕业生艰辛IT求职道路上的经验分享!见识入社会的不容易!
  20. 临床执业助理医师(综合练习)题库【9】

热门文章

  1. 关于去苹果服务器验证充值的一些看法
  2. mysql 不能添加外键 1215_MySQL错误1215:无法添加外键约束
  3. halcon 相似度_Halcon分类函数,shape模型
  4. Android在代码中设置drawableLeft(Right/Top/Bottom)
  5. 一个人幸运的前提,是他有能力改变自己
  6. 传腾讯人事大地震 马化腾将重整公司架构
  7. linux下gdb单步调试
  8. 分析一段H264视频数据
  9. Thymeleaf 简介、教程
  10. Postman用法说明