Apache Traffic Server插件开发手记(二)

目录

一、原理分析

二、主要函数

三、代码实现

四、测试过程

-----------------------------------------------------------------------------------

一、原理分析

ATS是做服务器最好的选择,服务器最重要的两件事就是接收处理用户的请求并发送反馈的信息给用户。这个帖子的目的在于介绍几个接收、发送数据的处理函数的使用。

先来看下,整体的结构

装逼的功能:我希望在ATS的基础上,开发一个插件,这个插件能够接收用户的连接请求,同时能发送数据给用户。

实际的功能:这不就是个Socket的发送、接收功能吗。。。

socket归socket,不同的是ATS如何利用Socket+Event+Continuations这三种机制实现这一最基本的功能。


二、主要函数

ATS中插件是运行在continuations(协程)的机制上的,可以说一个插件就是一个或多个continuations组成的,所以先介绍的两个函数是continuations的创建与销毁

TSCont TSContCreate(TSEventFunc funcp, TSMutex mutexp);

###funcp是指continuations用来处理事件的主要函数,创建函数是用来创建并绑定事件处理函数

void TSContDestroy(TSCont contp);

第二,之前介绍到,数据会随着continuations的挂起而保存,有点像中断,接下来的这个函数能将要保存的数据与协程绑定起来

void TSContDataSet(TSCont contp, void *data);

###void *data即要保存的数据,可以是类、结构体等等。

void *TSContDataGet(TSCont contp);

###获取dataset所保存的数据

TSAction TSNetAccept(TSCont contp, int port, int domain, int accept_threads);

###绑定并监听端口,第3个参数domain的值可参考socket的值,一般是设置AF_INET,最后一个是回调函数的线程ID,可设置回调可不设置

int TSContCall(TSCont contp, TSEvent event, void *edata);

###回调与cont绑定的eventhandler函数,第二个参数为事件的代号

第三,接收有关数据的读、写操作的函数


当请求接入后,TSNetAccept的data是一个TSVconn的类,也就是通过这个类在两者之间建立联系

数据的读取(写入)需要通过IObuffer、IOBufferReader、TSIOBufferBlock相互配合才能将数据从buffer中读取(写入)出来。

主要的几个函数(以读取函数为例)

TSVIO TSVConnRead(TSVConn connp, TSCont contp, TSIOBuffer bufp, int64_t nbytes);

###将连接发送给协程数据关联至IOBuffer中,同时nbytes设置IObuffer的空间大小,当有数据写入时,触发该协程的回调事件函数。

const char *TSIOBufferBlockReadStart(TSIOBufferBlock blockp, TSIOBufferReader readerp, int64_t *avail);

###将数据读出,avail表示用于存放读书数据字节数的地址

TSIOBufferBlock TSIOBufferBlockNext(TSIOBufferBlock blockp);

###每次读取的数据有限,通过block的方式多次读取,这里的block相当于指针一样,随着读取不断向后移动

三、代码实现

最后,是通过C++编写一个小插件。

首先,我们需要将这个插件挂载到ATS上,因此需要声明TSPluginInit函数,由于是在CPP的文件里写的,因此需要声明用gcc来编译这个入口函数就有如下代码,同时我们再此将插件功能进行初始化

extern "C" void TSPluginInit (int argc, const char *argv[]);

void TSPluginInit (int argc, const char *argv[])

{

        TSPluginRegistrationInfo info;

        info.plugin_name = "hello-world";

        info.vendor_name = "MyCompany";

        info.support_email = "ts-api-support@MyCompany.com";

        printf("开始运行插件\n");

        if (TSPluginRegister(&info) != TS_SUCCESS) {

        TSError("注册失败2");

        }

        helloinit();

}

在初始化函数中,需要做的有四件事:创建continuations、绑定数据存放、绑定事件处理的主函数、绑定端口并监听。在这之前,先创建一用于保存数据的机构体(或者类)

typedef struct {

        TSIOBuffer bufp;

        TSIOBuffer out_bufp;

        TSIOBufferReader readerp;

        TSIOBufferReader out_readerp;

        TSVConn write_vconnp;

        TSVConn read_vconnp;

        TSVIO read_vio;

        TSVIO write_vio;

} CacheVConnStruct;

于是,helloinit、以及主事件处理函数的代码为

int helloinit()

{

printf("绑定端口\n");

hello_appcep_cont_=TSContCreate(HelloAccpetHandler, TSMutexCreate());

printf("cont是:%d\n",hello_appcep_cont_);

CacheVConnStruct *cache_vconn = (CacheVConnStruct *)TSmalloc(sizeof(CacheVConnStruct));

TSContDataSet( hello_appcep_cont_, cache_vconn);

TSNetAccept(hello_appcep_cont_, 8899,AF_INET ,1);

}

int HelloAccpetHandler(TSCont cont,TSEvent event,void *data)

{

printf("cont是:%d\n",cont);

printf("回调连接的事件为:%d\n",event);

switch ((int)event)

{

case TS_EVENT_NET_ACCEPT:

printf("连接正常接入\n");

ReadFromVConn(cont,data);

break;

case TS_EVENT_VCONN_READ_COMPLETE:

printf("读取完毕\n");

//HandlerReadComplete(cont,data);

break;

case TS_EVENT_VCONN_READ_READY:

printf("buffer尚未读满\n");

HandlerReadComplete(cont,data);

}

return 1;

}

接下来,需要等待连接接入,为此我们需要写个客户端的脚本,方便采用python编写

# -*- coding: utf-8 -*-

import socket

import sys

datatosend="GET / HTTP/1.1\r\nHost: 192.168.31.138:8899\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36\r\nUpgrade-Insecure-Requests: 1\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: zh-CN,zh;q=0.9\r\n\r\n"

TCP_IP = '192.168.31.138'

TCP_PORT = 8899

ADDR=(TCP_IP,TCP_PORT)

print ADDR

try:

    #create an AF_INET, STREAM socket (TCP)

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

except socket.error, msg:

    print 'Error code: ' + str(msg[0]) + ' , Error message : ' + msg[1]

    sys.exit();

    #s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    print 'Socket Created'

try:

    s.connect((TCP_IP,TCP_PORT))

except socket.error , msg:

    print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]

    sys.exit()

s.send(datatosend)

i=i+1

当客户端连接服务器后,ATS会触发事件函数202表接受了请求,建立连接,这个时候就绑定IOBuffer并声明buffer的空间,同时我直接发送给对端一个链接的信号,代码如下:

void ReadFromVConn(TSCont cont,void *data)

{

CacheVConnStruct *recv_sm=(CacheVConnStruct*)TSContDataGet( cont);

recv_sm->bufp =TSIOBufferCreate();

recv_sm->read_vconnp=(TSVConn)data;

TSVConnRead((TSVConn)data, cont, recv_sm->bufp, 500);

TSVConn input_request=(TSVConn)data;

printf("%s\n",input_request);

TSIOBuffer input_bufp=TSIOBufferCreate();

TSIOBufferReader input_bufp_reader=TSIOBufferReaderAlloc(input_bufp);

TSIOBuffer output_bufp=TSIOBufferCreate();

TSIOBufferReader output_bufp_reader=TSIOBufferReaderAlloc(output_bufp);

/////////发送数据部分

TSIOBufferBlock blockp;

char *ptr_block;

int64_t avail;

blockp    = TSIOBufferStart(output_bufp);

ptr_block = TSIOBufferBlockWriteStart(blockp, &avail);

memcpy(ptr_block, "i love ats", 11);

TSIOBufferProduce(output_bufp, 11);

TSVIO write_io=TSVConnWrite(input_request, cont,output_bufp_reader, 11);

}

当IOBuffer中有数据的时候就会触发事件102或者103,102表示发送过来的数据尚未填满buffer,103表示buffer已经满了。那么将数据读出的代码如下:

int HandlerReadComplete(TSCont cont,void *data)

{

CacheVConnStruct *cache_vconn=(CacheVConnStruct*)TSContDataGet( cont);

cache_vconn->readerp=TSIOBufferReaderAlloc(cache_vconn->bufp);

int avail=TSIOBufferReaderAvail(cache_vconn->readerp);

printf("共有%d个数据\n",avail);

if(avail>0)

{

string str;

const char *buffer_temp=new char[1024];

int consumed=0;

TSIOBufferBlock block = TSIOBufferReaderStart(cache_vconn->readerp);

int64_t data_len;

str.reserve(avail + 1);

while (block)

{

buffer_temp=TSIOBufferBlockReadStart( block, cache_vconn->readerp, &data_len);

str.append(buffer_temp,data_len);

consumed+=data_len;

block=TSIOBufferBlockNext( block);

}

printf("\n%s\n",str.c_str());

}

}

四、测试过程

为了测试这个插件的过程,编写一个脚本,自动编译、自动重启、自动删除日志、自动启动测试客户端、自动关闭ATS,命令如下

/usr/local/ats/bin/tsxs -lpthread -o hello.so -c hello.cpp

sudo /usr/local/ats/bin/tsxs -o hello.so -i

sudo rm -f /usr/local/ats/var/log/trafficserver/*

sudo /usr/local/ats/bin/trafficserver restart

sleep 3s

python /home/carl/httpsend.py &

sleep 9s

sudo /usr/local/ats/bin/trafficserver stop

sleep 1s

可以看到,客户端启动后,接收到了ATS发送过来的数据


同时查看ATS的日志,可以看到对应事件回调的过程


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容