惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The Last Watchdog
The Last Watchdog
博客园_首页
Martin Fowler
Martin Fowler
S
SegmentFault 最新的问题
美团技术团队
小众软件
小众软件
V
V2EX
博客园 - Franky
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
The GitHub Blog
The GitHub Blog
Microsoft Security Blog
Microsoft Security Blog
Attack and Defense Labs
Attack and Defense Labs
S
Security Affairs
Simon Willison's Weblog
Simon Willison's Weblog
I
Intezer
T
The Exploit Database - CXSecurity.com
有赞技术团队
有赞技术团队
S
Schneier on Security
人人都是产品经理
人人都是产品经理
Security Archives - TechRepublic
Security Archives - TechRepublic
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
K
Kaspersky official blog
PCI Perspectives
PCI Perspectives
AI
AI
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
罗磊的独立博客
O
OpenAI News
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
The Register - Security
The Register - Security
V
Vulnerabilities – Threatpost
GbyAI
GbyAI
博客园 - 【当耐特】
C
Cisco Blogs
大猫的无限游戏
大猫的无限游戏
Help Net Security
Help Net Security
Google DeepMind News
Google DeepMind News
S
Securelist
Application and Cybersecurity Blog
Application and Cybersecurity Blog
P
Proofpoint News Feed
博客园 - 三生石上(FineUI控件)
雷峰网
雷峰网
L
LangChain Blog
SecWiki News
SecWiki News
博客园 - 叶小钗
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
V2EX - 技术
V2EX - 技术
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
J
Java Code Geeks
L
LINUX DO - 热门话题
Cisco Talos Blog
Cisco Talos Blog

Ivan on Containers, Kubernetes, and Server-Side

A grounded take on agentic coding for production environments Server-Side Playgrounds Reimagined: Build, Boot, and Network Your Own Virtual Labs [not a] Kubernetes 101 - Pods, Deployments, and Services As an Attempt To Automate Age-Old Infra Patterns JavaScript or TypeScript? How To Benefit From the Dichotomy On Software Design... and Good Writing Building a Firecracker-Powered Course Platform To Learn Docker and Kubernetes How To Publish a Port of a Running Container What Actually Happens When You Publish a Container Port A Visual Guide to SSH Tunnels: Local and Remote Port Forwarding Debugging Containers Like a Pro Docker: How To Debug Distroless And Slim Containers How To Extract Container Image Filesystem Using Docker | iximiuz Labs In Pursuit of Better Container Images: Alpine, Distroless, Apko, Chisel, DockerSlim, oh my! How To Start Programming In Go: Advice For Fellow DevOps Engineers Kubernetes Ephemeral Containers and kubectl debug Command How To Develop Kubernetes CLIs Like a Pro Docker Container Commands Explained: Understand, Don't Memorize | iximiuz Labs Learning Docker with Docker - Toying With DinD For Fun And Profit How To Extend Kubernetes API - Kubernetes vs. Django The Influence of Plumbing on Programming How To Call Kubernetes API from Go - Types and Common Machinery How To Call Kubernetes API using Simple HTTP Client Kubernetes API Basics - Resources, Kinds, and Objects OpenFaaS - Run Containerized Functions On Your Own Terms Learning Containers From The Bottom Up Docker Containers vs. Kubernetes Pods - Taking a Deeper Look | iximiuz Labs Learn-by-Doing Platforms for Dev, DevOps, and SRE Folks How HTTP Keep-Alive can cause TCP race condition How to Work with Container Images Using ctr | iximiuz Labs Multiple Containers, Same Port, no Reverse Proxy... Exploring Go net/http Package - On How Not To Set Socket Options Disposable Local Development Environments with Vagrant, Docker, and Arkade DevOps, SRE, and Platform Engineering My Choice of Programming Languages Prometheus Is Not a TSDB How to learn PromQL with Prometheus Playground Prometheus Cheat Sheet - Basics (Metrics, Labels, Time Series, Scraping) Rust - Writing Parsers With nom Parser Combinator Framework pq - parse and query log files as time series Prometheus Cheat Sheet - Moving Average, Max, Min, etc (Aggregation Over Time) Prometheus Cheat Sheet - How to Join Multiple Metrics (Vector Matching) The Need For Slimmer Containers Understanding Rust Privacy and Visibility Model Bridge vs. Switch: Takeaways from a Real Data Center Tour | iximiuz Labs From LAN to VXLAN: Networking Basics for Non-Network Engineers | iximiuz Labs KiND - How I Wasted a Day Loading Local Docker Images Go, HTTP handlers, panic, and deadlocks Exploring Kubernetes Operator Pattern Making Sense Out Of Cloud Native Buzz Service Discovery in Kubernetes: Combining the Best of Two Worlds API Developers Never REST How Container Networking Works: Building a Bridge Network From Scratch | iximiuz Labs Traefik: canary deployments with weighted load balancing Service Proxy, Pod, Sidecar, oh my! You Need Containers To Build Images You Don't Need an Image To Run a Container Not Every Container Has an Operating System Inside Working with container images in Go Master Go While Learning Containers Implementing Container Runtime Shim: Interactive Containers How to use Flask with gevent (uWSGI and Gunicorn editions) My 10 Years of Programming Experience Implementing Container Runtime Shim: First Code Implementing Container Runtime Shim: runc Kubernetes Repository On Flame Dealing with process termination in Linux (with Rust examples) conman - [the] Container Manager: Inception Journey From Containerization To Orchestration And Beyond Linux PTY - How docker attach and docker exec Commands Work Inside Illustrated introduction to Linux iptables From Docker Container to Bootable Linux Disk Image Пишем свой веб-сервер на Python: протокол HTTP Explaining async/await in 200 lines of code Explaining event loop in 100 lines of code Save the day with gevent Пишем свой веб-сервер на Python: процессы, потоки и асинхронный I/O Truly optional scalar types in protobuf3 (with Go examples) Node.js Writable streams distilled Node.js Readable streams distilled How to on starting processes (mostly in Linux) Дайджест интересных ссылок – Июль 2016 Пишем свой веб-сервер на Python: сокеты Наследование в JavaScript Мастерить!
9001 способ создать веб-сервер на Python
Ivan Velichko · 2019-05-02 · via Ivan on Containers, Kubernetes, and Server-Side

Дополняемый список способов запустить веб-сервер на Python:

Модуль socket и прямые руки

Берем модуль socket из стандартной библиотеки, создаем серверный сокет, принимаем входящие подключения, вычитываем и обрабатываем запросы и отправляем ответы. Все своими руками. Подробнее можно почитать тут.

Синхронный TCP сервер

Возможна обработка только одного клиента в один момент времени, весь код выполяется в одном процессе и одном (главном) потоке.

тыц чтобы смотреть код
# python3

import socket
import sys
import time

def run_server(port=53210):
  serv_sock = create_serv_sock(port)
  cid = 0
  while True:
    client_sock = accept_client_conn(serv_sock, cid)
    serve_client(client_sock, cid)
    cid += 1

def serve_client(client_sock, cid):
  request = read_request(client_sock)
  if request is None:
    print(f'Client #{cid} unexpectedly disconnected')
  else:
    response = handle_request(request)
    write_response(client_sock, response, cid)


def create_serv_sock(serv_port):
  serv_sock = socket.socket(socket.AF_INET,
                            socket.SOCK_STREAM,
                            proto=0)
  serv_sock.bind(('', serv_port))
  serv_sock.listen()
  return serv_sock

def accept_client_conn(serv_sock, cid):
    client_sock, client_addr = serv_sock.accept()
    print(f'Client #{cid} connected '
          f'{client_addr[0]}:{client_addr[1]}')
    return client_sock

def read_request(client_sock, delimiter=b'!'):
  request = bytearray()
  try:
    while True:
      chunk = client_sock.recv(4)
      if not chunk:
        # Клиент преждевременно отключился.
        return None

      request += chunk
      if delimiter in request:
        return request

  except ConnectionResetError:
    # Соединение было неожиданно разорвано.
    return None
  except:
    raise

def handle_request(request):
  time.sleep(5)
  return request[::-1]

def write_response(client_sock, response, cid):
  client_sock.sendall(response)
  client_sock.close()
  print(f'Client #{cid} has been served')


if __name__ == '__main__':
  run_server(port=int(sys.argv[1]))

Многопоточный TCP сервер

Очевидный недостаток синхронного подхода - отсутствие какой бы то ни было конкурентной обработки запросов. Берем модуль threading и начинаем обрабатывать каждый запрос в отдельном потоке.

тыц чтобы смотреть код
# python3

import socket
import sys
import time
import threading

def run_server(port=53210):
  serv_sock = create_serv_sock(port)
  cid = 0
  while True:
    client_sock = accept_client_conn(serv_sock, cid)
    t = threading.Thread(target=serve_client,
                         args=(client_sock, cid))
    t.start()
    cid += 1

def serve_client(client_sock, cid):
  request = read_request(client_sock)
  if request is None:
    print(f'Client #{cid} unexpectedly disconnected')
  else:
    response = handle_request(request)
    write_response(client_sock, response, cid)

def create_serv_sock(serv_port):
  serv_sock = socket.socket(socket.AF_INET,
                            socket.SOCK_STREAM,
                            proto=0)
  serv_sock.bind(('', serv_port))
  serv_sock.listen()
  return serv_sock

def accept_client_conn(serv_sock, cid):
  client_sock, client_addr = serv_sock.accept()
  print(f'Client #{cid} connected '
        f'{client_addr[0]}:{client_addr[1]}')
  return client_sock

def read_request(client_sock, delimiter=b'!'):
  request = bytearray()
  try:
    while True:
      chunk = client_sock.recv(4)
      if not chunk:
        # Клиент преждевременно отключился.
        return None

      request += chunk
      if delimiter in request:
        return request

  except ConnectionResetError:
    # Соединение было неожиданно разорвано.
    return None
  except:
    raise

def handle_request(request):
  time.sleep(5)
  return request[::-1]

def write_response(client_sock, response, cid):
  client_sock.sendall(response)
  client_sock.close()
  print(f'Client #{cid} has been served')


if __name__ == '__main__':
  run_server(port=int(sys.argv[1]))

Пул потоков-обработчиков

Недостаток подхода с потоком на каждый входящий запрос - накладные расходы на запуск новых потоков. Вводим пул потоков-обработчиков, минимум N из которых не должны завершаться после завершения обработки запроса, получаем сокращение накладных расходов на запуск.

# TODO: implement me

Многопроцессный TCP сервер

Альтернативный (и исторически более ранний) взгляд на обработку - каждый запрос в отдельном процессе. Берем низкоуровневый os.fork() и клонируем главный процесс сервера на каждый входящий запрос.

тыц чтобы смотреть код
# python3

import os
import socket
import sys
import time

def run_server(port=53210):
  serv_sock = create_serv_sock(port)
  active_children = set()
  cid = 0
  while True:
    client_sock = accept_client_conn(serv_sock, cid)
    child_pid = serve_client(client_sock, cid)
    active_children.add(child_pid)
    reap_children(active_children)
    cid += 1

def serve_client(client_sock, cid):
  child_pid = os.fork()
  if child_pid:
    # Родительский процесс, не делаем ничего
    client_sock.close()
    return child_pid

  # Дочерний процесс:
  #  - читаем запрос
  #  - обрабатываем
  #  - записываем ответ
  #  - закрываем сокет
  #  - завершаем процесс (os._exit())
  request = read_request(client_sock)
  if request is None:
    print(f'Client #{cid} unexpectedly disconnected')
  else:
    response = handle_request(request)
    write_response(client_sock, response, cid)
  os._exit(0)

def reap_children(active_children):
  for child_pid in active_children.copy():
    child_pid, _ = os.waitpid(child_pid, os.WNOHANG)
    if child_pid:
      active_children.discard(child_pid)

def create_serv_sock(serv_port):
  serv_sock = socket.socket(socket.AF_INET,
                            socket.SOCK_STREAM,
                            proto=0)
  serv_sock.bind(('', serv_port))
  serv_sock.listen()
  return serv_sock

def accept_client_conn(serv_sock, cid):
  client_sock, client_addr = serv_sock.accept()
  print(f'Client #{cid} connected '
        f'{client_addr[0]}:{client_addr[1]}')
  return client_sock

def read_request(client_sock, delimiter=b'!'):
  request = bytearray()
  try:
    while True:
      chunk = client_sock.recv(4)
      if not chunk:
        # Клиент преждевременно отключился.
        return None

      request += chunk
      if delimiter in request:
        return request

  except ConnectionResetError:
    # Соединение было неожиданно разорвано.
    return None
  except:
    raise

def handle_request(request):
  time.sleep(5)
  return request[::-1]

def write_response(client_sock, response, cid):
  client_sock.sendall(response)
  client_sock.close()
  print(f'Client #{cid} has been served')


if __name__ == '__main__':
  run_server(port=int(sys.argv[1]))

Модуль socketserver

Модуль стандартной библиотеки socketserver предназначен для сокращения рутинных действий при написании серверов на Python. Фактически, он предоставляет те же возможности, что и сервера в разделе Модуль socket и прямые руки, но... Просто сравните количество кода.

Класс TCPServer

Базовый, но не абстрактный класс для создания TCP сервера. Аналогичен синхронному TCP серверу. Только один клиент в один момент времени. Предоставляет функциональность для запуска сервера, приема входящих соединений и минимальной обработки ошибок. Обработка же запросов вынесена в класс BaseRequestHandler. Для создания обработчика необходимо отнаследоваться от BaseRequestHandler и определить как минимум метод handle().

# python3

import sys
from socketserver import BaseRequestHandler, TCPServer

class MyTCPHandler(BaseRequestHandler):
  def handle(self):
    data = self.request.recv(1024).strip()
    self.request.sendall(data[::-1])


if __name__ == '__main__':
  host = 'localhost'
  port = int(sys.argv[1])

  with TCPServer((host, port), MyTCPHandler) as srv:
    srv.serve_forever()

ThreadingMixIn + TCPServer

С синхронным TCPServer мы опять не можем обрабатывать входящие запросы конкурентно. Берем ThreadingMixIn и получаем многопоточный TCP сервер:

# python3

from socketserver import BaseRequestHandler, ThreadingMixIn, TCPServer

class MyTCPHandler(BaseRequestHandler):
  def handle(self):
    data = self.request.recv(1024)
    self.request.sendall(data[::-1])

# Важная строчка! Эквивалент socketserver.ThreadingTCPServer
# https://github.com/python/cpython/blob/43fc3bb7cf0278735eb0010d7b3043775a120cb5/Lib/socketserver.py#L682
class ThreadedTCPServer(ThreadingMixIn, TCPServer): pass


if __name__ == '__main__':
  host = 'localhost'
  port = int(sys.argv[1])

  with ThreadedTCPServer((host, port), MyTCPHandler) as srv:
    srv.serve_forever()

ForkingMixIn + TCPServer

Аналогично многопоточному серверу можно создать многопроцессный сервер, используя ForkingMixIn.

# Эквивалент socketserver.ForkingTCPServer
# https://github.com/python/cpython/blob/43fc3bb7cf0278735eb0010d7b3043775a120cb5/Lib/socketserver.py#L679
class ForkedTCPServer(ForkingMixIn, TCPServer): pass

Поточная обработка

Когда запрос или ответ слишком большой, держать его целиком в памяти неэффективно, требуется вычитывать или записывать запрос по частям. Еще одна ситуация, когда может пригодиться поточная обработка - пошаговое формирование ответа, когда даже небольшие части ответа имеют ценность для клиента сами по себе и могут быть отправлены клиенту немедленно. Для этих целей идеально подходит класс StreamRequestHandler:

# python3

import sys
from socketserver import StreamRequestHandler, TCPServer

class MyTCPHandler(StreamRequestHandler):
  def handle(self):
    while True:
      chunk = self.rfile.readline()
      if not chunk:
        break
      self.wfile.write(chunk[::-1])


if __name__ == '__main__':
  host = 'localhost'
  port = int(sys.argv[1])

  with TCPServer((host, port), MyTCPHandler) as srv:
    srv.serve_forever()

Модуль asyncio

Синхронная обработка запросов - непозволительная роскошь неэффективная неэффективность. Процессы и потоки - ограниченная конкурентная обработка, подходящая для десятков или сотен одновременных (и быстрых) запросов. Но что делать, если запросов тысячи? Или если каждое соединение может длиться минуты или даже часы (привет, WebSocket-ы)? Всех спасет asyncio - модуль стандартной библиотеки для написания высокопроизводительных сетевых приложений на основе асинхронного ввода-вывода. В частности, asyncio предоставляет реализацию асинхронного TCP сервера:

тыц чтобы смотреть код
# python3

import asyncio
import sys

counter = 0

async def run_server(host, port):
  server = await asyncio.start_server(serve_client, host, port)
  await server.serve_forever()

async def serve_client(reader, writer):
  global counter
  cid = counter
  counter += 1
  print(f'Client #{cid} connected')

  request = await read_request(reader)
  if request is None:
    print(f'Client #{cid} unexpectedly disconnected')
  else:
    response = await handle_request(request)
    await write_response(writer, response, cid)

async def read_request(reader, delimiter=b'!'):
  request = bytearray()
  while True:
    chunk = await reader.read(4)
    if not chunk:
      # Клиент преждевременно отключился.
      break

    request += chunk
    if delimiter in request:
      return request

  return None

async def handle_request(request):
  await asyncio.sleep(5)
  return request[::-1]

async def write_response(writer, response, cid):
  writer.write(response)
  await writer.drain()
  writer.close()
  print(f'Client #{cid} has been served')

if __name__ == '__main__':
  asyncio.run(run_server('127.0.0.1', int(sys.argv[1])))

Модуль http.server

Когда сетевого и транспортного уровня не достаточно и хочется работать на 7 небе уровне модели ISO OSI, поможет модуль стандартной библиотеки http.server. Модуль определяет классы для реализации HTTP серверов. В их числе HTTPServer, являющийся наследником socketserver.TCPServer, и ThreadingHTTPServer на основе ThreadingMixIn.

Этот модуль не для боевого применения! Его использование может привести к вызову произвольного кода на сервере!

В то же время, это удобная и полезная штука, позволяющая с помощью Python и ничего более, запускать HTTP сервер на локальной машине, например, чтобы раздавать статику.

python -m http.server 8000 --bind 127.0.0.1

Пример кастомизации HTTP сервера: отдаем специальный Content-Type заголовок для *.wasm файлов:

# python3

import sys
import http.server
from socketserver import TCPServer

class Handler(http.server.SimpleHTTPRequestHandler):
  pass

Handler.extensions_map['.wasm'] = 'application/wasm'

if __name__ == '__main__':
  host = 'localhost'
  port = int(sys.argv[1])

  with TCPServer((host, port), Handler) as httpd:
    httpd.serve_forever()

Модуль wsgiref

Модуль http.server хоть и предоставляет более высокоуровневую чем TCPServer абстракцию, все же с трудом может быть использован для написания кастомных веб-приложений напрямую. HTTP запросы необходимо парсить и передавать клиентскому приложению уже высокоуровневые примитивы (например, HTTP заголовки удобно представлять в виде dict). Аналогично, получаемые от приложения объекты, представляющие собой HTTP ответы, необходимо сериализовывать перед тем, как отправлять их по сети клиентам. Эта логика может быть непосредственно частью приложения. Но в таком случае ее придется дублировать при написании каждого следующего веб-приложения. Если же единожды договориться о формате взаимодействия веб-сервера и приложения, можно иметь общий интерфейс для всех клиентских приложений, занимающихся обработкой HTTP запросов. Тогда приложения будут отвечать непосредственно за обработку, а между приложением и веб-сервером должен располагаться слой, отвечающий за совместимость.

Web Server Gateway Interface (WSGI) - это стандартный (для Python) интерфейс для взаимодействия веб-сервера и питоно-приложения. Стандартная библиотека предоставляет эталонную реализацию WSGI. В частности, WSGI-совместимый HTTP сервер wsgiref.simple_server на основе уже знакомого нам http.server.


# python3

import sys
from wsgiref.simple_server import make_server

def my_app(environ, start_response):
  status = '200 OK'
  headers = [('Content-type', 'text/plain; charset=utf-8')]
  start_response(status, headers)

  return [b'Hello world!']


if __name__ == '__main__':
  host = 'localhost'
  port = int(sys.argv[1])

  with make_server(host, port, my_app) as srv:
    srv.serve_forever()