Skip to content

Commit

Permalink
Merge pull request #229 from shanbay/feature/multiprocessing
Browse files Browse the repository at this point in the history
add multiprocessing mode server
  • Loading branch information
fenngwd authored Jun 24, 2022
2 parents 37eff53 + 3ac543c commit a2adf8b
Show file tree
Hide file tree
Showing 23 changed files with 487 additions and 123 deletions.
12 changes: 12 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[flake8]
# https://github.com/ambv/black/blob/master/.flake8
# E203 whitespace before ':'
# E266 Too many leading '#' for block comment
# E501 Line too long
# W503 Line break occurred before a binary operator
ignore = E203, E266, E501, W503, W504, B950
max-line-length = 88
max-complexity = 18
exclude =
.git,
tests,
6 changes: 6 additions & 0 deletions .gitlint
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[general]
# Ignore certain rules (comma-separated list), you can reference them by
# their id or by their full name
ignore=body-is-missing

contrib=contrib-title-conventional-commits
2 changes: 2 additions & 0 deletions .isort.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[settings]
known_third_party = app,blinker,cachext,celery,configs,google,grpc,helloworld_pb2,helloworld_pb2_grpc,peewee,peeweext,pendulum,pkg_resources,pytest,sentry_sdk,setuptools,worldhello_pb2,worldhello_pb2_grpc
34 changes: 29 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,31 @@
repos:
- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
rev: v7.0.1
- repo: "https://github.com/pre-commit/pre-commit-hooks"
rev: v2.2.3
hooks:
- id: commitlint
stages: [commit-msg]
additional_dependencies: ["@commitlint/config-conventional"]
- id: trailing-whitespace
- id: end-of-file-fixer
- id: no-commit-to-branch
args: [ --branch, master, --branch, develop ]
- repo: https://github.com/asottile/seed-isort-config
rev: v1.9.1
hooks:
- id: seed-isort-config
- repo: "https://github.com/pre-commit/mirrors-isort"
rev: v5.10.1
hooks:
- id: isort
- repo: "https://github.com/ambv/black"
rev: 22.3.0
hooks:
- id: black
language_version: python3
exclude: "(tests)"
- repo: "https://github.com/pre-commit/pre-commit-hooks"
rev: v2.2.3
hooks:
- id: flake8
exclude: "(tests)"
- repo: https://github.com/jorisroovers/gitlint
rev: v0.17.0
hooks:
- id: gitlint
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ install:
- pip list
- pip install -r test-requirements.txt -U
script:
- echo $(git log -1 --pretty=%B) > latest_commit_msg; pre-commit run --hook-stage commit-msg commitlint --commit-msg-filename latest_commit_msg; rm -rf latest_commit_msg
- gitlint --commits "8d1d969b..HEAD"
- flake8 sea --exclude=*_pb2.py
- pytest tests/test_contrib/test_extensions/test_celery.py::test_celery_no_app --cov-fail-under=10
- pytest tests
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
# CHANGELOG

## [Unreleased]

### Added
- Added multiprocessing worker class
### Changed
- Use cached_property from standard lib when possible


## [2.3.3] - 2022-04-22
### Added
- Added `post_ready` signal that will be sended after sea app is ready
Expand Down
3 changes: 0 additions & 3 deletions commitlint.config.js

This file was deleted.

2 changes: 2 additions & 0 deletions docs/_sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@
- 内置扩展
- [celery](contrib/celery)
- [Sentry](contrib/sentry)
- 性能测试
- [benchmark](benchmark)
Binary file added docs/assets/images/2022-06-23-latency.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/images/2022-06-23-qps.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions docs/benchmark.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# 性能测试

## 测试结果(2022-06-23)

测试配置
发行版:5.15.41-1-MANJARO
CPU: AMD Rayzen 2600
Memory: 16GB DDR4

测试参数
并发:[50,100,200,300]
时长:30s

延迟比较
![Latency](/docs/assets/images/2022-06-23-latency.jpg "Latency")

QPS比较
![QPS](/docs/assets/images/2022-06-23-qps.jpg "QPS")
2 changes: 0 additions & 2 deletions sea/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ def create_app(root_path=None):
app_class = import_string("app:App")
_app = app_class(root_path, env=env)
_app.config.from_object(config)
# only filter default configurations
_app.config.load_config_from_env()

_app.load_middlewares()
_app.load_extensions_in_module(import_string("app.extensions"))
Expand Down
81 changes: 48 additions & 33 deletions sea/app.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import inspect
import logging
import os
import os.path
import sys

from sea import exceptions, utils
from sea.config import Config, ConfigAttribute
from sea.datatypes import ImmutableDict, ConstantsObject
from sea.datatypes import ConstantsObject, ImmutableDict

if sys.version_info.minor >= 8:
from functools import cached_property
Expand All @@ -19,47 +20,63 @@ class BaseApp:
:param root_path: the root path
:param env: the env
"""

config_class = Config
debug = ConfigAttribute('DEBUG')
testing = ConfigAttribute('TESTING')
tz = ConfigAttribute('TIMEZONE')
default_config = ImmutableDict({
'DEBUG': False,
'TESTING': False,
'TIMEZONE': 'UTC',
'GRPC_WORKERS': 3,
'GRPC_HOST': '[::]',
'GRPC_PORT': 6000,
'GRPC_LOG_LEVEL': 'WARNING',
'GRPC_LOG_HANDLER': logging.StreamHandler(),
'GRPC_LOG_FORMAT': '[%(asctime)s %(levelname)s in %(module)s] %(message)s', # NOQA
'GRPC_GRACE': 5,
'PROMETHEUS_SCRAPE': False,
'PROMETHEUS_PORT': 9091,
'MIDDLEWARES': [
'sea.middleware.RpcErrorMiddleware'
]
})
testing = ConfigAttribute("TESTING")
tz = ConfigAttribute("TIMEZONE")
default_config = ImmutableDict(
{
"TESTING": False,
"TIMEZONE": "UTC",
"GRPC_WORKERS": 4,
"GRPC_THREADS": 1, # Only appliable in multiprocessing server
"GRPC_WORKER_MODE": "threading", # Worker mode. threading|multiprocessing
"GRPC_HOST": "0.0.0.0",
"GRPC_PORT": 6000,
"GRPC_LOG_LEVEL": "WARNING",
"GRPC_LOG_HANDLER": logging.StreamHandler(),
"GRPC_LOG_FORMAT": "[%(asctime)s %(levelname)s in %(module)s] %(message)s", # NOQA
"GRPC_GRACE": 5,
"PROMETHEUS_SCRAPE": False,
"PROMETHEUS_PORT": 9091,
"MIDDLEWARES": ["sea.middleware.RpcErrorMiddleware"],
}
)

def __init__(self, root_path, env):
if not os.path.isabs(root_path):
root_path = os.path.abspath(root_path)
self.root_path = root_path
self.name = os.path.basename(root_path)
self.env = env
self.debug = (
os.environ.get("SEA_DEBUG") not in (None, "0", "false", "no")
or env == "development"
)
self.config = self.config_class(root_path, self.default_config)
self._servicers = {}
self._extensions = {}
self._middlewares = []

def _setup_root_logger(self):
fmt = self.config["GRPC_LOG_FORMAT"]
lvl = self.config["GRPC_LOG_LEVEL"]
h = self.config["GRPC_LOG_HANDLER"]
h.setFormatter(logging.Formatter(fmt))
root = logging.getLogger()
root.setLevel(lvl)
root.addHandler(h)

@cached_property
def logger(self):
logger = logging.getLogger('sea.app')
self._setup_root_logger()

logger = logging.getLogger("sea.app")
if self.debug and logger.level == logging.NOTSET:
logger.setLevel(logging.DEBUG)
if not utils.logger_has_level_handler(logger):
h = logging.StreamHandler()
h.setFormatter(logging.Formatter('%(message)s'))
h.setFormatter(logging.Formatter("%(message)s"))
logger.addHandler(h)
return logger

Expand Down Expand Up @@ -88,16 +105,15 @@ def _register_servicer(self, servicer):
"""
name = servicer.__name__
if name in self._servicers:
raise exceptions.ConfigException(
'servicer duplicated: {}'.format(name))
raise exceptions.ConfigException("servicer duplicated: {}".format(name))
add_func = self._get_servicer_add_func(servicer)
self._servicers[name] = (add_func, servicer)

def _get_servicer_add_func(self, servicer):
for b in servicer.__bases__:
if b.__name__.endswith('Servicer'):
if b.__name__.endswith("Servicer"):
m = inspect.getmodule(b)
return getattr(m, 'add_{}_to_server'.format(b.__name__))
return getattr(m, "add_{}_to_server".format(b.__name__))

def _register_extension(self, name, ext):
"""register extension
Expand All @@ -107,28 +123,27 @@ def _register_extension(self, name, ext):
"""
ext.init_app(self)
if name in self._extensions:
raise exceptions.ConfigException(
'extension duplicated: {}'.format(name))
raise exceptions.ConfigException("extension duplicated: {}".format(name))
self._extensions[name] = ext

def load_middlewares(self):
mids = ['sea.middleware.GuardMiddleware'] + \
self.config.get('MIDDLEWARES')
mids = ["sea.middleware.GuardMiddleware"] + self.config.get("MIDDLEWARES")
for mn in mids:
m = utils.import_string(mn)
self._middlewares.insert(0, m)
return self.middlewares

def load_extensions_in_module(self, module):
def is_ext(ins):
return not inspect.isclass(ins) and hasattr(ins, 'init_app')
return not inspect.isclass(ins) and hasattr(ins, "init_app")

for n, ext in inspect.getmembers(module, is_ext):
self._register_extension(n, ext)
return self.extensions

def load_servicers_in_module(self, module):
for _, _servicer in inspect.getmembers(module, inspect.isclass):
if _servicer.__name__.endswith('Servicer'):
if _servicer.__name__.endswith("Servicer"):
self._register_servicer(_servicer)
return self.servicers

Expand Down
Loading

0 comments on commit a2adf8b

Please sign in to comment.