å°ãåã«ãç§ã¯Common Lispã®ç 究ãå§ããŸããã æ°ããããã°ã©ãã³ã°èšèªãç¿åŸããã®ã¯ç°¡åãªäœæ¥ã§ã¯ãªãããã«æããŸããç¹ã«ããããŸã§ã«åºäŒã£ããã¹ãŠã®èšèªãšã¯ãŸã£ããç°ãªãå Žåã§ãã ããã§ç§ã¯
Land Of Lispããå§ããããšã«ããŸããã ãã®æ¬ã¯éåžžã«è¯ããé¢çœãåçããããåå¿è
ã«ã¯ãšãŠãè¯ãã§ãã ç« ã®1ã€ã§ã¯ãCommon Lispã§WebãµãŒããŒãäœæããæ¹æ³ã«ã€ããŠèª¬æããŸããã ç§ã¯ãã®ãããã¯ãå°ãéçºããããšã«ããŸããããæçµçã«ã¯ãã®ç« ã§èª¬æãããã®ãæ£ç¢ºã«å
¥æããããšã¯ã§ããŸããã§ããããéåžžã«èå³æ·±ãWebãµãŒããŒã«ãªããŸããã ãœãŒã¹ã³ãŒãã¯
ãã¡ãã«ãããŸã ã
æžãã«ã¯ãemacsãsbclãslimeãquicklispãã€ã³ã¹ããŒã«ãããLinuxãå¿
èŠã§ãã ã€ã³ã¹ããŒã«ãæ§æãããã³äœ¿çšæ¹æ³ã«ã€ããŠã¯èª¬æããŸãããããã«ã€ããŠã¯ãã€ã³ã¿ãŒãããäžã«å€ãã®èšäºããããŸãã WebãµãŒããŒå
šäœãmywebãšãã1ã€ã®ããã±ãŒãžã«å«ãŸããŸãã ãã®ååã®ãã©ã«ããŒãäœæãããã®äžã«2ã€ã®ãã©ã«ããŒlogããã³webãäœæããŸãã ãã°ãã©ã«ããŒã«ã¯ãWebãµãŒããŒã®ãã°ãã¡ã€ã«ãå«ãŸããŸãã Webãã©ã«ããŒã«ã¯ãWebãµãŒããŒãã¯ã©ã€ã¢ã³ãã«æäŸããHTMLããŒãžãšç»åãå«ãŸããŸãã WebãµãŒããŒå
šäœã¯7ã€ã®ãã¡ã€ã«ã§æ§æãããŠããŸãã
ããã±ãŒãžã宣èšãããã¡ã€ã«ãšãããã±ãŒãžã®èª¬æçšã®asdãã¡ã€ã«ããå§ããŸãããã
package.lispãã¡ã€ã«ãäœæããŸãã
(in-package :cl-user) (defpackage :myweb (:use :cl :usocket :bordeaux-threads) (:export :start-http :stop-http :list-workers :list-requests)) (defpackage :myweb.util (:use :cl :local-time) (:export :parse-request :read-utf-8-string :response-write :get-param :get-header :http-response :file-response :html-template :log-info :log-warning :log-error)) (defpackage :myweb.handler (:use :cl) (:export :process-request))
ã芧ã®ãšãããWebãµãŒããŒã¯3ã€ã®ããã±ãŒãžã§æ§æãããŠããŸãã
- myweb -WebãµãŒããŒãèµ·åããã³åæ¢ããããã®é¢æ°ãå«ãŸããŸã
- myweb.util-ãªã¯ãšã¹ãã®åŠçã«åœ¹ç«ã€é¢æ°ãå«ãŸããŸã
- myweb.handler-ãªã¯ãšã¹ãåŠçã³ãŒãèªäœãå«ãŸããŸã
ããã±ãŒãžå
é¢æ°ã¯éåžžããã¡ã€ã«ã®å
é ã«é
眮ãããå€æ°ãšé¢æ°ã宣èšããããã±ãŒãžã®ååã瀺ããŸãã ãã®å Žåãããã±ãŒãžã宣èšããŠãããããã¡ã€ã³ããã±ãŒãžcl-userã§ããã±ãŒãžã宣èšããå¿
èŠããããŸãã
ããã±ãŒãžå®£èšã®
ïŒuseããã³
ïŒexportãã£ã¬ã¯ãã£ãã«æ³šæããŠãã ããã
ïŒuseã䜿çšãããšãé¢æ°åã®å
é ã«ããã±ãŒãžåãæå®ããã«ä»ã®ããã±ãŒãžã®é¢æ°ã
䜿çšã§ãããããå
¥åããããã¹ãã®éãæžããŸãã
ïŒexportã¯ãããã±ãŒãžã®å€éšã§äœ¿çšã§ããé¢æ°ã®ååãèšå®ããŸãã ã芧ã®ãšãããããã±ãŒãžã«ã¯mywebé¢æ°start-httpããã³stop-httpããããŸãã ããã±ãŒãžã§ããcl-userã§ã¯ãæåã«ïŒexportãã£ã¬ã¯ãã£ãã䜿çšããŠå®£èšããªãéããmywebïŒstart-httpãä»ããŠããããåŒã³åºãããšã¯ã§ããŸããã
ããã±ãŒãžã®åºåã¯ãã§ã«ãããŸãããçŸåšã¯ãããã®ããã±ãŒãžã®ãœãŒã¹ã³ãŒããèšè¿°ããå¿
èŠããããŸãã web.lispãutil.lispãããã³handler.lispãã¡ã€ã«ãäœæããããããã«ããã±ãŒãžå
åŒã³åºããè¿œå ããŸãã web.lispã®å Žå-ïŒã€ã³ããã±ãŒãžïŒmywebïŒãutil.lispã®å ŽåïŒã€ã³ããã±ãŒãžïŒmyweb.utilïŒãªã© ãŸããåŒã³åºãã§log.lispãã¡ã€ã«ãäœæããå¿
èŠããããŸãïŒã€ã³ããã±ãŒãžïŒcl-logïŒã ãã®ãã¡ã€ã«ã¯ã
cl-logãã®ã³ã°ã·ã¹ãã ãå®è¡ããã³æ§æããããã«å¿
èŠã§ãã
WebãµãŒããŒã®ãã¡ã€ã«æ§é ãäœæããæåŸã®äœæ¥ã¯ãmyweb.asdãã¡ã€ã«ã®äœæã§ãããã®ãã¡ã€ã«ã«ã¯ããã¹ãŠãæ©èœããããã«asdfã·ã¹ãã ãèªã¿èŸŒãå¿
èŠããããã¡ã€ã«ãèšè¿°ãããŠããŸãã
ããŒïŒserial tã¯ãasdfããªã¹ããããŠããã®ãšåãé åºã§ãã¡ã€ã«ãããŠã³ããŒãããããšã瀺ããŸãã
次ã«ãload.lispãã¡ã€ã«ãäœæããå¿
èŠããããŸããããã«ãããããã±ãŒãžãããŒããããslimekãµãŒããŒãã¹ã©ã€ã çšã«èµ·åãããŸãã
(in-package :cl-user) (quicklisp:quickload "swank") (quicklisp:quickload "usocket") (quicklisp:quickload "bordeaux-threads") (quicklisp:quickload "trivial-utf-8") (quicklisp:quickload "cl-log") (quicklisp:quickload "local-time") (pushnew '*default-pathname-defaults* asdf:*central-registry*) (asdf:load-system 'myweb) (swank:create-server)
éçºãç¶ããã«ã¯ãswankããã§ã«å®è¡ããquicklispã䜿çšããŠå¿
èŠãªãã¹ãŠã®ã©ã€ãã©ãªãããŒãããå¿
èŠããããŸãã ãããè¡ãã«ã¯ãmywebãã£ã¬ã¯ããªã§sbclãå®è¡ããé¢æ°ãåŒã³åºããŸãïŒquicklispïŒquickloadâ swankâïŒã swankãã€ã³ã¹ããŒã«ããåŸãsbclã³ãã³ãã©ã€ã³ããïŒswankïŒcreate-serverïŒãåŒã³åºããŠswankãµãŒããŒãèµ·åããŸãã
emacsããã¹ã©ã€ã æ¥ç¶ã䜿çšããŠãå®è¡äžã®sbclã«æ¥ç¶ããemacsã®ã¹ã©ã€ã ã¢ãŒããšctrl-eããŒã®çµã¿åããã䜿çšããŠãload.lispããquicklispã䜿çšããŠä»ã®ãã¹ãŠã®æ©èœãåŒã³åºããŸãã ãã¹ãŠãæ£ããè¡ã£ãå Žåãquicklispã¯å¿
èŠãªãã¹ãŠã®ã©ã€ãã©ãªãããŠã³ããŒãããasdfã䜿çšããŠããããããŒãããŸãã ãã¹ãŠãéçºãéå§ããæºåãã§ããŠããŸãã
WebãµãŒããŒèªäœããå§ããŸãããã 圌ã«ã¯ãœã±ãããå¿
èŠã§ãã ç§ã¯ãåºã䜿çšãããŠãã
usocketã©ã€ãã©ãªã䜿çšããŠãœã±ãããæäœããããšã«ã
ãŸãã ã ã¹ã¬ãããå¿
èŠã«ãªããŸãããã®ããã«
bordeaux-threadsã䜿çšããŸãã ããããæåã«ãäœæããhttp-requestã®åŠçã¢ãã«ã«ã€ããŠã話ããããšæããŸãã åãªã¯ãšã¹ãã¯åå¥ã®ã¹ã¬ããã§åŠçãããŸãã ãªã¯ãšã¹ãã®æ°ã«å¿ããŠäœæãããã¯ãŒã«ãŒã¹ã¬ããããããŸãã ãã®äžã«ã¯ãåå¥ã®ã¢ã€ãã«ã¹ããªãŒã ãããããªã¯ãšã¹ããåŠçããåŸãæ¡ä»¶åŸ
æ©ç¶æ
ã«ãªããæ°ãããªã¯ãšã¹ããåŸ
æ©ããŸãã ãããã£ãŠãæ°ããã¯ãŒã«ãŒã¹ã¬ãããäœæããè² æ
ã軜æžã§ããŸãã HTTPãªã¯ãšã¹ããåŠçããããã®äžçš®ã®ã¹ã¬ããããŒã«ã¡ã«ããºã ãå€æããŸããã
web.lispãã¡ã€ã«ã§mutexã®ãœã±ãããšå€æ°ã宣èšããããšããå§ããŸãããã
(defvar *listen-socket* nil) (defvar *listen-thread* nil) (defvar *request-mutex* (make-lock "request-mutex")) (defvar *request-threads* (list)) (defvar *worker-mutex* (make-lock "worker-mutex")) (defvar *workers* (list)) (defvar *worker-num* 0) (defvar *idle-workers* (list)) (defvar *idle-workers-num* 0) (defvar *request-queue* (list))
ãªã¯ãšã¹ããã¹ã¬ããéã§åãå
¥ããŠåæ£ãããããã«ãå¥ã®ã¹ã¬ããã䜿çšããŸãããã®ãã€ã³ã¿ãŒãžã®ãã€ã³ã¿ãŒã¯* listen-thread *ã«ä¿åãããŸãã start-httpã¡ãœããããå§ããŸãããïŒ
(defun start-http (host port &key (worker-limit 10) (idle-workers 1)) (if (not *listen-socket*) (setq *listen-thread* (make-thread (lambda () (http-acceptor host port worker-limit idle-workers)) :name "socket-acceptor")) "http server already started"))
ããã¯ããã£ã¹ãã³ãµãŒã¹ããªãŒã ãéå§ããåçŽãªé¢æ°ã§ãããhttp-acceptoré¢æ°ãåŒã³åºããŸãã ãŸãã2ã€ã®ããŒããããŸã-ããã¯worker-limit-ã¯ãŒã«ãŒã®æ倧æ°ãããã³idle-workers-ã¢ã€ãã«ã¯ãŒã«ãŒã®æ°ã§ãã
ã¯ãšãªé
åžé¢æ°èªäœãèšè¿°ããŸãããã
(defun http-acceptor (host port worker-limit idle-workers) (setq *listen-socket* (socket-listen host port :reuse-address t :element-type '(unsigned-byte 8) :backlog (* worker-limit 2))) (let ((request-id 0) (worker-id 0)) (loop while *listen-thread* do (let* ((socket (socket-accept *listen-socket* :element-type '(unsigned-byte 8)))) (progn (setq request-id (1+ request-id)) (acquire-lock *worker-mutex*) (if (>= *worker-num* worker-limit) (push (cons request-id socket) *request-queue*)
æåã«è¡ãããšã¯ãæå®ãããã¢ãã¬ã¹ãšããŒããžã®ãœã±ãããªãã¹ã³ã§ãã ããã«ã«ãŒãå
ã§socket-acceptãå®è¡ããæ¥ç¶ãããã¯ã©ã€ã¢ã³ãã«ãœã±ãããäœæããŸããããã¯ã¯ãŒã«ãŒã§åŠçããå¿
èŠããããŸãã ããã«ãrequest-idããªã¯ãšã¹ãã«å²ãåœãŠãŸãã ãã®æ®µéã§ããªã¯ãšã¹ãã®åŠçæ¹æ³ãšåŠçæ¹æ³ã決å®ããå¿
èŠããããŸãã ãŸããã¢ã€ãã«ã¹ã¬ããã®æ°ã確èªããŸãã ãã¹ãŠã®ã¯ãŒã«ãŒãããžãŒã®å Žåããªã¯ãšã¹ããåŠçã®ããã«ãã¥ãŒã«è¿œå ããŸãã 空ãã¢ã€ãã«ã¯ãŒã«ãŒãããå Žåã¯ããªã¯ãšã¹ããåã³ãã¥ãŒã«è¿œå ããŸãããä»åã¯ïŒcondition-notifyïŒcaar * idle-workers *ïŒïŒïŒãåŒã³åºããŸãã 3çªç®ã®ã±ãŒã¹ã§ã¯ãæ°ããã¯ãŒã«ãŒãäœæããŠãªã¯ãšã¹ããæž¡ãã ãã§ãã¯ãŒã«ãŒã¹ã¬ããé¢æ°ã§åŠçãããŸãã ãã¹ãŠãéåžžã«ç°¡åã§ãã ã¯ãŒã«ãŒã¹ããªãŒã ãåŠçããããã®é¢æ°ãèšè¿°ããã ãã§ãã
(defun worker-thread (request-id socket idle-workers) (if request-id
request-idã§åŒã³åºãããã£ãå Žåãæåã«èŠæ±ãåŠçããå¿
èŠããããŸãã åã«è£å©é¢æ°http-workerãåŒã³åºããŠãã¯ã©ã€ã¢ã³ããœã±ãããããã«æž¡ããŸãã 次ã«ãåŠçèŠæ±ããŸã ãããã©ããããã§ãã¯ããŸããæåã®èŠæ±ããã¥ãŒããåé€ããåŠçã®ããã«ã¯ãŒã«ãŒã¹ã¬ããã«æž¡ãã ãã§ãã¯ãŒã«ãŒã¹ã¬ããé¢æ°ãååž°çã«åŒã³åºããŸãã 質åãçºçããå ŽåããããŸããããã¥ãŒã«å€æ°ã®ãªã¯ãšã¹ããããå Žåãªã©ãããæç¹ã§ã¹ã¿ãã¯ããªãŒããŒãããŒãããšååž°å¶éãçºçããŸããïŒãworker-threadãåŒã³åºããåŸãé¢æ°ã§äœãååž°çã«åŒã³åºãããªããããååž°å¶éã¯çºçããŸããã ã»ãšãã©ãã¹ãŠã®ææ°ã®Common Lispå®è£
ã¯ããã®æé©åããµããŒãããŠããŸãã ãã¥ãŒã空ã®å Žåãã¢ã€ãã«ã¯ãŒã«ãŒã®æ°ã確èªããå¿
èŠããããŸãã ãã¹ãŠãåé¡ãªãå Žåããªã¯ãšã¹ããçµäºããã¯ãŒã«ãŒã®ãªã¹ãããã¯ãŒã«ãŒãåé€ããŸãã ããã§ãªãå Žåã¯ãæ¡ä»¶åŸ
æ©ãè¡ããã¯ãŒã«ãŒã¯ã¢ã€ãã«ã¯ãŒã«ãŒã«ãªããŸãã
ãæ°ã¥ãã®å Žåã¯ããªã¹ãã¯ãŒã«ãŒãåŒã³åºããŸãã ããã¯ããããã¹ã¬ããã®ã¯ãŒã«ãŒãªã¹ããåçŽã«æ¶å»ãããã«ããŒé¢æ°ã§ãã
http-workeré¢æ°ãæžãããšã¯æ®ã£ãŠããŸãïŒ
(defun http-worker (socket) (let* ((stream (socket-stream socket)) (request (myweb.util:parse-request stream))) (myweb.handler:process-request request stream) (finish-output stream) (socket-close socket))) (defun list-workers () (with-lock-held (*worker-mutex*) (setq *workers* (remove-if (lambda (w) (not (thread-alive-p w))) *workers*)) (setq *worker-num* (length *workers*)) *workers*))
ããã§ãœã±ããã¹ããªãŒã ãäœæãããªã¯ãšã¹ãã解æããŠmyweb.handlerã«æž¡ããŸãïŒprocess-requestïŒãããã®æ©èœã«ã€ããŠã¯2çªç®ã®éšåã§èª¬æããŸãïŒã list-workersã¯åçŽã«ã¯ãŒã«ãŒã®ãªã¹ããè¿ãã以åã«ãããã¹ã¬ãããã¯ãªã¢ããŸããã condition-waitã®åã«worker-threadã§ãã®é¢æ°ãåŒã³åºããŸãã
æåŸã«è¡ãå¿
èŠãããã®ã¯ãWebãµãŒããŒãåæ¢ããstop-httpé¢æ°ãäœæããããšã§ãã
(defun stop-http () (if *listen-socket* (progn (stop-thread) (socket-close *listen-socket*) (setq *listen-socket* nil) (setq *request-queue* nil) (setq *worker-num* 0) (setq *workers* nil) (mapcar (lambda (i) (destroy-thread (cdr i))) *idle-workers*) (setq *idle-workers-num* 0) (setq *idle-workers* nil) (release-lock *worker-mutex*) (setq *request-threads* nil) (release-lock *request-mutex*) (setq *request-mutex* (make-lock "request-mutex")) (setq *worker-mutex* (make-lock "worker-mutex"))))) (defun stop-thread () (if (and *listen-thread* (thread-alive-p *listen-thread*)) (destroy-thread *listen-thread*)))
ã芧ã®ãšãããããã§ã¯ãã¹ãŠãã·ã³ãã«ã§ãããã£ã¹ãã³ãµãŒã®ãããŒãåæ¢ãããã¹ãŠã®ã¯ãŒã«ãŒã殺ããŠãªã¹ãããŒãã«ããŸãã
ãããã£ãŠããã¹ãŠããªã¯ãšã¹ããåŠçããæºåãã§ããŠããŸãã ããã«ã€ããŠã¯ã
第2éšã§èª¬æããŸãã
ãæž
èŽããããšãããããŸããïŒ
PSã¹ãã«ãã§ãã¯ãšã¬ã€ã¢ãŠãã«é¢ãããã«ããããããšã
ertaquo