上篇文章写了很多文字,这里就不打算过多累述了,对于刚接触erlang的来说可能有点难看,不过大意理解就好了,写完以后用ab压了一下,300个请求 70个并发请求百度一个搜索页面花费了5秒多,在用ab直接压了一下这个搜索页面,花了一个相当长的等待时间,最后还没出结果就断掉了。
      代码主要分了4个模块:
         1、index.erl 主程代码文件,启动服务:index:http_init_start().
         2、common.erl 提供一些公用函数,主要是对我自己定义的字典(形如[{name, “xxx”}, {pswd, “xxx”}]这样结构的对象)值查找、更新、删除。
         3、config.erl 一个配置函数模块,因为我定义的url路径为:http://服务地址:端口/请求标识符/请求路径,如:http://192.168.0.235:8010/baidu/w/s?wd=sdf 其实访问的是:http://www.baidu.com/s?wd=sdf,目的是为了安全、去除域名解析、缩短url等。
         4、net_work.erl 一个更http请求相关的模块。
      说多了,代码来:

%% Author: tuyl
%% Created: 2012-5-16
%% Description: TODO: Add description to index
-module(index).
%-export([]).
-compile(export_all).
%/usr/local/lib/erlang/lib/observer-1.0/priv/bin/etop
http_init_start()->
	%开启一个进程统一执行一个http请求
	Did = spawn(?MODULE, do_http_request_process, []),
	%开启一个进程统一接受http请求并归档
	Pid = spawn(?MODULE, accept_http_request_process, [Did, []]),
	%开一个8010的端口接受http请求
	start(8010, Pid).
%%%%%%%%%%%%%%%%%%%%%%%%%接收前端处理相关进程%%%%%%%%%%%%%%%%%%%%%%%%%
start(Port, ReqPid)->
	N = erlang:system_info(schedulers),
	listen(Port, N, ReqPid),
	io:format("http start with:~b schedulers on port:~b~n!", [N, Port]).
listen(Port, N, ReqPid)->
	Opts = [{active, false},
			binary,
			{packet, http_bin},
			{reuseaddr, true}],
	{ok, Listen} = gen_tcp:listen(Port, Opts),
	Spawn = fun(I)->
					register(list_to_atom("acceptor_" ++ integer_to_list(I)), spawn_opt(?MODULE, accept, [Listen, I, ReqPid], [link, {scheduler, I}]))
			end,
	lists:foreach(Spawn, lists:seq(1, N)).
accept(Listen, I, ReqPid)->
	%被动模式只能用gen_tcp:recv接收
	case gen_tcp:accept(Listen) of
		{ok, Socket}-> spawn_opt(?MODULE, loop, [Socket, [], ReqPid], [{scheduler, I}]);
		Error -> erlang:error(Error)
	end,
	accept(Listen, I, ReqPid).
loop(Socket, Header, ReqPid)->
	case gen_tcp:recv(Socket, 0) of
		{ok, http_eoh}->
			%io:format("Receive header:~p~n", [Header]),
			Path = common:list_find(Header, abs_path),
			send_request(ReqPid, Path, Socket),
			ok;
		{ok, Data} ->
			case Data of
				{http_request,_,{abs_path,Path},_}->
					Header1 = [{abs_path, binary_to_list(Path)}|Header],
					io:format("Receive request:~p~n", [binary_to_list(Path)]);
				{http_header, _, Key, _, Val}->
					%io:format("Receive header:~p~n", [Key]),
					Header1 = [{Key, binary_to_list(Val)}|Header];
				_ ->
					Header1 = Header,
					io:format("Receive data:~p~n", [Data])
			end,
			loop(Socket, Header1, ReqPid);
		Error ->
			io:format("Receive Other data:~p~n", [Error])
	end.
%%%%%%%%%%%%%%%%%%%%%%%%%处理接收到的请求相关进程%%%%%%%%%%%%%%%%%%%%%%%%%
do_http_request_process(Url, UrlId, Pid)->
	Body = net_work:http_get_body(Url),
	Pid ! {re_http, Url, UrlId, Body}.
do_http_request_process()->
	receive
		%获取到url访问请求
		{do_http, Url, UrlId, Pid}->
			% 开一个进程处理接收到的http请求
			spawn(?MODULE, do_http_request_process, [Url, UrlId, Pid]);
		% 其它意外请求,不确定
		OtherRequest->
			io:format("do_http_request_process other:~p~n", [OtherRequest])
	end,
	do_http_request_process().
accept_http_request_process(DoHttpId, UrlList)->
	io:format("UrlList:~p~n", [UrlList]),
	receive
		%获取到url访问请求
		{do_http, Url, Socket}->
			UrlId = get_url_unique_url(Url), %url唯一编号
			SocketList = common:list_find(UrlList, UrlId),
			if SocketList == undefined->
				   DoHttpId ! {do_http, Url, UrlId, self()},
				   accept_http_request_process(DoHttpId, [{UrlId, [Socket]}|UrlList]);
			   true ->
				   NSocketList = common:list_update(UrlList, UrlId, [Socket|SocketList]),
				   accept_http_request_process(DoHttpId, NSocketList)
			end;
		%url获取到资源返回请求
		{re_http, Url, UrlId, Data}->
			SocketList = common:list_find(UrlList, UrlId),
			%如果url存在的话
			if SocketList /= undefined->
				   %开一个进程批量返回socket请求数据
				   spawn(?MODULE, response_socket_list, [Data, SocketList]),
				   NUrlList = common:list_delete(UrlList, UrlId),
				   accept_http_request_process(DoHttpId, NUrlList);
			   true-> %不存在写个日志
				   io:format("ERROR url in UrlList:~p~n over!", [Url]),
				   accept_http_request_process(DoHttpId, UrlList)
			end;
		%直接返回请求
		{re_data, Data, Socket}->
			%一个请求就不开进程了
			response_socket_list(Data, [Socket]),
			accept_http_request_process(DoHttpId, UrlList);
		%其它意外请求,不确定
		OtherRequest->
			io:format("accept_http_request_process other:~p~n", [OtherRequest]),
			accept_http_request_process(DoHttpId, UrlList)
	end.
%批量返回所有http请求
response_socket_list(Body, [Socket|SocketList])->
	gen_tcp:send(Socket, net_work:body_pack(Body)),
	gen_tcp:close(Socket),
	response_socket_list(Body, SocketList);
response_socket_list(_Body, [])->
	io:format("send body over! ~n").
%生成唯一url,合并url请求
get_url_unique_url(Url)->
	{ok, UrlInfo} = http_uri:parse(Url),
	{Scheme, _Opts, Host, Port, Path, Args} = UrlInfo,
	Xargs = lists:sort(string:tokens(Args, "&")),
	%过滤第一个为下划线的参数
	Iargs = lists:filter(fun(X)->string:sub_string(X, 1,1) /= "_" end, Xargs),
	atom_to_list(Scheme)++"://"++Host++":"++integer_to_list(Port)++Path++string:join(Iargs, "&").
%处理接收到的请求
send_request(ReqPid, Path, Socket)->
	PathInfo = string:tokens(Path, "/"),
    io:format("Format path: ~p, ~p ~n", [Path, PathInfo]),
    if PathInfo == []->
		   Body = "Hello World!",
		   ReqPid!{re_data, Body, Socket};
	   true ->
		   [Host|XPath] = PathInfo,
		   XHost = config:get_host(Host),
		   if XHost == undefined->
				  Body = "404 Not Found Host Key!",
				  ReqPid!{re_data, Body, Socket};
			  true ->
				  Url = XHost++string:join(XPath, "/"),
				  ReqPid!{do_http, Url, Socket}
		   end
    end.
%% Author: tuyl
%% Created: 2012-5-24
%% Description: TODO: Add description to common
-module(common).
-compile(export_all).
%---------------------------查找-----------------------------%
%查找list (eg:[{key,value}])中的某key的值
list_find([Info|List1], Key)->
	case Info of
		{Key, Val} ->
			Val;
		{_, _} ->
			list_find(List1, Key)
	end;
list_find([], _Key)->
	undefined.
%---------------------------更新-----------------------------%
%更新list (eg:[{key,value}])中的某key的值
list_update(List, Key, Value)->
	list_update(List, [], Key, Value).
%循环之用
list_update([Info|List1], PreList, Key, Value)->
	case Info of
		{Key, _} ->
			PreList++[{Key,Value}|List1];
		{_, _} ->
			list_update(List1, [Info|PreList], Key, Value)
	end;
%循环之用
list_update([], _PreList, _Key, _Value)->
	undefined.
%---------------------------删除-----------------------------%
%更新list (eg:[{key,value}])中的某key的值
list_delete(List, Key)->
	list_delete(List, [], Key).
%循环之用
list_delete([Info|List1], PreList, Key)->
	case Info of
		{Key, _} ->
			PreList++List1;
		{_, _} ->
			list_delete(List1, [Info|PreList], Key)
	end;
%循环之用
list_delete([], _PreList, _Key)->
	undefined.
%% Author: tuyl
%% Created: 2012-5-25
%% Description: TODO: Add description to config
-module(config).
-compile(export_all).
get_host(Key)->
	Config = [{"baidu", "http://www.baidu.com/"},
			  {"google", "http://www.google.com.hk/"},
			  {"500wan", "http://www.500wan.com/"},
			  {"mtime", "http://www.mtime.com/"}],
	common:list_find(Config, Key).
%% Author: tuyl
%% Created: 2012-5-25
%% Description: TODO: Add description to net_work
-module(net_work).
-compile(export_all).
body_pack(NewBody)->
	Response = "HTTP/1.1 200 OK\r\nContent-Length:"++integer_to_list(length(NewBody))++"\r\n\r\n"++NewBody,
	list_to_binary(Response).
http_get_body(Url)->
	inets:start(),
	case httpc:request(Url) of
		{ok, {{_Version, 200, _ReasonPhrase}, _Headers, Body}}->
			Body;
		{ErrorKey, Data}->
			io:format("net_work error:~p, ~p~n", [ErrorKey, Data])
	end.
无标签信息 0 条

      从上周开始正式停止德州扑克的android开发,另外开启了一个长轮询研究,其实之前我也写过博客,也写了一点demo,后来很多人+Q询问一些相关问题。这次再次拾起是打算做一些优化,大家都明白,长连接的弊端无非是响应服务连接数限制,因为之前rong和xkai学习了一下erlang的开发,因为erlang在多进程方面非常有优势,它的进程非系统进程,可以把多核发挥的淋漓尽致,所以打算把erlang的优点用在长连接轮询上。
      因为暗黑3发布的关系,所以最近分心很厉害,上周更是有点沉迷,从flong那里借了300买了个账号(借钱?恩,这次借了很多钱把大学的助学贷款还了,最后落得一身干净),所以白天也没什么灵感,而且白天上班很少有很不错的点子,有晚玩游戏到2点左右,睡觉满脑子刀光剑影,所以就想了一下,其实长轮询很大的问题是:服务端做while不释放请求,导致请求到达上限时只能排队,而在一般情况下,长轮询的请求都是同一请求,如:群聊,群内成员都是发送了同一个查询请求;再如,竞拍,所以关注同一个竞拍产品的都是发送了同一个查询请求。所以结论:在服务端拦截用户请求,对同一查询同一请求,得到结果后统一返回。
      想法不错,问题呢,当然有,要不然这就会成为规范,而不会沦落到被研究:
            1、请求的cookie信息被拦截,否则几乎不可能统一请求连接。这就要求请求的无状态性(跟登录等浏览器数据无关)。
            2、对url中特殊字段需要过滤,比如我规定,以下划线开头的变量会被过滤。因为用ajax发送请求时一般会带一个时间戳变量免除cache。
      肯定还有些隐蔽问题,但问题总是能客服,被解决的。看erlang这厮可教一个苦逼,从周1-周3,一直看书,看一会就想放弃,变态的语言,压根下不了手。最大的三个问题:字典、全局变量、遍历,昨天(周四)终于开窍了,开始写了个接受http请求的,然后开启了若干个进程,因为进程越多越能发挥多核的优势,当然要合理,下面说一下这次编码中的过程和设计到的进程
      1、初始化启动一个进程A,用于真正实施http请求,即server发送http请求给apache类似http服务,获得结果,发送数据给进程B。
      2、初始化启动一个进程B,用于归档url,统一分配,发送真正http请求给A,返回数据。
      3、开启服务,监听一个端口,开N个进程用来accept前端http请求(N为内核个数,一般够用了)。
      4、每次接收到一个请求,开启一个进程用于处理这次请求,包括发送请求给进程B。

无标签信息 0 条
13 / 05 / 2012 buling

       博客记录琐事,内容可能比较恶心,请选择阅读,恳求不要传阅。
       这个周花了两天实现想做一个边下载边播放的android应用,虽然最终一无所成,但我还是说一下过程吧,实现边下载边播放理论上应该是没有问题,下载了两个demo,也了解预加载一个data文件播放,然后再开启一个线程默默下载,直到第一个文件播放完成…循环。xing说以前做过,可能存在卡的现象,能理解,在播放器load文件需要时间,而这个时间几乎是没有办法节约出来。
      为什么会一无所成呢,这里就不得不说一下万恶的迅雷了,其实也怪不了人家…..在网上拷了个mp4文件播放,发现conn.getContentLength的时候返回了-1,最终没有搞定原因,应该是服务器不支持http下载,那还有什么办法可以下载呢,我对这个盲目的很,再者就是遇到网络死链,即链接已失效,但迅雷还能下载资源的链接,就搞不定,所以一头扎进了“迅雷 协议”、“迅雷 候选资源”、“迅雷 不为人知 api”…..两天中有80%的时间是在搜索中度过的,中国人都太懒了,我也是,如果一开始就自己抓包,解密协议可能不成功,至少也有个大概了,哎….
      上周几乎没做多少事情,就写了一个ppt,ppt内容还是比较充足,但是因为是在公司写的,而且被加密了,估计是分享不出来了。然后hait让我研究一下comet技术,最好用erlang写一个响应服务,这个正在着手弄,现在有点概念了,等上班跟他们在确认一下思路就开始动工。还有上周被lulu收编了,其实不想这样,因为讨厌服从,讨厌被要求。前几天我提了一下手机客户端的几点改进(1、记住密码:不在客户端记录密码原文或密码的md5值等,采取向服务器申请一个唯一标识,每次用户登录更好一次标识,N个时间后标识过期,要求玩家重新登录再分配;2、赛事列表页面:希望加入socket消息实时通知赛事变化,毕竟手机不比pc,轮询是不现实的;3、在登录后返回用户头像地址:登录后再发送一次http请求获取用户头像地址没有必要);这几点都是分批提出的,lulu说让我提一个文档,我不想,不想把时间花费在这些无用的事情上,如果是在我们组,肯定提一点就干掉一点(如果可行),现在我也不在客户端上有想法了,想了会很麻烦,这就是程序员。
      最近吃多一点点就会肚子不舒服,那天去医院检查了一下,什么都没有,还花费了大半百,总感觉现代医院,应该抽点血啥都能检查出来,结果科技还没有我想象的那么前沿,还要留点“米共”化验才行。
      昨晚做了萝卜排骨宴,今天早上起来吧剩下的一点吃完了,中午石头请吃饭,很不想去,之前也有几次这种大型饭局都不想参与,导致最后落得个“耍大牌”的称号,还好我性格一向比较无耻,你们说是就是了吧。记得去年回家,家亲戚都来我们家聚餐,我实在是不知道应该说点什么最后吃了一顿很郁闷的饭。今天中午本来也是极其不打算去,但碍于石头诚意,最后还是去了,结果因为饭桌玩游戏无视现场被鄙视,我要说点什么呢,明知我就这副德行,何必在意呢。我喜欢几个趣味相投的朋友吃饭,可以真实的聊,不会那么飘那么多约束,不会跟某人聊的时候忽略了别的人,而且不太喜欢吃外面的事物,所以尽量不参与饭局。对那些个对我德行受伤的人和事在此道歉了,知不知晓也是这样。

无标签信息 0 条
02 / 05 / 2012 buling

博客记录琐事,内容很私人,看就罢了,恳求不要传阅。
      谢谢你们带我做技术,谢谢你们让我学会写代码,谢谢你们教我除了代码还有做人的道理…
      从2010年7月到现在已经来了快两年了,两年来我过的很积极,几乎没有过失落或无所成,很多时候我都不在乎是否周末,不在乎是否18点钟了。我一直说这工作后(没什么准备谈这辈子)最大的幸运就是遇到了wangsl和xing俩位老大和hait的帮助了,记得好像以前也写过一些这样的博客,不知道还在不在,中间因为一些事情删除了一部分,但有的事还是要写,有的人还是要说,喜欢传的就传吧。
那些人,那些不能忘记的过去...
      从进公司开始没感觉什么特别,后来进入三个月的学习期,王老大要求很严格,而且给我们布置的题目也很有含金量,我也很用心做,这为我后来在php方面的提高攒了不少基础,第一个项目就是做竞拍,最大的成就莫过于把之前学到的一套mvc使用到项目中,而且后来开始做邮件系统接触服务开发,鬼使神差认识了hait,我记不得到底为什么认识了hait,但后来他演示了很多服务开发的事情,记得最深的是在我座位上用了几行命令启了一个ice storm的date demo,感觉好神奇,好向往,后来也就一门心思的想写服务程序,第一个程序就是邮件发送系统,前台加后台。写完就2010年底了,这一年多感觉做的还不错,但考核只得了40-70,老大说你们新来的这批都这个级,恩,理解,接下来我们组的一哥们辞职去了腾讯,接下来2011年的优秀员工给了我和shengw(俩应届生)。
      业开待的时间里没有什么值得回忆的,除了我们组的几个人(lijingai、zhengxuze、huanglinsheng、搞忘了一个),事隔一年想起种种还是不由得…11年3月份正式被hait招聘到系统平台,开始我认识第二个我人生最重要的人-xing,11年前就几天给我布置了第一个应用,开发sms短信网关,一点感念都没有,又到年底了,准备了一些基础,过了个年开始sms网关开发,也开始认识到应用开发的基本过程,开始认识到变量都能保存状态,这是很多从前端转后台同学分不清楚的地方。接下来写了好几个应用,我也很愿意主动承担分配的任务,每个应用都学到很多,我也写了总结,后来zhaok离开了公司,我很痛恨我的任性和自以为是,如果我不进系平,如果我不好强,如果我不接那个sso项目,是不是可以…。星哥为人很和善,对我们几个都很照顾,遇到问题总是平心静气的告诉我们,上次数据库切换,那天晚上因为我提前修改了配置,结果数据库重连超过次数锁死了数据库,第二天技研的神棍就给我订了一个4级事故,下午没心情上班,请假半天回家睡觉。后来hait和xing知道了打电话开导,xing还帮忙受了这次事故,这真心不是我想要的。
      去年年底公司打算做德州扑克游戏,之前我建议xing申请负责,结果他因为种种原因拒绝了,当时我也说他不参加我也不会参加,最后我真的没有参与到德州的服务开发,在整个德州扑克设计期间我也花了很多时间跟他们讨论争辩,因为脾气不咋的,经常出现火爆场面。年后因为没有事情,xing找了一个实时监控的项目给我做,但因为花了很多时间做尝试,而且写的代码没有太多的技术含量,导致最后有点承受不了,然后我也给xing说了这件事情,这次事情我后悔不已,虽然后来我也道歉了,但还是一直很难放下,希望xing能原谅我的不是抬举和无知行为。接下来应用没开发完就开始做德州android版开发,大概花了7天时间完成了一个最简单可运行版本,xing也给了我很多支持和帮助,虽然不是android本身,但更让我难受:很多小琐碎他宁愿自己去写也不想让我停下android,我承认我确实盲目的不可收拾了,但这些事情还是能应付,至少也是个组长,还对亲自网关升级,还跟他们做联调,还加班等业开同学调试…真心麻烦你了
      上次聊天说不想去龙岗,我也意识到一些,但总想至少也应该是7、8月以后的事情,没曾想会这么快,我真的适应不了,之前一直以来我都想没有什么我承受不起,我也过的很坦然,你不怕得罪人,今天你把我开了,明天换家公司照样工作,你再牛皮从行业把我封杀了,我换个行业一样站起来。我总是告诉自己,我知道这个社会的原则,知道做人的准则,最终肯定会被这个社会给同化,也会学会圆滑,也会学会打马虎眼,也会慈祥,也会忍让,也会学会合作…,但我只是不想学会的太快,我才工作两年。慢慢经过几次事情和hait说的一些话,也慢慢开始注意自己的一些行为,也开始学会一些表达方式,以后还要学会和团队一起成长,做事…
      我知道你会离开,但我不知道来的会这么快,谢谢你教会我的一切,只是我现在只能说谢谢…如果还有以后

无标签信息 0 条