代码改变世界

Cowboy 源码分析(二十三)

2012-06-27 01:09  rhinovirus  阅读(1073)  评论(0编辑  收藏  举报

  大家好,上一篇在文章的最后我们提到cowboy_http_protocol:terminate_request/3 函数,今天我们继续往下看:

-spec terminate_request(any(), #http_req{}, #state{}) -> ok.
terminate_request(HandlerState, Req, State) ->
    HandlerRes = handler_terminate(HandlerState, Req, State),
    next_request(Req, State, HandlerRes).

  这个函数就两行代码:

  首先调用cowboy_http_protocol:handler_terminate/3 函数:

-spec handler_terminate(any(), #http_req{}, #state{}) -> ok.
handler_terminate(HandlerState, Req, #state{handler={Handler, Opts}}) ->
    try
        Handler:terminate(Req#http_req{resp_state=locked}, HandlerState)
    catch Class:Reason ->
        PLReq = lists:zip(record_info(fields, http_req), tl(tuple_to_list(Req))),
        error_logger:error_msg(
            "** Handler ~p terminating in terminate/2~n"
            "   for the reason ~p:~p~n"
            "** Options were ~p~n** Handler state was ~p~n"
            "** Request was ~p~n** Stacktrace: ~p~n~n",
            [Handler, Class, Reason, Opts,
             HandlerState, PLReq, erlang:get_stacktrace()])
    end.

  Handler:terminate(Req#http_req{resp_state=locked}, HandlerState)

  = default_handler:terminate(Req#http_req{resp_state=locked}, HandlerState)

  跟之前类似,调用 default_handler:terminate/2 函数:

terminate(_Req, _State) ->
    ok.

  简单到不能再简单的函数,当函数正确执行时,返回ok,那么如果是执行函数中有异常,那么cowboy_http_protocol:handler_terminate/3 函数就会捕获到异常。

  我们来做个测试,修改default_handler:terminate/2 函数,让它手动抛出异常,然后来跟踪下代码的执行过程,修改代码如下:

terminate(_Req, _State) ->
    throw("terminate error."),
    ok.

  如上面所示,很简单,我们添加了一行代码,throw("terminate error."), 好了,重新编译,然后运行,这里我依然使用Debugger,来断点查看下,运行情况,如下图:

  

  注意:

  < Class = throw
  < Reason = "terminate error."

  接下来,我们看下异常处理的具体逻辑:

    catch Class:Reason ->
        PLReq = lists:zip(record_info(fields, http_req), tl(tuple_to_list(Req))),
        error_logger:error_msg(
            "** Handler ~p terminating in terminate/2~n"
            "   for the reason ~p:~p~n"
            "** Options were ~p~n** Handler state was ~p~n"
            "** Request was ~p~n** Stacktrace: ~p~n~n",
            [Handler, Class, Reason, Opts,
             HandlerState, PLReq, erlang:get_stacktrace()])
    end.

  这里有几个系统函数,我们之前没接触过,这里我们按照老规矩,我们来一个一个看下:

  首先是 tuple_to_list/1 函数,这个函数比较常用,erlang doc地址:http://www.erlang.org/doc/man/erlang.html#tuple_to_list-1 从函数命名,应该很容易明白,就是将元组转成列表,下图是官方的解释,以及例子:  

  

  接下来是 tl/1 函数,返回去掉列表中第一个元素的列表,erlang doc地址:http://www.erlang.org/doc/man/erlang.html#tl-1 和上面一样,我截了个图,方便大家查看:

  

  然后看下,record_info/2 函数参考坚强2002同学的文章:http://www.cnblogs.com/me-sa/archive/2012/05/22/erlang-code-snippet-2.html

  下面是我写的测试代码:

  

  大家看看,我想应该能够对这个函数有一定的理解了吧。

  往后就是 lists:zip/2 函数,erlang doc地址:http://www.erlang.org/doc/man/lists.html#zip-2 从网上找的,关于 lists模块中的函数中文解释,地址:http://blog.csdn.net/zhangjingyangguang/article/details/7377787 谢谢这位朋友的翻译,下图是该函数的解释,很清楚:

  

  最后是 error_logger:error_msg/2 函数,向错误日志发送一个错误消息。它的参数与io:format(Format, Data)函数的参数一样,erlang doc 地址:http://www.erlang.org/doc/man/error_logger.html#info_msg-2

  

  最后是我们可以从Erlangshell看到的错误信息

  

  

  好了,这个函数就讲到这里。

  下一篇,我们将从 cowboy_http_protocol:terminate_request/3 函数的这一行继续往下看:next_request(Req, State, HandlerRes).

  最后,谢谢大家支持,晚安。