erlang下的fast-cgi模式访问(php-fpm),构建多语言开发平台
本帖最后由 bs 于 2010-05-04 21:49 编辑
Erlang做为高并发(c100K)连接前端,后端使用fast-cgi构建多语言开发平台,甚至负载均衡/分布式应用,
本文以php-fpm为例说明:
php脚本代码:
- <?php
- echo 'hello world!';
- ?>
复制代码erlang代码:
- -module(fcgi).
- -export([start/0]).
- start() ->
- Env = [{"SERVER_SOFTWARE","test web server"},
- {"SERVER_NAME","localhost"},
- {"HTTP_HOST","localhost"},
- {"GATEWAY_INTERFACE","CGI/1.1"},
- {"SERVER_PROTOCOL","HTTP/1.1"},
- {"SERVER_PORT","8080"},
- {"REQUEST_METHOD","GET"},
- {"REQUEST_URI","/t.php"},
- {"DOCUMENT_ROOT","/var/www/erlang"},
- {"DOCUMENT_ROOT_MOUNT","/"},
- {"SCRIPT_FILENAME","/var/www/erlang/t.php"},
- {"PATH_INFO",[]},
- {"PATH_TRANSLATED",[]},
- {"SCRIPT_NAME","/t.php"},
- {"REMOTE_ADDR","127.0.0.1"},
- {"REMOTE_HOST","127.0.0.1"},
- {"SERVER_ADDR","127.0.0.1"},
- {"LOCAL_ADDR","127.0.0.1"},
- {"QUERY_STRING",[]},
- {"HTTP_ACCEPT",
- "text/html;q=0.9,text/plain;q=0.5"},
- {"HTTP_USER_AGENT","unknow"},
- {"HTTP_ACCEPT_CHARSET","GBK,utf-8;q=0.7,*;q=0.3"},
- {"HTTP_ACCEPT_LANGUAGE","zh-CN,zh;q=0.8"},
- {"HTTP_ACCEPT_ENCODING","gzip,deflate,sdch"},
- {"HTTP_CACHE_CONTROL","max-age=0"}],%%构造http访问相关配置
- {ok, Socket}=gen_tcp:connect("127.0.0.1",9000,[binary, {packet, 0}],10000),
- %%发送初始请求8字节
- fcgi_send_record(Socket,1,
- 1, %%fast-cgi requestID application,下同
- <<1:16,0:8,0:40>>), %%<<1:16,是否长连接1或0:8,填充:40>>
- %%请求内容
- fcgi_send_record(Socket, 4, %%fast-cgi类型参数,下同
- 1, Env),
- %%完成
- fcgi_send_record(Socket, 4,1, []),
- recv_msg(Socket),
- gen_tcp:close(Socket).
- %%返回内容接收
- recv_msg(Socket) ->
- receive
- {tcp, Socket, Bin} ->
- %%原报文<<Version:8, Type:8, RequestId:16, ContentLength:16,PaddingLength:8, Reserved:8,Str/binary >> = Bin,
- <<_:32,ContentLength:16,_:16,Str/binary >> = Bin,
- %%计算输出内容长度
- Dlen=ContentLength-53,
- %%获取内容
- << _H:53/binary,Data:Dlen/binary,_Other/binary >> = Str,
- io:format("Received msg: ~p~n", [Data]);
- _other ->
- io:format("Other msg: ~p~n", [_other]),
- recv_msg(Socket),
- error
- after 3000 ->io:format("Time out.~n")
- end.
- %%发送选项
- fcgi_send_record(Socket, Type, RequestId, NameValueList) ->
- EncodedRecord = fcgi_encode_record(Type, RequestId,NameValueList),
- gen_tcp:send(Socket, EncodedRecord).
- %%组包
- fcgi_encode_record(Type, RequestId, NameValueList)
- when is_list(NameValueList) ->
- fcgi_encode_record(Type, RequestId,fcgi_encode_name_value_list(NameValueList));
- %%判断ContentData是否满8字节,否则填充
- fcgi_encode_record(Type, RequestId, ContentData)
- when is_binary(ContentData) ->
- ContentLength = size(ContentData),
- PaddingLength = if
- ContentLength rem 8 == 0 ->
- 0;
- true ->
- 8 - (ContentLength rem 8)
- end,
- %%填充数据,每8字节组包 不足用0填充
- PaddingData = <<0:(PaddingLength*8)>>,
- Version = 1,
- Reserved = 0,
- <<Version:8,
- Type:8,
- RequestId:16,
- ContentLength:16,
- PaddingLength:8,
- Reserved:8,
- ContentData/binary,
- PaddingData/binary >>.
- %%将环境变量组成binary
- fcgi_encode_name_value_list(_NameValueList = []) ->
- << >>;
- fcgi_encode_name_value_list(_NameValueList = [{Name, Value} | Tail]) ->
- <<(fcgi_encode_name_value(Name,Value))/binary,(fcgi_encode_name_value_list(Tail))/binary >>.
- fcgi_encode_name_value(Name, _Value = undefined) ->
- fcgi_encode_name_value(Name, "");
- fcgi_encode_name_value(Name, Value) when is_list(Name) and is_list(Value) ->
- NameSize = length(Name),
- NameSizeData = << NameSize:8 >>,
- ValueSize = length(Value),
- ValueSizeData = <<ValueSize:8 >>,
- << NameSizeData/binary,
- ValueSizeData/binary,
- (list_to_binary(Name))/binary,
- (list_to_binary(Value))/binary >>.
复制代码在Eshell下:
>fcgi:start().
Received msg: <<"hello world!">>
ok
初始内容数据:
ContentData=<<Role:16,
KeepConn:8, %%是否长连接
0:40 %%填充数据
>>
环境变量内容数据(样例):
- ContentData=[{"SERVER_SOFTWARE","test web server"},
- {"SERVER_NAME","localhost"},
- {"HTTP_HOST","localhost"},
- {"GATEWAY_INTERFACE","CGI/1.1"},
- {"SERVER_PROTOCOL","HTTP/1.1"},
- {"SERVER_PORT","8080"},
- {"REQUEST_METHOD","GET"},
- {"REQUEST_URI","/t.php"},
- {"DOCUMENT_ROOT","/var/www/erlang"},
- {"DOCUMENT_ROOT_MOUNT","/"},
- {"SCRIPT_FILENAME","/var/www/erlang/t.php"},
- {"PATH_INFO",[]},
- {"PATH_TRANSLATED",[]},
- {"SCRIPT_NAME","/t.php"},
- {"REMOTE_ADDR","127.0.0.1"},
- {"REMOTE_HOST","127.0.0.1"},
- {"SERVER_ADDR","127.0.0.1"},
- {"LOCAL_ADDR","127.0.0.1"},
- {"QUERY_STRING",[]},
- {"HTTP_ACCEPT",
- "text/html;q=0.9,text/plain;q=0.5"},
- {"HTTP_USER_AGENT","unknow"},
- {"HTTP_ACCEPT_CHARSET","GBK,utf-8;q=0.7,*;q=0.3"},
- {"HTTP_ACCEPT_LANGUAGE","zh-CN,zh;q=0.8"},
- {"HTTP_ACCEPT_ENCODING","gzip,deflate,sdch"},
- {"HTTP_CACHE_CONTROL","max-age=0"}],%%构造http访问相关配置
复制代码通信报文:
<<Version:8,
Type:8,
RequestId:16,
ContentLen:16, %%内容长度
PaddingLength:8, %%填充长度
Reserved:8,
ContentData/binary, %%内容数据
PaddingData/binary %%填充数据
>>.
返回的内容,前8个字节数据如下:
<<Version:8, Type:8, RequestId:16, ContentLength:16,PaddingLength:8, Reserved:8,
Data/binary>>
Data为处理结果数据,类似 <<"X-Powered-By: PHP/5.2.13rnContent-type: text/htmlrnrnhello world!">>
ContentLength既fastcgi处理结果数据的长度(字节).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
附性能测试及测试代码:
复制代码单线程1万次压力测试
> echo:test(10000).
Received data: <<"hello world!">>
....
Received data: <<"hello world!">>
Spand 2.106399 Sec.
测试机:
2G内存
1.8G双核
erlang环境
Erlang R13B04 (erts-5.7.5) [source] [64-bit] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false]