免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 9425 | 回复: 1
打印 上一主题 下一主题

[其他] erlang下的fast-cgi模式访问(php-fpm),构建多语言开发平台 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-05-04 21:47 |只看该作者 |倒序浏览
本帖最后由 bs 于 2010-05-04 21:49 编辑

Erlang做为高并发(c100K)连接前端,后端使用fast-cgi构建多语言开发平台,甚至负载均衡/分布式应用,
本文以php-fpm为例说明:





php脚本代码:
  1. <?php
  2. echo 'hello world!';
  3. ?>
复制代码
erlang代码:
  1. -module(fcgi).
  2. -export([start/0]).

  3. start() ->
  4.     Env = [{"SERVER_SOFTWARE","test web server"},
  5.       {"SERVER_NAME","localhost"},
  6.       {"HTTP_HOST","localhost"},
  7.       {"GATEWAY_INTERFACE","CGI/1.1"},
  8.       {"SERVER_PROTOCOL","HTTP/1.1"},
  9.       {"SERVER_PORT","8080"},
  10.       {"REQUEST_METHOD","GET"},
  11.       {"REQUEST_URI","/t.php"},
  12.       {"DOCUMENT_ROOT","/var/www/erlang"},
  13.       {"DOCUMENT_ROOT_MOUNT","/"},
  14.       {"SCRIPT_FILENAME","/var/www/erlang/t.php"},
  15.       {"PATH_INFO",[]},
  16.       {"PATH_TRANSLATED",[]},
  17.       {"SCRIPT_NAME","/t.php"},
  18.       {"REMOTE_ADDR","127.0.0.1"},
  19.       {"REMOTE_HOST","127.0.0.1"},
  20.       {"SERVER_ADDR","127.0.0.1"},
  21.       {"LOCAL_ADDR","127.0.0.1"},
  22.       {"QUERY_STRING",[]},
  23.       {"HTTP_ACCEPT",
  24.        "text/html;q=0.9,text/plain;q=0.5"},
  25.       {"HTTP_USER_AGENT","unknow"},
  26.       {"HTTP_ACCEPT_CHARSET","GBK,utf-8;q=0.7,*;q=0.3"},
  27.       {"HTTP_ACCEPT_LANGUAGE","zh-CN,zh;q=0.8"},
  28.       {"HTTP_ACCEPT_ENCODING","gzip,deflate,sdch"},
  29.       {"HTTP_CACHE_CONTROL","max-age=0"}],%%构造http访问相关配置

  30.         {ok, Socket}=gen_tcp:connect("127.0.0.1",9000,[binary, {packet, 0}],10000),
  31.         %%发送初始请求8字节
  32.         fcgi_send_record(Socket,1,
  33.                 1,                        %%fast-cgi requestID application,下同
  34.                 <<1:16,0:8,0:40>>),        %%<<1:16,是否长连接1或0:8,填充:40>>
  35.         %%请求内容
  36.         fcgi_send_record(Socket, 4,        %%fast-cgi类型参数,下同
  37.                 1, Env),
  38.         %%完成
  39.         fcgi_send_record(Socket, 4,1, []),       
  40.         recv_msg(Socket),
  41.         gen_tcp:close(Socket).


  42. %%返回内容接收
  43. recv_msg(Socket) ->
  44.   receive
  45.     {tcp, Socket, Bin} ->
  46.       %%原报文<<Version:8, Type:8, RequestId:16, ContentLength:16,PaddingLength:8, Reserved:8,Str/binary >> = Bin,
  47.       <<_:32,ContentLength:16,_:16,Str/binary >> = Bin,
  48.       %%计算输出内容长度
  49.       Dlen=ContentLength-53,
  50.       %%获取内容
  51.       << _H:53/binary,Data:Dlen/binary,_Other/binary >> = Str,
  52.       io:format("Received msg: ~p~n", [Data]);
  53.     _other   ->
  54.         io:format("Other msg: ~p~n", [_other]),
  55.         recv_msg(Socket),
  56.         error
  57.   after 3000 ->io:format("Time out.~n")
  58.   end.



  59. %%发送选项
  60. fcgi_send_record(Socket, Type, RequestId, NameValueList) ->
  61.     EncodedRecord = fcgi_encode_record(Type, RequestId,NameValueList),
  62.     gen_tcp:send(Socket, EncodedRecord).



  63. %%组包
  64. fcgi_encode_record(Type, RequestId, NameValueList)
  65.   when is_list(NameValueList) ->
  66.     fcgi_encode_record(Type, RequestId,fcgi_encode_name_value_list(NameValueList));
  67. %%判断ContentData是否满8字节,否则填充
  68. fcgi_encode_record(Type, RequestId, ContentData)
  69.         when is_binary(ContentData) ->
  70.         ContentLength = size(ContentData),
  71.         PaddingLength = if
  72.                         ContentLength rem 8 == 0 ->
  73.                             0;
  74.                         true ->
  75.                             8 - (ContentLength rem 8)
  76.                     end,
  77.         %%填充数据,每8字节组包        不足用0填充       
  78.         PaddingData = <<0:(PaddingLength*8)>>,
  79.         Version = 1,
  80.         Reserved = 0,
  81.     <<Version:8,
  82.       Type:8,
  83.       RequestId:16,
  84.       ContentLength:16,
  85.       PaddingLength:8,
  86.       Reserved:8,
  87.       ContentData/binary,
  88.       PaddingData/binary >>.




  89. %%将环境变量组成binary
  90. fcgi_encode_name_value_list(_NameValueList = []) ->
  91.     << >>;
  92. fcgi_encode_name_value_list(_NameValueList = [{Name, Value} | Tail]) ->
  93.     <<(fcgi_encode_name_value(Name,Value))/binary,(fcgi_encode_name_value_list(Tail))/binary >>.
  94. fcgi_encode_name_value(Name, _Value = undefined) ->
  95.     fcgi_encode_name_value(Name, "");
  96. fcgi_encode_name_value(Name, Value) when is_list(Name) and is_list(Value) ->
  97.     NameSize = length(Name),
  98.     NameSizeData = << NameSize:8 >>,
  99.     ValueSize = length(Value),
  100.     ValueSizeData = <<ValueSize:8 >>,
  101.     << NameSizeData/binary,
  102.       ValueSizeData/binary,
  103.       (list_to_binary(Name))/binary,
  104.       (list_to_binary(Value))/binary >>.
复制代码
在Eshell下:

>fcgi:start().
Received msg: <<"hello world!">>
ok












初始内容数据:
ContentData=<<Role:16,
        KeepConn:8,                  %%是否长连接
        0:40                                %%填充数据
        >>

环境变量内容数据(样例):

  1. ContentData=[{"SERVER_SOFTWARE","test web server"},
  2.       {"SERVER_NAME","localhost"},
  3.       {"HTTP_HOST","localhost"},
  4.       {"GATEWAY_INTERFACE","CGI/1.1"},
  5.       {"SERVER_PROTOCOL","HTTP/1.1"},
  6.       {"SERVER_PORT","8080"},
  7.       {"REQUEST_METHOD","GET"},
  8.       {"REQUEST_URI","/t.php"},
  9.       {"DOCUMENT_ROOT","/var/www/erlang"},
  10.       {"DOCUMENT_ROOT_MOUNT","/"},
  11.       {"SCRIPT_FILENAME","/var/www/erlang/t.php"},
  12.       {"PATH_INFO",[]},
  13.       {"PATH_TRANSLATED",[]},
  14.       {"SCRIPT_NAME","/t.php"},
  15.       {"REMOTE_ADDR","127.0.0.1"},
  16.       {"REMOTE_HOST","127.0.0.1"},
  17.       {"SERVER_ADDR","127.0.0.1"},
  18.       {"LOCAL_ADDR","127.0.0.1"},
  19.       {"QUERY_STRING",[]},
  20.       {"HTTP_ACCEPT",
  21.        "text/html;q=0.9,text/plain;q=0.5"},
  22.       {"HTTP_USER_AGENT","unknow"},
  23.       {"HTTP_ACCEPT_CHARSET","GBK,utf-8;q=0.7,*;q=0.3"},
  24.       {"HTTP_ACCEPT_LANGUAGE","zh-CN,zh;q=0.8"},
  25.       {"HTTP_ACCEPT_ENCODING","gzip,deflate,sdch"},
  26.       {"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.13\r\nContent-type: text/html\r\n\r\nhello world!">>
ContentLength既fastcgi处理结果数据的长度(字节).






原先CGI版本:
http://bbs.chinaunix.net/thread-1632957-1-1.html

论坛徽章:
0
2 [报告]
发表于 2010-05-04 22:32 |只看该作者
附性能测试及测试代码:

  1. -module( echo ).
  2. -export( [ test/1] ).
  3. %%测试脚本

  4. test(N)->
  5.     Start = erlang:now(),
  6.     dotimes(N),
  7.     Stop = erlang:now(),
  8.     time_diff(Start, Stop).

  9. dotimes(0) -> done;
  10. dotimes(N) ->
  11.     fcgi:start(),
  12.     dotimes(N - 1).

  13. time_diff({_,A2,A3}, {_,B2,B3}) ->
  14.         io:fwrite("Spand ~p Sec.~n",[(B2 - A2) + (B3 - A3) / 1000000.0]).
复制代码
单线程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]
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP