1

Hunchentoot源码分析之架构流程

 2 years ago
source link: https://imnisen.github.io/hunchentoot-main-architecture.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Hunchentoot源码分析之架构流程

hunchentoot-03.gif

Hunchentoot 是一个common lisp实现的web服务器,同时也提供了构建动态网站的一些工具集。能够处理 HTTP/1.1 chunking 、持久化连接(keep-alive)和 SSL 。 本文分析了hunchentoot基本架构,从启动一个服务实例到处理一个请求的这个过程进行介绍。

本文代码基于hunchentoot 1.2.35。

1 涉及的主要源码文件

  • acceptor.lisp => hunchentoot实例,当需要启动服务器时,实例化一个acceptor,然后调用start方法监听请求
  • taskmaster.lisp => 当acceptor接收到请求后交给对应的taskmaster来处理,实现不同的taskmaster可以实现不同的处理逻辑(比如单线程还是多线程)
  • request.lisp => request请求相关数据对象
  • reply.lisp =>reply(response)相应相关数据对象

2 实例化acceptor

下面是启动服务器的一个示例:

(hunchentoot:start (make-instance 'hunchentoot:easy-acceptor :port 4242))

上面代码里的 easy-acceptor 的是继承自 acceptor 的子类。

当需要启动服务器时,实例化一个acceptor时会根据是否支持线程来实例化不同的 taskmaster ,同时也绑定了相应的request和reply类。

如下面代码所示:

(defclass acceptor ()
  (...)
  (:default-initargs
   :address nil
   :port 80
   :name (gensym)
   :request-class 'request   ;; <--
   :reply-class 'reply       ;; <--
   #-lispworks :listen-backlog #-lispworks 50
   :taskmaster (make-instance (cond (*supports-threads-p* 'one-thread-per-connection-taskmaster)  ;; <--
                                    (t 'single-threaded-taskmaster)))
   :output-chunking-p t
   :input-chunking-p t
   :persistent-connections-p t
   :read-timeout *default-connection-timeout*
   :write-timeout *default-connection-timeout*
   :access-log-destination *error-output*
   :message-log-destination *error-output*
   :document-root (load-time-value (default-document-directory))
   :error-template-directory (load-time-value (default-document-directory "errors")))
  (:documentation "...")

taskmaster.lisp 里定义了两大类 taskmaster ,关系如下图:

hunchentoot-01.png

3 启动服务

实例化完 acceptor 后调用其 start 方法开始监听。在监听成功后,在taskmaster里绑定对应的执行acceptor的指针,做到两者可以互相引用。如下所示:

(defmethod start ((acceptor acceptor))
  (setf (acceptor-shutdown-p acceptor) nil)
  (start-listening acceptor)
  (let ((taskmaster (acceptor-taskmaster acceptor)))
    (setf (taskmaster-acceptor taskmaster) acceptor)  ;; <--
    (execute-acceptor taskmaster))
  acceptor)

当有请求时,主要处理流程和相应代码位置如下图:

hunchentoot-02.png

当调用 acceptorstart 方法后,会调用对应 taskmasterexecute-acceptor 方法,根据对应 taskmaster 的不同(多线程还是单线程),以不同的方式(是否新建线程)调用 acceptoracceptor-connections 。接下来的处理流程就像图上所示,还是比较清楚的。

可以看到 acceptor 配合 taskmaster 使用,将不变的逻辑放到 acceptor 里,根据实现会变化的逻辑放到了 taskmaster 里,还是比较巧妙的。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK