4 Commits

Author SHA1 Message Date
a9adfa5cb1 debug 3 2023-07-27 16:08:36 +08:00
d2025c628e debug 2 2023-07-27 16:04:57 +08:00
45e8b692f0 debug 2023-07-27 16:01:14 +08:00
857d26d092 feat: add metrics for nameko services 2023-07-27 14:02:01 +08:00
42 changed files with 145 additions and 187 deletions

View File

@@ -12,41 +12,28 @@ on:
release: release:
types: [published] types: [published]
# permissions:
# contents: read
jobs:
# deploy:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v3
# - name: Set up Python
# uses: actions/setup-python@v3
# with:
# python-version: '3.x'
# - name: Install dependencies
# run: |
# python -m pip install --upgrade pip
# pip install build
# - name: Build package
# run: python -m build
# - name: Publish package
# uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
# with:
# user: ${{ secrets.PYPI_API_USER }}
# password: ${{ secrets.PYPI_API_PASSWORD }}
pypi-publish:
name: upload release to PyPI
runs-on: ubuntu-latest
permissions: permissions:
contents: read contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Set up Python
- uses: pdm-project/setup-pdm@v3 uses: actions/setup-python@v3
with:
- name: Publish package distributions to PyPI python-version: '3.x'
run: pdm publish -u ${{ secrets.PYPI_API_USER }} -P ${{ secrets.PYPI_API_PASSWORD }} - name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install build
- name: Build package
run: python -m build
- name: Publish package
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
with:
user: ${{ secrets.PYPI_API_USER }}
password: ${{ secrets.PYPI_API_PASSWORD }}

2
.gitignore vendored
View File

@@ -108,8 +108,6 @@ ipython_config.py
# in version control. # in version control.
# https://pdm.fming.dev/#use-with-ide # https://pdm.fming.dev/#use-with-ide
.pdm.toml .pdm.toml
.pdm-python
.pdm-build/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/ __pypackages__/

View File

@@ -1,17 +1,8 @@
# namekoplus # namekoplus
[![Upload Python Package](https://github.com/Bryanthelol/namekoplus/actions/workflows/python-publish.yml/badge.svg?event=release)](https://github.com/Bryanthelol/namekoplus/actions/workflows/python-publish.yml)
A lightweight Python distributed microservice solution A lightweight Python distributed microservice solution
## Installation ## Command Line Tool Usage
```shell
python3 -m pip install namekoplus
```
## CLI Usage
### Checkout Command ### Checkout Command
@@ -36,5 +27,5 @@ namekoplus init --directory <dir_name> --type <template_type>
See Documents: See Documents:
- [English](https://legendary-sopapillas-e2626d.netlify.app/)
- [中文](https://doc.bearcatlog.com/) - [中文](https://doc.bearcatlog.com/)
- [English](https://legendary-sopapillas-e2626d.netlify.app/)

1
namekoplus/__init__.py Normal file
View File

@@ -0,0 +1 @@
from namekoplus.chassis.chassis import *

View File

@@ -35,7 +35,8 @@
"dashLength": 10, "dashLength": 10,
"dashes": false, "dashes": false,
"datasource": { "datasource": {
"type": "statsd" "type": "statsd",
"uid": "5qrwjFCnk"
}, },
"fill": 1, "fill": 1,
"fillGradient": 0, "fillGradient": 0,
@@ -46,7 +47,7 @@
"y": 0 "y": 0
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": ${loop.index}, "id": 5,
"legend": { "legend": {
"alignAsTable": true, "alignAsTable": true,
"avg": true, "avg": true,
@@ -91,7 +92,7 @@
"title": "${service_name} | ${grafana_dict['stat_name']}", "title": "${service_name} | ${grafana_dict['stat_name']}",
"tooltip": { "tooltip": {
"shared": true, "shared": true,
"sort": ${loop.index}, "sort": 0,
"value_type": "individual" "value_type": "individual"
}, },
"type": "graph", "type": "graph",
@@ -115,12 +116,7 @@
"yaxis": { "yaxis": {
"align": false "align": false
} }
% if grafana_dict['is_last'] == 1:
} }
% else:
},
% endif
% endfor % endfor
], ],
"refresh": "3s", "refresh": "3s",

View File

@@ -1,3 +1,4 @@
import importlib
import inspect import inspect
import os import os
import shutil import shutil
@@ -9,10 +10,6 @@ import shortuuid
from python_on_whales import DockerException, ClientNotFoundError, DockerClient, docker from python_on_whales import DockerException, ClientNotFoundError, DockerClient, docker
from mako.template import Template from mako.template import Template
INIT_TYPE_CHOICES = ['all', 'rpc', 'event', 'http', 'timer', 'demo']
MIDDLEWARE_CHOICES = ['rabbitmq', 'metrics']
TEST_TYPE_CHOICES = ['unit']
def check_docker(): def check_docker():
""" """
@@ -27,10 +24,9 @@ def check_docker():
click.echo('Please start docker correctly', err=True) click.echo('Please start docker correctly', err=True)
raise raise
# TODO 目前在某些系统环境的检查不准确,暂时去除这个检查逻辑 if not docker.compose.is_installed():
# if not docker.compose.is_installed(): click.echo('Please install docker-compose first', err=True)
# click.echo('Please install docker-compose first', err=True) raise
# raise
@contextmanager @contextmanager
@@ -109,7 +105,7 @@ def start_statsd_agent():
def start_statsd_exporter(): def start_statsd_exporter():
with status(f'Starting statsd exporter'): with status(f'Starting statsd exporter'):
statsd_mapping_file_path = os.getcwd() + '/statsd_mapping.yml' statsd_mapping_file_path = os.path.join('.', 'statsd_mapping.yml')
returned_string = docker.run(image='prom/statsd-exporter:latest', name='statsd-exporter', pull='missing', returned_string = docker.run(image='prom/statsd-exporter:latest', name='statsd-exporter', pull='missing',
detach=True, restart='always', tty=True, hostname='statsd-exporter', detach=True, restart='always', tty=True, hostname='statsd-exporter',
publish=[(9125, 9125, 'udp'), (9102, 9102)], interactive=True, publish=[(9125, 9125, 'udp'), (9102, 9102)], interactive=True,
@@ -136,7 +132,7 @@ def start_grafana():
grafana_conf_dir = os.path.join(get_directory('chassis-agent'), 'metric-configs') grafana_conf_dir = os.path.join(get_directory('chassis-agent'), 'metric-configs')
grafana_provisioning_path = os.path.join(grafana_conf_dir, 'grafana_conf/provisioning') grafana_provisioning_path = os.path.join(grafana_conf_dir, 'grafana_conf/provisioning')
grafana_config_path = os.path.join(grafana_conf_dir, 'grafana_conf/config/grafana.ini') grafana_config_path = os.path.join(grafana_conf_dir, 'grafana_conf/config/grafana.ini')
grafana_dashboard_path = os.path.join(os.getcwd(), 'grafana_dashboards') grafana_dashboard_path = os.path.join(grafana_conf_dir, 'grafana_conf/dashboards')
returned_string = docker.run(image='grafana/grafana:latest', name='grafana', hostname='grafana', returned_string = docker.run(image='grafana/grafana:latest', name='grafana', hostname='grafana',
detach=True, restart='always', tty=True, interactive=True, detach=True, restart='always', tty=True, interactive=True,
publish=[(3100, 3000)], pull='missing', publish=[(3100, 3000)], pull='missing',
@@ -158,7 +154,6 @@ def stop_network(network_name):
def start_metric_servers(): def start_metric_servers():
# TODO 检查相应容器是否已启动,如果启动,则先删除
start_network('metric_servers') start_network('metric_servers')
sleep(0.5) sleep(0.5)
start_prometheus() start_prometheus()
@@ -238,7 +233,7 @@ def cli():
@click.option('-t', '--type', '_type', @click.option('-t', '--type', '_type',
default='all', default='all',
show_default=True, show_default=True,
type=click.Choice(INIT_TYPE_CHOICES, case_sensitive=False), type=click.Choice(['all', 'rpc', 'event', 'http', 'timer', 'demo'], case_sensitive=False),
help='The template type of nameko service') help='The template type of nameko service')
def init(directory, _type): def init(directory, _type):
""" """
@@ -263,7 +258,7 @@ def init(directory, _type):
@cli.command() @cli.command()
@click.option('-m', '--middleware', @click.option('-m', '--middleware',
required=True, required=True,
type=click.Choice(MIDDLEWARE_CHOICES, case_sensitive=False), type=click.Choice(['rabbitmq', 'metrics'], case_sensitive=False),
help='The middleware name') help='The middleware name')
def start(middleware): def start(middleware):
""" """
@@ -276,7 +271,7 @@ def start(middleware):
@cli.command() @cli.command()
@click.option('-m', '--middleware', @click.option('-m', '--middleware',
required=True, required=True,
type=click.Choice(MIDDLEWARE_CHOICES, case_sensitive=False), type=click.Choice(['rabbitmq', 'metrics'], case_sensitive=False),
help='The middleware name') help='The middleware name')
def stop(middleware): def stop(middleware):
""" """
@@ -293,7 +288,7 @@ def stop(middleware):
@click.option('-t', '--type', '_type', @click.option('-t', '--type', '_type',
default='unit', default='unit',
show_default=True, show_default=True,
type=click.Choice(TEST_TYPE_CHOICES, case_sensitive=False), type=click.Choice(['unit'], case_sensitive=False),
help='The test type of the nameko service') help='The test type of the nameko service')
def test_gen(directory, _type): def test_gen(directory, _type):
""" """
@@ -322,17 +317,15 @@ def metric_config_gen(module, class_name_str):
""" """
Generate metric config for nameko services. Generate metric config for nameko services.
""" """
import sys
from statsd.client.timer import Timer from statsd.client.timer import Timer
sys.path.append(os.getcwd())
for root, dirs, files in os.walk(os.getcwd()): import sys
for _dir in dirs: click.echo(f'sys.path: {sys.path}')
sys.path.append(os.path.join(root, _dir))
# Extract information of statsd config from the class of nameko service # Extract information of statsd config from the class of nameko service
dest_dir = module.split('.')[0]
file_name = module.split('.')[-1] file_name = module.split('.')[-1]
_module = __import__(module) _module = importlib.import_module(name='.'+file_name, package=dest_dir)
config_list = [] config_list = []
for class_name in class_name_str.split(','): for class_name in class_name_str.split(','):
members = inspect.getmembers(getattr(getattr(_module, file_name), class_name), predicate=inspect.isfunction) members = inspect.getmembers(getattr(getattr(_module, file_name), class_name), predicate=inspect.isfunction)
@@ -349,7 +342,6 @@ def metric_config_gen(module, class_name_str):
}) })
# Generate one file of statsd config yaml for statsd exporter # Generate one file of statsd config yaml for statsd exporter
with status(f'Creating statsd_mapping.yml'):
metric_configs_dir = os.path.join(get_directory('chassis-agent'), 'metric-configs') metric_configs_dir = os.path.join(get_directory('chassis-agent'), 'metric-configs')
template_file_path = os.path.join(metric_configs_dir, 'statsd_mapping.yml.mako') template_file_path = os.path.join(metric_configs_dir, 'statsd_mapping.yml.mako')
output_file = os.path.join('.', 'statsd_mapping.yml') output_file = os.path.join('.', 'statsd_mapping.yml')
@@ -357,24 +349,14 @@ def metric_config_gen(module, class_name_str):
**{'config_list': config_list}) **{'config_list': config_list})
# Generate files of json for grafana dashboard # Generate files of json for grafana dashboard
if not os.access('grafana_dashboards', os.F_OK):
with status(f'Creating directory {os.path.abspath("grafana_dashboards")!r}'):
os.makedirs('grafana_dashboards')
with status(f'Creating files of Grafana.json into the directory of grafana_dashboards'):
for class_name in class_name_str.split(','): for class_name in class_name_str.split(','):
grafana_list = [] grafana_list = []
for config in config_list: for config in config_list:
if config['class_name'] == class_name: if config['class_name'] == class_name:
grafana_list.append(config) grafana_list.append(config)
for idx, grafana_dict in enumerate(grafana_list):
if idx + 1 == len(grafana_list):
grafana_dict['is_last'] = 1
else:
grafana_dict['is_last'] = 0
grafana_configs_dir = os.path.join(get_directory('chassis-agent'), 'metric-configs') grafana_configs_dir = os.path.join(get_directory('chassis-agent'), 'metric-configs')
grafana_file_path = os.path.join(grafana_configs_dir, 'grafana.json.mako') grafana_file_path = os.path.join(grafana_configs_dir, 'grafana.json.mako')
output_file = os.path.join('grafana_dashboards', f'{class_name}_Grafana.json') output_file = os.path.join(dest_dir, f'{class_name}_Grafana.json')
template_to_file(template_file=grafana_file_path, dest=output_file, output_encoding='utf-8', template_to_file(template_file=grafana_file_path, dest=output_file, output_encoding='utf-8',
**{'service_name': class_name, 'uid': shortuuid.uuid(), **{'service_name': class_name, 'uid': shortuuid.uuid(),
'grafana_list': grafana_list}) 'grafana_list': grafana_list})

View File

@@ -2,8 +2,7 @@ AMQP_URI: pyamqp://${RABBIT_USER:admin}:${RABBIT_PASSWORD:admin}@${RABBIT_HOST:l
WEB_SERVER_ADDRESS: '0.0.0.0:8000' WEB_SERVER_ADDRESS: '0.0.0.0:8000'
RPC_EXCHANGE: 'nameko-rpc' RPC_EXCHANGE: 'nameko-rpc'
max_workers: 20 max_workers: 10
PREFETCH_COUNT: 20
parent_calls_tracked: 20 parent_calls_tracked: 20
LOGGING: LOGGING:

View File

@@ -1,6 +1,5 @@
AMQP_URI: pyamqp://${RABBIT_USER:admin}:${RABBIT_PASSWORD:admin}@${RABBIT_HOST:localhost}:${RABBIT_PORT:5672}/ AMQP_URI: pyamqp://${RABBIT_USER:admin}:${RABBIT_PASSWORD:admin}@${RABBIT_HOST:localhost}:${RABBIT_PORT:5672}/
RPC_EXCHANGE: 'nameko-rpc' RPC_EXCHANGE: 'nameko-rpc'
max_workers: 20 max_workers: 10
PREFETCH_COUNT: 20
parent_calls_tracked: 20 parent_calls_tracked: 20

View File

@@ -1,8 +1,7 @@
AMQP_URI: pyamqp://${RABBIT_USER:admin}:${RABBIT_PASSWORD:admin}@${RABBIT_HOST:localhost}:${RABBIT_PORT:5672}/ AMQP_URI: pyamqp://${RABBIT_USER:admin}:${RABBIT_PASSWORD:admin}@${RABBIT_HOST:localhost}:${RABBIT_PORT:5672}/
RPC_EXCHANGE: 'nameko-rpc' RPC_EXCHANGE: 'nameko-rpc'
max_workers: 20 max_workers: 10
PREFETCH_COUNT: 20
parent_calls_tracked: 20 parent_calls_tracked: 20
LOGGING: LOGGING:

View File

@@ -2,8 +2,7 @@ AMQP_URI: pyamqp://${RABBIT_USER:admin}:${RABBIT_PASSWORD:admin}@${RABBIT_HOST:l
WEB_SERVER_ADDRESS: '0.0.0.0:8000' WEB_SERVER_ADDRESS: '0.0.0.0:8000'
RPC_EXCHANGE: 'nameko-rpc' RPC_EXCHANGE: 'nameko-rpc'
max_workers: 20 max_workers: 10
PREFETCH_COUNT: 20
parent_calls_tracked: 20 parent_calls_tracked: 20
LOGGING: LOGGING:

View File

@@ -1,8 +1,7 @@
AMQP_URI: pyamqp://${RABBIT_USER:admin}:${RABBIT_PASSWORD:admin}@${RABBIT_HOST:localhost}:${RABBIT_PORT:5672}/ AMQP_URI: pyamqp://${RABBIT_USER:admin}:${RABBIT_PASSWORD:admin}@${RABBIT_HOST:localhost}:${RABBIT_PORT:5672}/
RPC_EXCHANGE: 'nameko-rpc' RPC_EXCHANGE: 'nameko-rpc'
max_workers: 20 max_workers: 10
PREFETCH_COUNT: 20
parent_calls_tracked: 20 parent_calls_tracked: 20
LOGGING: LOGGING:

View File

@@ -1,8 +1,7 @@
AMQP_URI: pyamqp://${RABBIT_USER:admin}:${RABBIT_PASSWORD:admin}@${RABBIT_HOST:localhost}:${RABBIT_PORT:5672}/ AMQP_URI: pyamqp://${RABBIT_USER:admin}:${RABBIT_PASSWORD:admin}@${RABBIT_HOST:localhost}:${RABBIT_PORT:5672}/
RPC_EXCHANGE: 'nameko-rpc' RPC_EXCHANGE: 'nameko-rpc'
max_workers: 20 max_workers: 10
PREFETCH_COUNT: 20
parent_calls_tracked: 20 parent_calls_tracked: 20
LOGGING: LOGGING:

View File

@@ -1,68 +0,0 @@
[project]
name = "namekoplus"
version = "0.5.3"
description = "A lightweight Python distributed microservice solution"
keywords = ["lightweight python distributed microservice solution"]
readme = "README.md"
authors = [
{name = "Bryant He", email = "bryantsisu@qq.com"},
]
classifiers = [
"Development Status :: 3 - Alpha",
"Environment :: Web Environment",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Topic :: Software Development :: Libraries :: Python Modules",
]
requires-python = ">=3.8, <4"
dependencies = [
"click==8.1.5",
"mako==1.2.4",
"nameko==3.0.0rc11",
"pytest==7.4.0",
"python-on-whales==0.65.0",
"shortuuid==1.0.11",
]
license = {text = "MIT"}
[project.urls]
"Homepage" = "https://github.com/Bryanthelol/namekoplus"
"Source Code" = "https://github.com/Bryanthelol/namekoplus"
"Bug Tracker" = "https://github.com/Bryanthelol/namekoplus/issues"
[project.optional-dependencies]
ha = [
"cachetools==5.3.0",
"circuitbreaker==2.0.0",
"tenacity==8.2.2",
]
ob = [
"nameko-sentry==1.0.0",
"nameko-tracer==1.4.0",
]
log = [
"logstash_formatter==0.5.17",
"loguru==0.7.2",
]
metric = [
"statsd==4.0.1",
]
schema = [
"marshmallow==3.20.1",
]
security = [
"cryptography",
]
dev = [
"environs==9.5.0",
"python-dotenv==1.0.0",
]
[project.scripts]
namekoplus = "namekoplus.command:cli"
[build-system]
requires = ["pdm-backend"]
build-backend = "pdm.backend"

78
setup.py Normal file
View File

@@ -0,0 +1,78 @@
from setuptools import setup, find_packages
from codecs import open
from os import path
here = path.abspath(path.dirname(__file__))
with open(path.join(here, 'README.md'), encoding='utf-8') as f:
long_description = f.read()
setup(
name='namekoplus',
version='0.4.0',
description='A lightweight Python distributed microservice solution',
long_description=long_description,
long_description_content_type='text/markdown',
url='',
project_urls={
'Documentation': 'https://doc.bearcatlog.com/',
'Source Code': 'https://github.com/Bryanthelol/namekoplus',
'Bug Tracker': 'https://github.com/Bryanthelol/namekoplus/issues',
},
author='Bryant He',
author_email='bryantsisu@qq.com',
license='MIT',
classifiers=[
'Development Status :: 3 - Alpha',
'Environment :: Web Environment',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python :: 3',
'Topic :: Software Development :: Libraries :: Python Modules',
],
platforms='any',
python_requires='>=3.8, <4',
keywords='lightweight python distributed microservice solution',
packages=find_packages(exclude=['contrib', 'docs', 'tests']),
include_package_data=True,
data_files=['README.md'],
entry_points={
'console_scripts': [
'namekoplus = namekoplus.command:cli',
],
},
install_requires=[
'nameko==3.0.0rc11',
'click==8.1.5',
'python-on-whales==0.63.0',
'pytest==7.4.0',
'mako==1.2.4',
'shortuuid==1.0.11'
],
extras_require={
'ha': ['tenacity==8.2.2',
'cachetools==5.3.0',
'circuitbreaker==2.0.0',
'statsd==4.0.1',
'logstash_formatter==0.5.17',
'nameko-sentry==1.0.0',
'nameko-tracer==1.4.0'],
'apiflask': ['apiflask>=1.3.1',
'gevent>=22.10.2',
'gunicorn==20.1.0'],
'rocketry': ['rocketry==2.4.0'],
'gutter': ['gutter==0.5.0'],
'mysql': ['pymysql==1.0.3',
'sqlalchemy==2.0.15',
'sqlacodegen==2.3.0',
'alembic==1.11.1'],
'security': ['cryptography'],
'dev': ['environs==9.5.0']
},
)

View File

@@ -1 +0,0 @@
from .chassis.chassis import *

View File