äœæ¥ã§bjoernã䜿çšãããã£ãã®ã§ãããæ確ãªäŸãå«ããããžã§ã¯ãã®å®å šãªå ¬åŒããã¥ã¡ã³ããã©ãã«ãèŠã€ãããŸããã§ããã ãããžã§ã¯ãã«readthedocs.orgãŸãã¯ä»ã®å Žæã«éåžžã®ããã¥ã¡ã³ããããããŒãžããããã©ããã
ç³ãèš³ãããŸãããããã®æç¹ã§äŸããåŠã¶å¿ èŠã¯ãããŸããã ããã¯WSGIãµãŒããŒãªã®ã§ã䜿çšæ³ã¯ããªãèªæã§ããå¿ èŠããããŸããïŒ
ããªãã«ãšã£ãŠæãããªããšã¯ä»ã®äººã«ãšã£ãŠã¯æçœã§ã¯ãªããããããŸãã-ãããåé¡ã§ãããçµéšãšç¥èã®éãã§ãã ãã¹ãŠã®äººãéããŸãã ãã¡ããããããã¯åãªãWSGIãµãŒããŒã§ãããšèšãã®ã¯æ£ããã§ãããããã§ãã䜿çšãã補åã«é¢ããããã¥ã¡ã³ããããããã®äœ¿çšæ¹æ³ãçœé»ã§æžãããŠãããšãã©ãã»ã©çŽ æŽããããã«ã€ããŠåæããã§ããããããªãã¯äœ¿ãããšãã§ããŸãããããŠã©ã®ãããªå Žåã«å€±ã䟡å€ãããããŸãããããããŠè§Šããšãäœãèµ·ãã£ãŠããã®ãããããŠã©ãã«è¡ãã®ãæ³åãã€ããªãã圌ã®é¡ã«ã¶ã€ããç²ç®ã®åç«ã®ããã«æããã®ã¯ã©ãã»ã©ã²ã©ãã§ãã
ç§ã¯ãããžã§ã¯ãã®ããã¥ã¡ã³ããŒã·ã§ã³ã®äŸ¡å€ã絶察ã«èªããŸãããã確ãã«ãbjoernãã§ããååã«æ·±å»ãªæçãã補åã«ã¯ãå°ãªããšã圢æãããã³ãã¥ããã£ãšã®é¢ä¿ã§è¯ãããŒã³ãšããŠå¿
èŠã§ãããšä¿¡ããŠããŸãã
åæããŸããããããã¯å€§æè¿ã§ãïŒ :)
ãã®æç¹ã§äŸããåŠã¶å¿ èŠããããŸãã
ã©ã®ãããªäŸã§ããïŒ
ãã¹ãð¬
åæããŸããããããã¯å€§æè¿ã§ãïŒ :)
@jonashaagããããæäŸããã«ã¯ãååãªèœåãå¿ èŠã§ãã ããªãã¯ããããå©ããããšãã§ããŸããïŒ
ããªãã®äŸãããç§ã¯æ¬¡ã®ãªãã·ã§ã³ãæšæž¬ããããšãã§ããŸãïŒ
import bjoern
from datetime import datetime
HOST = '0.0.0.0'
PORT = 8080
def app(e, s):
s('200 OK', [])
return str(datetime.now()).encode('utf-8')
try:
bjoern.run(app, HOST, PORT)
except KeyboardInterrupt:
pass
import bjoern
import os, signal
from datetime import datetime
HOST = '0.0.0.0'
PORT = 8080
N_WORKERS = 2
worker_pids = []
def app(e, s):
s('200 OK', [])
return b'%i: %s' % (
os.getpid(),
str(datetime.now()).encode('utf-8')
)
bjoern.listen(app, HOST, PORT)
for _ in range(N_WORKERS):
pid = os.fork()
if pid > 0: # parent
worker_pids.append(pid)
elif pid == 0: # worker
try:
bjoern.run()
except KeyboardInterrupt:
pass
exit()
try:
for _ in range(N_WORKERS):
os.wait()
except KeyboardInterrupt:
for pid in worker_pids:
os.kill(pid, signal.SIGINT)
è€æ°ã®ã¹ã¬ããïŒãããããã¹ãã¢ãªã³ã°ã®åä¿¡ããšåŒã°ãããã®ïŒãå®è¡ããŠããæ©èœããªãããã§ãã
import bjoern
from datetime import datetime
import threading
HOST = '0.0.0.0'
PORT = 8080
N_THREADS = 2
def app(e, s):
s('200 OK', [])
return b'%s: %s' % (
threading.current_thread().name,
datetime.now()
)
sock = bjoern.listen(app, HOST, PORT, reuse_port=True)
for i in range(0, N_THREADS):
t = threading.Thread(target=bjoern.server_run, args=[sock, app])
t.start()
$ python multiple-threads.py
Assertion failed: ("libev: a signal must not be attached to two different loops", !signals [w->signum - 1].loop || signals [w->signum - 1].loop == loop) (ev.c: ev_signal_start: 4565)
Aborted (core dumped)
ãããŠãç§ãç解ããŠããªãããšãããã€ããããŸãã
ç§ã®ç解ã§ã¯ã bjoern
å¹æçã«äœ¿çšããã«ã¯ãã¢ããªã±ãŒã·ã§ã³ã³ãŒããéåæã«ããå¿
èŠããããŸãã bjoern
ã¯æ段ãæäŸããŸããã ãµãŒãããŒãã£ã®ã©ã€ãã©ãªã䜿çšããå¿
èŠããããŸããïŒ äœããå§ãã¯ãããŸããïŒ bjoern
ã€ãã³ãã«ãŒãããã®ããã«åå©çšããŸããïŒ
ã¢ããªã±ãŒã·ã§ã³ã³ãŒããéåæïŒdbããã¡ã€ã«ããããã¯ãŒã¯ïŒã§ãããå€ãã®èšç®ïŒCPUïŒãè¡ããªãå Žåã bjoern
ãé©ããŠããŸããïŒ ã€ãŸãããŠãŒã¹ã±ãŒã¹ã¯äœã§ããïŒ ããããããããããã§ãè€æ°ã®ã¯ãŒã«ãŒãå®è¡ããããšãæãŸããã§ãããã ãã以å€ã®å Žåã¯ãæ倧ã§1ã€ã®CPUã³ã¢ã䜿çšãããŸãã äœãã¢ããã€ã¹ã¯ãããŸããïŒ CPUã³ã¢ããšã«1人ã®ã¯ãŒã«ãŒïŒ
çç£æºåã¯ã§ããŠããŸããïŒ
ããã¹ãããå®è¡ããŠãããšãã«ç§ãèŠã€ããããã€ãã®åé¡ïŒ
AttributeError: module 'bjoern' has no attribute 'features'
httplib
ã䜿çšãããã¹ãïŒãããŠãããããç§ã®å°æ¥ã®èªå·±ã®ããã®ã¡ã¢ïŒ
$ docker run --rm -itv $PWD:/app -w /app alpine sh
/ # apk add build-base libev-dev git python3-dev py3-requests
/ # cd app
/app # git clone https://github.com/jonashaag/bjoern
/app # cd bjoern
/app/bjoern # git submodule update --init
/app/bjoern # python3 setup.py install
ãã¡ãã ïŒïŒ
äŸ1ãš2ã¯åé¡ãããŸãããã次ã®ããã«ãªããŸãã
1ïŒäŸå€ãç¡èŠããããšã¯ãå§ãããŸãã
2ïŒãã®åŸos.exec*
å®è¡ããªãå Žåã¯ãããã»ã¹ããã©ãŒã¯ããããšããå§ãããŸããã ãŠãŒã¹ã±ãŒã¹ã§ã¯æ©èœãããããããŸããããbjoernãšã¯é¢ä¿ã®ãªãçç±ãããããã»ã©å
ç¢ã§ã¯ãããŸããã䜿çšããŠããä»ã®ã©ã€ãã©ãªã®äžã«ã¯ãforkã§ã¯ããŸãæ©èœããªããã®ããããŸãã
3ïŒããªãã¯èª€è§£ããã«éããããŸããã åä¿¡ã¹ãã¢ãªã³ã°ã¯ã SO_REUSEPORT
ïŒ bjoern.run(..., reuse_port=True)
ïŒã䜿çšããŠãåãããŒãã§è€æ°ã®å®å
šã«å¥åã®ããã»ã¹ãå®è¡ããããšã§ãã ããã«åãPythonããã»ã¹ã䜿çšããããšã¯ãå§ãããŸããïŒãã©ãŒã¯ãªã©ã䜿çšïŒããOSããã»ã¹ãå®å
šã«åé¢ããŸãã ã芧ã®ãšãããlibevã®å¶éã«ãããã¹ã¬ããã¯æ©èœããŸããã
ç§ã®ç解ã§ã¯ã
bjoern
å¹æçã«äœ¿çšããã«ã¯ãã¢ããªã±ãŒã·ã§ã³ã³ãŒããéåæã«ããå¿ èŠããããŸããbjoern
ã¯æ段ãæäŸããŸããã ãµãŒãããŒãã£ã®ã©ã€ãã©ãªã䜿çšããå¿ èŠããããŸããïŒ äœããå§ãã¯ãããŸããïŒbjoern
ã€ãã³ãã«ãŒãããã®ããã«åå©çšããŸããïŒ
ã¯ããšãããã Bjoernã¯ãçµæã€ãã¬ãŒã¿é
ç®ãé
延èšç®ããããšãé€ããŠãã¢ããªã±ãŒã·ã§ã³ãé©åã«éåæåããæ¹æ³ãæäŸããŠããŸããïŒã€ãŸããã³ãŒãã§èšç®ã§ããããã«ããã«ã¯ãå€ãã®yield
ã¹ããŒãã¡ã³ãã䜿çšããããä»ã®æ¹æ³ã§èšè¿°ããå¿
èŠããããŸããçµæã1ã€ãã€ïŒïŒ
def async_example():
piece1 = dostuff1()
yield b"" # or a non-empty intermediate result
piece2 = dostuff2()
yield b"". # or a non-empty intermediate result
piece3 = dostuff3()
result = combine_pieces(piece1, piece2, piece3)
yield result
éåæã€ã³ã¿ãŒãã§ãŒã¹ãæäŸããªãçç±ã¯ã99ïŒ ã®Webã¢ããªã±ãŒã·ã§ã³ãããã°ã©ãã³ã°ããã®ã«ããããè¯ãéžæã§ã¯ãªããšæãããã§ãã ãããã¯å€ãã®åé¡ãšã¹ãã²ããã£ã³ãŒããåŒãèµ·ãããå©ç¹ã¯éåžžã«ç¹å®ã®ã¿ã€ãã®ã¢ããªã±ãŒã·ã§ã³ïŒI / OããŠã³ã+éåžžã«å€§èŠæš¡ïŒã«éå®ãããŸãã
ãã ããWebãµãŒããŒã®å ŽåãéåæI / Oã䜿çšããããšã¯éåžžã«çã«ããªã£ãŠããŸããããšãã°ãäžéšã®ã¯ã©ã€ã¢ã³ããé ãå Žåã§ããã¯ãŒã«ãŒãè¿œå ããªããŠãèŠæ±ãåŠçãç¶ããããšãã§ããŸãïŒããã§ãªãå ŽåããµãŒããŒããããã¯ãããããµãŒããŒãå æãããããŸãïŒããããã®ã¹ããŒã¿ã¹ããã§ãã¯ããããã®ãªãœãŒã¹ïŒã
ãããã£ãŠãbjoernã¯éåæã€ãã³ãã«ãŒãã䜿çšããŠå®è£ ãããŸãããã¢ããªã±ãŒã·ã§ã³ã«éåæã€ã³ã¿ãŒãã§ã€ã¹ãæäŸããŸããã
ã¢ããªã±ãŒã·ã§ã³ã³ãŒããéåæïŒdbããã¡ã€ã«ããããã¯ãŒã¯ïŒã§ãããå€ãã®èšç®ïŒCPUïŒãè¡ããªãå Žåãbjoernãé©ããŠããŸããïŒ ã€ãŸãããŠãŒã¹ã±ãŒã¹ã¯äœã§ããïŒ ããããããããããã§ãè€æ°ã®ã¯ãŒã«ãŒãå®è¡ããããšãæãŸããã§ãããã ãã以å€ã®å Žåã¯ãæ倧ã§1ã€ã®CPUã³ã¢ã䜿çšãããŸãã äœãã¢ããã€ã¹ã¯ãããŸããïŒ CPUã³ã¢ããšã«1人ã®ã¯ãŒã«ãŒïŒ
ããã¯ããããã99ïŒ ã®ã¢ããªã±ãŒã·ã§ã³ã«é©åºŠã«é«éãªWebãµãŒããŒãšåããããé©ããŠããŸãã çè«çã«ã¯ããã®çš®ã®ã¯ãŒã¯ããŒãã¯ãéåæã€ã³ã¿ãŒãã§ã€ã¹ãæäŸããWebãµãŒããŒã«ã¯ããã«é©ããŠããŸãã
åŽåè ã®æ°ã«é¢ããæšå¥šäºé ã¯ãããŸããã ã¢ããªã±ãŒã·ã§ã³ã«ãã£ãŠç°ãªããŸãã ã³ã¢/ã¹ã¬ããããšã«1人ã®ã¯ãŒã«ãŒããå§ããã®ã劥åœãªããã§ãã
çç£æºåã¯ã§ããŠããŸããïŒ
ããããªãã ç§ã¯ãã®ãããžã§ã¯ããçŽ10幎åã«ãCããœã±ãããããã³Python CAPIãåŠã¶æ¹æ³ãšããŠéå§ããŸããã ç§ã¯è€æ°ã®ãããžã§ã¯ãã§åé¡ãªãæ¬çªç°å¢ã§äœ¿çšããŠãããä»ã®äººãåé¡ãªã䜿çšããŠãããšæããŸãããã»ãšãã©ã®äŒæ¥ã¯ãuWSGIãgunicornãªã©ã®æåã§å®éã«æŠéã§ãã¹ãããããµãŒããŒã«åºå·ããŠãããšæããŸãïŒãã€ã§ãåãããšãããããšããå§ãããŸããïŒ
statsdãæ©èœããŸããïŒå¥ã®ããŒãã®ãªãã¹ã³ãéå§ããŸããïŒ
statsdã¯ããã·ã¥ã䜿çšããŸããã ã¯ã©ã€ã¢ã³ãã¯ãµãŒããŒã«ã¡ããªãã¯ãéä¿¡ããŸããïŒ
AttributeErrorïŒã¢ãžã¥ãŒã« 'bjoern'ã«ã¯å±æ§ 'features'ããããŸãã
䜿çšããŠããããŒãžã§ã³ãªã©ã§ããã®ããã®æ°ããåé¡ãéãããšãã§ããŸããïŒ
äžéšã®ãã¹ãã¯Python3ïŒhttplibã䜿çšãããã¹ãïŒã§ã¯æ©èœããŸãã
確ãã«ããããã¯å€§æè¿ã§ã:)
Dockerãã¡ã€ã«ã«ã€ããŠã¯ãPRãéä¿¡ãããšãAlpineããŒã¹ã®ã€ã¡ãŒãžããã¹ã¿ãŒã«ããŒãžã§ããŸãã :)
äŸå€ãç¡èŠããããšã¯ãå§ãããŸãã
ããã§KeyboardInterrupt
æå³ããã®ã§ã¯ãªããšæããŸãã ãããããäŸå€ã«ãã£ãŠããã»ã¹ãã¯ã©ãã·ã¥ããªãããã«ããããšããå§ãããŸãã ãããã app()
ã§äŸå€ãçºçãããããšããŸããããã¯ã©ãã·ã¥ããŸããã§ããã
ãŸãããã®åŸos.exec *ãå®è¡ããªãå Žåã¯ãããã»ã¹ããã©ãŒã¯ããããšã¯ãå§ãããŸããã
ããŠãããã¯ç§ãã¡ã«äžããŸãïŒ
master.py
ïŒ
import bjoern
import signal
import subprocess
N_WORKERS = 2
workers = [subprocess.Popen(['python', 'worker.py']) for i in range(N_WORKERS)]
try:
for w in workers:
w.wait()
except KeyboardInterrupt:
pass
worker.py
ïŒ
import os
from datetime import datetime
import bjoern
HOST = '0.0.0.0'
PORT = 8080
def app(e, s):
print('%s: %s' % (datetime.now(), e['PATH_INFO']))
s('200 OK', [])
return b'%i: %s\n' % (
os.getpid(),
str(datetime.now()).encode('utf-8')
)
try:
bjoern.run(app, HOST, PORT, reuse_port=True)
except KeyboardInterrupt:
pass
ããããä»ãããã»ã¹ãçµäºããã®ãã©ã®ããã«åŸ
ã€ã®ã§ããïŒ ããããå®æçã«poll()
ã«ãªãå¯èœæ§ããããŸããããã£ãšè¯ãæ¹æ³ãããã®ã§ã¯ãªãã§ããããã
éåæã€ã³ã¿ãŒãã§ãŒã¹ãæäŸããªãçç±ã¯ã99ïŒ ã®Webã¢ããªã±ãŒã·ã§ã³ãããã°ã©ãã³ã°ããã®ã«ããããè¯ãéžæã§ã¯ãªããšæãããã§ãã
æ確ã«ããããã«ãç§ã¯æè¿ãŸã§äžŠè¡æ§ãããŸãæ°ã«ããŸããã§ããã ã©ããããããããŸããããŸããã ç§ãç¥ã£ãŠããã®ã¯ãããã1ã€ã®ã¹ã¬ãããŸãã¯ããã»ã¹ã ãã§ã¯ãªãã£ããšããããšã ãã§ãã
ã€ãŸãã bjoern
ã¯ã app()
é¢æ°ã¯ã¯ãªãã£ã«ã«ã»ã¯ã·ã§ã³ã®ãããªãã®ã§ãããåã®é¢æ°ãçµäºãããŸã§åŒã³åºãããªããšããããšã§ããïŒ ãããŠã99ïŒ
ã®Webã¢ããªã±ãŒã·ã§ã³ã§ã¯ããã®ãããªå¶çŽã¯å€§ããªåœ±é¿ãäžããŸãããïŒ æ確ã«ããããã«ãç§ã¯æŠããå§ããã€ããã¯ãããŸããã bjoern
äžã§èµ°ã£ãŠãããšãã«äœã倧äžå€«ããç¥ãããã ãã§ãã
ãµã€ãã¯éåžžäœãããŸããïŒ åœŒãã¯ããŒã¿ããŒã¹ã«åãåãããŸãã ãªã¢ãŒããµãŒãã¹ïŒhttpãªã¯ãšã¹ãïŒãšéä¿¡ããŸãã ç»åã®ããªãã³ã°/ãµã€ãºå€æŽãããã¯ããããCPUã«äŸåããçš®é¡ã®è² è·ã§ãã æè¿ãç§ãããŸãç¥ããªãWebãœã±ããããããŸãã ãããã¯ããããããã§æãé¢é£æ§ã®é«ã掻åã§ãã
ãããŠãããªãã¯ããããã¹ãŠãbjeorn
倧äžå€«ã ãšæããŸããïŒ ã€ãŸããé·æéå®è¡ãããã¯ãšãªãããå Žåãè€æ°ã®ã¯ãŒã«ãŒãå®è¡ããªããšãWebãµãŒããŒå
šäœããããã¯ãããŸãã ãµã€ããéä¿¡ãããµãŒãããŒãã£ã®ãµãŒããŒãå¿çãé
ããå§ããå Žåãåãããšãèµ·ãããŸãã ãããã£ãŠãåäžã®ããã»ã¹ãå®è¡ããããšã¯ãåé¡ãæ±ããããã«èãããŸãã 次ã«ãäžéšã®WebãµãŒããŒã¯ãè€æ°ã®ã¯ãŒã«ãŒã¹ã¬ãããå®è¡ããè€æ°ã®ã¯ãŒã«ãŒããã»ã¹ãå®è¡ã§ããŸãã ããã¯ããããã n_processes
* n_threads
ã¯ãŒã«ãŒããã»ã¹ãå®è¡ãããããã¡ã¢ãªå¹çãé«ããªããŸãã ãŸããè² è·ã倧ãããªããšïŒè² è·ã«åãããŠïŒãè¿œå ã®ã¯ãŒã«ãŒã¹ã¬ããïŒãŸãã¯è¿œå ã®ã¯ãŒã«ãŒããã»ã¹ïŒãçæã§ãããã®ããããŸãã bjoern
ã«ã¯æ¹æ³ããããããããŸãããã...é ã®ãŠã£ãºããã...äœãæãæµ®ãã³ãŸããã
ããã¯ããªãã®çããå€ããŸããïŒ ãããã«ãããéçãã¡ã€ã«ãæäŸããã«ã¯ããããããªããŒã¹ãããã·ïŒ nginx
ïŒïŒãå¿
èŠã§ããïŒ
statsdã¯ããã·ã¥ã䜿çšããŸããã ã¯ã©ã€ã¢ã³ãã¯ãµãŒããŒã«ã¡ããªãã¯ãéä¿¡ããŸããïŒ
å®éãç§ã¯statsd
ã«ã€ããŠäœãç¥ããŸããã statsd={'host': '127.0.0.1', 'port': 8888, ...}
èŠãŠããã¹ããéå§ããããŒã8888ã§äœããªãã¹ã³ããŠããªãã®ãèŠãŠã BJOERN_WANT_STATSD=yes
ã§ãã«ãããŸããããå¹æã¯ãããŸããã§ããã ç§ã®æªããããããã
ããã§
KeyboardInterrupt
æå³ããã®ã§ã¯ãªããšæããŸã
ç³ãèš³ãããŸããããããã¯ãŸãã«ç§ãæå³ããããšãªã®ã§ãããå ·äœçã«ããå¿ èŠããããŸã:)
ããŠãããã¯ç§ãã¡ã«äžããŸãïŒ[...]
ããããä»ãããã»ã¹ãçµäºããã®ãã©ã®ããã«åŸ ã€ã®ã§ããïŒ ããããå®æçã«poll()
ã«ãªãå¯èœæ§ããããŸããããã£ãšè¯ãæ¹æ³ãããã®ã§ã¯ãªãã§ããããã
ããã¯åé¡ãªãããã«èŠããŸãããéåžžããããã®ããã»ã¹ãåŠçããé©åãªããã»ã¹ãããŒãžã£ãŒãå¿ èŠã§ãã ä»ã®ã¢ããªã±ãŒã·ã§ã³ããã»ã¹ãåŠçããããã»ã¹ãããŒãžã£ãŒã䜿çšããã ãã§ããŸããŸããããŸã 䜿çšããŠããªãå Žåã¯ãsupervisordãsystemdãªã©ã䜿çšã§ããŸãã
åã®ãã®ãçµäºãããŸã§åŒã³åºãããŸãã
ã¯ãïŒ
ã€ãŸããé·æéå®è¡ãããã¯ãšãªãããå Žåãè€æ°ã®ã¯ãŒã«ãŒãå®è¡ããªããšãWebãµãŒããŒå šäœããããã¯ãããŸãã ãµã€ããéä¿¡ãããµãŒãããŒãã£ã®ãµãŒããŒãå¿çãé ããå§ããå Žåãåãããšãèµ·ãããŸãã ãããã£ãŠãåäžã®ããã»ã¹ãå®è¡ããããšã¯ãåé¡ãæ±ããããã«èãããŸãã 次ã«ãäžéšã®WebãµãŒããŒã¯ãè€æ°ã®ã¯ãŒã«ãŒã¹ã¬ãããå®è¡ããè€æ°ã®ã¯ãŒã«ãŒããã»ã¹ãå®è¡ã§ããŸãã ããã¯ããããã
n_processes
*n_threads
ã¯ãŒã«ãŒããã»ã¹ãå®è¡ãããããã¡ã¢ãªå¹çãé«ããªããŸãã ãŸããè² è·ã倧ãããªããšïŒè² è·ã«åãããŠïŒãè¿œå ã®ã¯ãŒã«ãŒã¹ã¬ããïŒãŸãã¯è¿œå ã®ã¯ãŒã«ãŒããã»ã¹ïŒãçæã§ãããã®ããããŸãã
ããªããèšã£ãŠããããšã¯æ£ããã§ãã å ·äœçã«ã¯ãã»ãšãã©ã®å Žåãåå¥ã®ããã»ã¹ãããã¹ã¬ããã䜿çšããæ¹ãã¡ã¢ãªå¹çãé«ããªããŸãïŒãã ããforkã䜿çšãããšã¡ã¢ãªãããããç¯çŽã§ããŸãããforkã«ã¯ä»ã®æ¬ ç¹ããããŸãïŒã ãããã£ãŠãã¢ããªã±ãŒã·ã§ã³ã®ã¡ã¢ãªãããããªã³ãã倧ããå Žåã¯ãå¥ã®ãµãŒããŒã䜿çšããã»ããããã§ãããã ãšã³ãžãã¢ãªã³ã°æéãå€§å¹ ã«ç¯çŽããããã«ã¡ã¢ãªãè¿œå è³Œå ¥ã§ããå Žåãé€ããŸã:)
ãå€éšãµãŒãã¹ãåŸ ã€ããšããç¹ã«ã€ããŠã¯ãå®éã®æå³ã¯éãããŠãããšæããŸãã å®éã«ã¯ãå€éšãµãŒãã¹ã«ã¯å®¹éå¶éãããïŒããšãã°ã1ç§ãããã«åŠçãããèŠæ±ïŒãã¢ããªã±ãŒã·ã§ã³ãæ§ç¯ãããã¯ãããžãè©äŸ¡ãããšãã«ããããèæ ®ã«å ¥ããå¿ èŠããããŸãã ãããã£ãŠãå®å šã«éåæã®ã¢ããªã±ãŒã·ã§ã³ã䜿çšãããšãå€éšãµãŒãã¹ã«å¯ŸããŠ1ç§ãããããå€ãã®ãªã¯ãšã¹ããè¡ãããšãã§ããŸããïŒçè«çã«ã¯ïŒããµãŒãã¹ã®å¿çããã以äžéããªãããšã¯ãªããå®äºãŸã§ã®åèšæéã¯åããŸãŸã§ãã ãªã¯ãšã¹ãã¯ãµãŒãã¹ã®ãã¥ãŒã«èç©ããããµãŒãã¹å šäœãéè² è·ã«ãªãå¯èœæ§ããããŸãã èå§ãäžè¶³ã
質åã¯; ããã«ããã¯ã¯ã©ãã«ãããŸããïŒ ã¢ããªã±ãŒã·ã§ã³ãé ãããåå ã¯äœã§ããïŒ å€éšãµãŒãã¹ãåŸ ã£ãŠããŸããïŒ æ¬¡ã«ããããã®ãµãŒãã¹ãæé©åããŸãã ã¢ããªã±ãŒã·ã§ã³ãå®è¡ã§ããåæãªã¯ãšã¹ãã®æ°ã¯éãããŠããŸããïŒ æ¬¡ã«ããããæé©åããŸãïŒããšãã°ãéåæã«åãæ¿ããããšã«ãã£ãŠïŒã
éåæããã°ã©ãã³ã°ã§ã¯ããããã®éåæãã©ãã€ã ã䜿çšããã ãã§ãç¡å¶éã®åæå®è¡æ§ããç¡æãã§ååŸã§ããããã«èŠããå ŽåããããŸãã ããããããªãŒã©ã³ãã¯ãããŸããã éåæããã°ã©ãã³ã°ã«ã¯ãåæããã°ã©ãã³ã°ãè¡ããšãã«å¯ŸåŠããå¿ èŠã®ãªãä»ã®å€ãã®æ¬ ç¹ããããŸãã æçµçã«åé¡ãšãªãã®ã¯ãç¹å®ã®ã¢ããªã±ãŒã·ã§ã³ã§äœãæ©èœãããããããŠãšã³ãžãã¢ãªã³ã°æéãšãµãŒããŒã³ã¹ãã«è²»ããããéãã©ãã ãéèŠãããã§ãã å人çã«ã¯ãéåæããã°ã©ãã³ã°ã¯ããããç¹å®ã®ããã©ãŒãã³ã¹ã®åé¡ã解決ãããšããçµéšããããã»ãšãã©ã®ã¢ããªã±ãŒã·ã§ã³ã³ãŒãã«äœ¿çšããããšã¯ãå§ãããŸããã
ãããã«ãããéçãã¡ã€ã«ãæäŸããã«ã¯ããããããªããŒã¹ãããã·ïŒ
nginx
ïŒïŒãå¿ èŠã§ããïŒ
WSGIã¢ããªã±ãŒã·ã§ã³ãwsgi.file_wrapper
ãµããŒãããŠããå Žåã¯ãbjoernã䜿çšããŠéçãã¡ã€ã«ãæäŸã§ããŸãïŒã»ãšãã©ã®ãã¬ãŒã ã¯ãŒã¯ã¯ãµããŒãããŠããŸãïŒã ãã ãã倧èŠæš¡ãªæ¬çªã¢ããªã±ãŒã·ã§ã³ã®å Žåã¯ãã©ã®ãããªå Žåã§ããªããŒã¹ãããã·ã䜿çšããããšãåžžã«ãå§ãããŸãã
bjoern
ãå®è¡ããæ¹æ³ãæ¹åãç¶ããããšãèšç»ããŠããŸãã ä»ã®ãšããdocker
ã§ãã ã³ã¡ã³ãã¯å€§æè¿ã§ãã
docker-compose.yml
ïŒ
version: '3'
x-defaults: &defaults
restart: always
logging:
options:
max-size: 50m
max-file: '5'
services:
app:
<<: *defaults
image: python:alpine
command: python /server.py
volumes:
- ./server.py:/server.py
deploy:
replicas: 2
# nginx:
# <<: *defaults
# image: nginx:alpine
# volumes:
# - ./nginx.conf:/etc/nginx/conf.d/default.conf
# ports:
# - 8888:80
haproxy:
<<: *defaults
image: haproxy:alpine
ports:
- 8888:80
volumes:
- ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg
nginx.conf
ïŒ
server {
location / {
proxy_pass http://app:8080;
}
}
server.py
ïŒ
#!/usr/bin/env python
import socket
from http.server import HTTPServer, BaseHTTPRequestHandler
class MyHTTPRequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.end_headers()
self.wfile.write(socket.gethostname().encode('ascii'))
httpd = HTTPServer(('', 8080), MyHTTPRequestHandler)
httpd.serve_forever()
haproxy.cfg
ïŒ
listen in
# mode http
bind :80
server-template srv 2 app:8080 check # resolvers docker_resolver
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
# resolvers docker_resolver
# nameserver dns 127.0.0.11:53
ãŸãã¯bjoern
ïŒ
docker-compose.yml
ïŒ
...
app:
<<: *defaults
build: .
command: python /server.py
volumes:
- ./server.py:/server.py
deploy:
replicas: 2
...
Dockerfile
ïŒ
FROM python:alpine
ENV PYTHONUNBUFFERED 1
RUN apk add --no-cache build-base libev-dev \
&& pip install bjoern
server.py
ïŒ
import os
import socket
from datetime import datetime
import bjoern
HOST = '0.0.0.0'
PORT = 8080
def app(e, s):
print('%s - - [%s] "%s %s %s" 200 -' % (
e['REMOTE_ADDR'],
datetime.now(),
e['REQUEST_METHOD'],
e['PATH_INFO'],
e['SERVER_PROTOCOL']))
s('200 OK', [])
return socket.gethostname().encode('utf-8')
print('starting bjoern (%s:%s)' % (HOST, PORT))
bjoern.run(app, HOST, PORT)
å
責äºé
ã haproxy
ã䜿ãã®ã¯åããŠã§ãã æ§æãæé©ã§ãªãå¯èœæ§ããããŸãã
äŸå€ãç¡èŠããããšã¯ãå§ãããŸãã
ããã§KeyboardInterruptãæå³ããã®ã§ã¯ãªããšæããŸã
ç³ãèš³ãããŸããããããã¯ãŸãã«ç§ãæå³ããããšãªã®ã§ãããå ·äœçã«ããå¿ èŠããããŸã:)
ãããããã§ã¯KeyboardInterrupt
ãç¡èŠããããšã®äœãåé¡ã«ãªã£ãŠããŸããïŒ ãµãŒããŒãåæ¢ãããšãã«ã¹ã¿ãã¯ãã¬ãŒã¹ã衚瀺ãããªãããã«ãè¿œå ãããããã®ãŸãŸã«ããŠãããŸããã
ã¢ããªã±ãŒã·ã§ã³ãå®è¡ã§ããåæãªã¯ãšã¹ãã®æ°ã¯éãããŠããŸããïŒ æ¬¡ã«ããããæé©åããŸãïŒããšãã°ãéåæã«åãæ¿ããããšã«ãã£ãŠïŒã
ãããã©ã®ããã«ç¢ºèªããã°ããã®ã§ãããã...ä»»æã®æç¹ã§bjoern
ã«ãã£ãŠãã¥ãŒã«å
¥ããããŠãããªã¯ãšã¹ãã®æ°ã確èªããæ¹æ³ã¯ãããŸããïŒ nginx
ïŒ äœãææ¡ããŠããããŸããïŒ
bjoern
ããã³systemd
server.py
ïŒ
import os
from datetime import datetime
import systemd.daemon
import bjoern
HOST = '0.0.0.0'
PORT = 8080
def app(e, s):
print('%s: %s' % (datetime.now(), e['PATH_INFO']))
s('200 OK', [])
return b'%i: %s\n' % (
os.getpid(),
str(datetime.now()).encode('utf-8')
)
listen_fds = systemd.daemon.listen_fds()
if listen_fds:
bjoern.server_run(listen_fds[0], app)
else:
bjoern.run(app, HOST, PORT)
/etc/systemd/system/bjoern.service
ïŒ
[Unit]
Description=Bjoern server
[Service]
WorkingDirectory=/path/to/prj/root
ExecStart=/path/to/prj/root/env/bin/python server.py
Restart=always
/etc/systemd/system/bjoern.socket
ïŒ
[Unit]
Description=Socket for the Bjoern server
[Socket]
ListenStream=8080
[Install]
WantedBy=sockets.target
ããã§ã¯ãéåžžã®å®è¡æïŒ systemctl start
ïŒãšãœã±ããã®ã¢ã¯ãã£ãåã®çµæã®äž¡æ¹ã§æ©èœããããã«ããŸãã åŸè
ã®å Žåãæããã«ãœã±ãããéå§ããå¿
èŠããããŸãã
$ python -m venv env
$ ./env/bin/pip install systemd-python
$ systemctl start bjoern.socket
$ curl -sS localhost:8080
ãããæ©èœãããæ¹æ³ãããããã§ããã€ãŸããCGIã¢ãŒãïŒ Accept=yes
ãæ¥ç¶ããšã®æ°ããããã»ã¹ïŒã§ãã ãããããã®è¯ããŠãŒã¹ã±ãŒã¹ãèãããšäœãæãæµ®ãã³ãŸããã ã©ã¡ãã®å Žåãé©åã§ã¯ãªãããã§ãïŒäœããã®çç±ã§ããã©ãŒãã³ã¹ãç ç²ã«ããææããªãéãïŒã ãããŠããŸããç§ã¯ããããã£ã1ã€ã®ãªã¯ãšã¹ããåŠçããããã«ããæ¹æ³ããç¥ããŸããã ãããã£ãŠããã®å Žåã®æ瀺ã¯ãããŸããã
ãããŠã3çªç®ã®ãªãã·ã§ã³ã§ãããã³ãã¬ãŒããŠããããã¡ã€ã«ããããŸãã ã©ã¡ããæ¡çšããããšãã§ããŸãããããããã®ã¿ã¹ã¯ã«é©ããŠãããã©ããã¯ããããŸããã ãã®çµæãå
šäœãšããŠå¶åŸ¡ã§ããªãåå¥ã®ãµãŒãã¹ãããã€ããããŸãã ãããããšã«ããç§ãã¡ãæã£ãŠãããã³ãã¬ãŒããŠããããã¡ã€ã«ã§ã¯ïŒãœã±ããã®ã¢ã¯ãã£ãåã¯ãªãã reuse_port=True
éšåã«æ³šæããŠãã ããïŒïŒ
server.py
ïŒ
import os
from datetime import datetime
import bjoern
HOST = '0.0.0.0'
PORT = 8080
def app(e, s):
print('%s: %s' % (datetime.now(), e['PATH_INFO']))
s('200 OK', [])
return b'%i: %s\n' % (
os.getpid(),
str(datetime.now()).encode('utf-8')
)
bjoern.run(app, HOST, PORT, reuse_port=True)
/etc/systemd/system/[email protected]
ïŒ
[Unit]
Description=Bjoern server
[Service]
WorkingDirectory=/path/to/prj/root
ExecStart=/path/to/prj/root/env/bin/python server.py
Restart=always
[Install]
WantedBy=multi-user.target
$ systemctl start bjoern<strong i="37">@1</strong>
$ systemctl start bjoern<strong i="38">@2</strong>
$ curl -sS localhost:8080
ã¢ããªã±ãŒã·ã§ã³ãå®è¡ã§ããåæãªã¯ãšã¹ãã®æ°ã¯éãããŠããŸããïŒ æ¬¡ã«ããããæé©åããŸãïŒããšãã°ãéåæã«åãæ¿ããããšã«ãã£ãŠïŒã
ã©ããã£ãŠãã§ãã¯ãããããã®ããªâŠ
æãç°¡åãªæ¹æ³ã¯ã1ã€ã®ã€ã³ã¹ã¿ã³ã¹ããå§ããŠã1ç§ãããã«åŠçã§ãããªã¯ãšã¹ãã®æ°ã確èªãã次ã«2ã€ã®ã€ã³ã¹ã¿ã³ã¹ãªã©ã確èªããŠããã以äžã®æ¹åãèŠãããªããªãããã«ããããšã ãšæããŸãã ãŸããåãã¹ãã®ã¡ã¢ãªãšCPUã®äœ¿çšçã確èªããŠãã ããã ã¡ã¢ãªãŸãã¯CPUã飜åãããããšãã§ããå Žåã¯ããã倧ããªãã·ã³ããŸãã¯ããå€ãã®ãã·ã³ãå¿ èŠã§ãããããã¯ããªãœãŒã¹ãæ£ç¢ºã«ã©ãã§äœ¿çšãããŠããããææ¡ããå¿ èŠããããŸãã
ã¡ãªã¿ã«ãå±éèšå®ãwikiã«èªç±ã«è¿œå ããŠãã ããïŒ
æãåèã«ãªãã³ã¡ã³ã
ããªãã«ãšã£ãŠæãããªããšã¯ä»ã®äººã«ãšã£ãŠã¯æçœã§ã¯ãªããããããŸãã-ãããåé¡ã§ãããçµéšãšç¥èã®éãã§ãã ãã¹ãŠã®äººãéããŸãã ãã¡ããããããã¯åãªãWSGIãµãŒããŒã§ãããšèšãã®ã¯æ£ããã§ãããããã§ãã䜿çšãã補åã«é¢ããããã¥ã¡ã³ããããããã®äœ¿çšæ¹æ³ãçœé»ã§æžãããŠãããšãã©ãã»ã©çŽ æŽããããã«ã€ããŠåæããã§ããããããªãã¯äœ¿ãããšãã§ããŸãããããŠã©ã®ãããªå Žåã«å€±ã䟡å€ãããããŸãããããããŠè§Šããšãäœãèµ·ãã£ãŠããã®ãããããŠã©ãã«è¡ãã®ãæ³åãã€ããªãã圌ã®é¡ã«ã¶ã€ããç²ç®ã®åç«ã®ããã«æããã®ã¯ã©ãã»ã©ã²ã©ãã§ãã
ç§ã¯ãããžã§ã¯ãã®ããã¥ã¡ã³ããŒã·ã§ã³ã®äŸ¡å€ã絶察ã«èªããŸãããã確ãã«ãbjoernãã§ããååã«æ·±å»ãªæçãã補åã«ã¯ãå°ãªããšã圢æãããã³ãã¥ããã£ãšã®é¢ä¿ã§è¯ãããŒã³ãšããŠå¿ èŠã§ãããšä¿¡ããŠããŸãã