添加 demo 代码
This commit is contained in:
parent
fc7623ff33
commit
0d940ca58f
27
README.md
27
README.md
|
@ -2,4 +2,29 @@
|
|||
|
||||
chassis <ˈʃæsi> n. 底盘,底架
|
||||
|
||||
寓意服务基座
|
||||
仓库名寓意服务基座,基础库。
|
||||
|
||||
|
||||
微服务底座的目的是简化服务的创建过程,同时确保开发者拥有一套所有服务都要遵循的标准。
|
||||
|
||||
包含的特性
|
||||
1. 错误报告
|
||||
2. 日志收集
|
||||
3. Metrics 数据收集
|
||||
4. 配置获取
|
||||
5. 数据存储设置(ORM)
|
||||
6. 健康检查
|
||||
7. 服务注册和发现
|
||||
8. 模板代码
|
||||
|
||||
|
||||
实现的功能
|
||||
- 服务发现
|
||||
- 可观测
|
||||
- 传输
|
||||
- 负载均衡
|
||||
- 限流降级(消息确认+熔断)
|
||||
|
||||
|
||||
安装方法:
|
||||
`python3 -m pip install -U https://gitea.bearcatlog.com/BryantStudio/chassis.git`
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
import logging
|
||||
|
||||
from jaeger_client import Config
|
||||
from logstash_formatter import LogstashFormatterV1
|
||||
from statsd import StatsClient
|
||||
|
||||
|
||||
def init_statsd(prefix=None, host=None, port=8125):
|
||||
statsd = StatsClient(host, port, prefix=prefix)
|
||||
return statsd
|
||||
|
||||
|
||||
def init_logger():
|
||||
logger = logging.getLogger()
|
||||
handler = logging.StreamHandler()
|
||||
formatter = LogstashFormatterV1()
|
||||
handler.setFormatter(formatter)
|
||||
logger.addHandler(handler)
|
||||
return logger
|
||||
|
||||
|
||||
def init_tracer(service):
|
||||
config = Config(
|
||||
config={
|
||||
'sampler': {
|
||||
'type': 'const',
|
||||
'param': 1,
|
||||
},
|
||||
'local_agent': {
|
||||
'reporting_host': "jaeger",
|
||||
'reporting_port': 5775,
|
||||
},
|
||||
'logging': True,
|
||||
'reporter_batch_size': 1,
|
||||
},
|
||||
|
||||
service_name=service,
|
||||
)
|
||||
|
||||
# this call also sets opentracing.tracer
|
||||
return config.initialize_tracer()
|
|
@ -0,0 +1,7 @@
|
|||
.venv
|
||||
.git
|
||||
.gitignore
|
||||
.dockerignore
|
||||
/log/*
|
||||
/tmp/*
|
||||
/test/reports/*
|
|
@ -0,0 +1,261 @@
|
|||
{
|
||||
"__inputs": [
|
||||
{
|
||||
"name": "Prometheus",
|
||||
"label": "Prometheus",
|
||||
"description": "",
|
||||
"type": "datasource",
|
||||
"pluginId": "prometheus",
|
||||
"pluginName": "Prometheus"
|
||||
}
|
||||
],
|
||||
"__requires": [
|
||||
{
|
||||
"type": "grafana",
|
||||
"id": "grafana",
|
||||
"name": "Grafana",
|
||||
"version": "4.6.2"
|
||||
},
|
||||
{
|
||||
"type": "panel",
|
||||
"id": "graph",
|
||||
"name": "Graph",
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "datasource",
|
||||
"id": "prometheus",
|
||||
"name": "Prometheus",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
],
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"gnetId": null,
|
||||
"graphTooltip": 0,
|
||||
"hideControls": false,
|
||||
"id": null,
|
||||
"links": [],
|
||||
"refresh": false,
|
||||
"rows": [
|
||||
{
|
||||
"collapse": false,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${Prometheus}",
|
||||
"fill": 1,
|
||||
"id": 5,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": true,
|
||||
"current": true,
|
||||
"hideEmpty": false,
|
||||
"hideZero": false,
|
||||
"max": true,
|
||||
"min": true,
|
||||
"rightSide": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": true
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "null",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 6,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "admin_service_hello",
|
||||
"format": "time_series",
|
||||
"hide": false,
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "admin_service_hello {{quantile}}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Admin Service | Hello",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"transparent": false,
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${Prometheus}",
|
||||
"fill": 1,
|
||||
"id": 10,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": true,
|
||||
"current": true,
|
||||
"max": true,
|
||||
"min": true,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": true
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "null",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 6,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "admin_service_error",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "admin_service_error {{quantile}}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Admin Service | Error",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"repeat": null,
|
||||
"repeatIteration": null,
|
||||
"repeatRowId": null,
|
||||
"showTitle": false,
|
||||
"title": "Dashboard Row",
|
||||
"titleSize": "h6"
|
||||
}
|
||||
],
|
||||
"schemaVersion": 14,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-1h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"refresh_intervals": [
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
],
|
||||
"time_options": [
|
||||
"5m",
|
||||
"15m",
|
||||
"1h",
|
||||
"6h",
|
||||
"12h",
|
||||
"24h",
|
||||
"2d",
|
||||
"7d",
|
||||
"30d"
|
||||
]
|
||||
},
|
||||
"timezone": "",
|
||||
"title": "Admin Service",
|
||||
"version": 5
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
version: "3"
|
||||
|
||||
services:
|
||||
statsd-agent:
|
||||
image: statsd/statsd:latest
|
||||
restart: always
|
||||
ports:
|
||||
- "8125:8125/udp"
|
||||
- "8126:8126"
|
||||
volumes:
|
||||
- "./statsd_config.js:/usr/src/app/config.js"
|
||||
networks:
|
||||
chassis_agent:
|
||||
statsd-exporter:
|
||||
image: prom/statsd-exporter:latest
|
||||
hostname: "statsd-exporter"
|
||||
command: "--statsd.mapping-config=/tmp/statsd_mapping.yml"
|
||||
ports:
|
||||
- "9102:9102"
|
||||
- "9125:9125/udp"
|
||||
volumes:
|
||||
- "./statsd_mapping.yml:/tmp/statsd_mapping.yml"
|
||||
networks:
|
||||
chassis_agent:
|
||||
|
||||
networks:
|
||||
chassis_agent:
|
||||
driver: bridge
|
||||
|
||||
#volumes:
|
||||
# chassis_agent_data:
|
||||
# driver: local
|
|
@ -0,0 +1,8 @@
|
|||
(function () {
|
||||
return {
|
||||
"port": 8125,
|
||||
"backends": ["./backends/repeater"],
|
||||
"repeater": [{host: 'statsd-exporter', port: 9125}],
|
||||
};
|
||||
})();
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
mappings:
|
||||
- match: "admin-service.admin-service.hello"
|
||||
observer_type: summary
|
||||
name: "hello"
|
||||
labels:
|
||||
provider: "$2"
|
||||
outcome: "$3"
|
||||
job: "admin-service"
|
||||
summary_options:
|
||||
quantiles:
|
||||
- quantile: 0.99
|
||||
error: 0.001
|
||||
- quantile: 0.95
|
||||
error: 0.01
|
||||
- quantile: 0.9
|
||||
error: 0.05
|
||||
- quantile: 0.5
|
||||
error: 0.005
|
||||
max_summary_age: 30s
|
||||
summary_age_buckets: 3
|
||||
stream_buffer_size: 1000
|
||||
- match: "admin-service.admin-service.error"
|
||||
observer_type: summary
|
||||
name: "error"
|
||||
labels:
|
||||
provider: "$2"
|
||||
outcome: "$3"
|
||||
job: "admin-service"
|
||||
summary_options:
|
||||
quantiles:
|
||||
- quantile: 0.99
|
||||
error: 0.001
|
||||
- quantile: 0.95
|
||||
error: 0.01
|
||||
- quantile: 0.9
|
||||
error: 0.05
|
||||
- quantile: 0.5
|
||||
error: 0.005
|
||||
max_summary_age: 30s
|
||||
summary_age_buckets: 3
|
||||
stream_buffer_size: 1000
|
|
@ -0,0 +1,24 @@
|
|||
AMQP_URI: pyamqp://${RABBIT_USER:guest}:${RABBIT_PASSWORD:guest}@${RABBIT_HOST:localhost}:${RABBIT_PORT:5672}/
|
||||
RPC_EXCHANGE: 'nameko-rpc'
|
||||
|
||||
max_workers: 10
|
||||
parent_calls_tracked: 20
|
||||
|
||||
LOGGING:
|
||||
version: 1
|
||||
formatters:
|
||||
tracer:
|
||||
(): nameko_tracer.formatters.PrettyJSONFormatter
|
||||
handlers:
|
||||
tracer:
|
||||
class: logging.StreamHandler
|
||||
formatter: tracer
|
||||
loggers:
|
||||
nameko_tracer:
|
||||
level: INFO
|
||||
handlers: [tracer]
|
||||
|
||||
SENTRY:
|
||||
DSN: "xxxx"
|
||||
CLIENT_CONFIG:
|
||||
site: "my site name"
|
|
@ -0,0 +1,44 @@
|
|||
import json
|
||||
import datetime
|
||||
import requests
|
||||
from nameko.web.handlers import http
|
||||
from nameko.timer import timer
|
||||
from statsd import StatsClient
|
||||
from circuitbreaker import circuit
|
||||
|
||||
|
||||
class DemoChassisService:
|
||||
name = "demo_chassis_service"
|
||||
|
||||
statsd = StatsClient('localhost', 8125, prefix='simplebank-nameko_demo')
|
||||
|
||||
@http('GET', '/health')
|
||||
@statsd.timer('health')
|
||||
def health(self, _request):
|
||||
return json.dumps({'ok': datetime.datetime.utcnow().__str__()})
|
||||
|
||||
@http('GET', '/external')
|
||||
@circuit(failure_threshold=5, expected_exception=ConnectionError)
|
||||
@statsd.timer('external')
|
||||
def external_request(self, _request):
|
||||
response = requests.get('https://jsonplaceholder.typicode.com/posts/1')
|
||||
return json.dumps({'code': response.status_code, 'body': response.text})
|
||||
|
||||
@http('GET', '/error')
|
||||
@circuit(failure_threshold=5, expected_exception=ZeroDivisionError)
|
||||
@statsd.timer('http_error')
|
||||
def error_http_request(self):
|
||||
return json.dumps({1 / 0})
|
||||
|
||||
|
||||
class HealthCheckService:
|
||||
name = "health_check_service"
|
||||
|
||||
statsd = StatsClient('localhost', 8125, prefix='simplebank-nameko_demo')
|
||||
|
||||
@timer(interval=10)
|
||||
@statsd.timer('check_demo_service')
|
||||
def check_demo_service(self):
|
||||
response = requests.get('http://0.0.0.0:8000/health')
|
||||
print("DemoChassisService HEALTH CHECK: status_code {}, response: {}".format(
|
||||
response.status_code, response.text))
|
|
@ -0,0 +1,41 @@
|
|||
from nameko.events import BROADCAST, EventDispatcher, event_handler
|
||||
from nameko.rpc import rpc
|
||||
from nameko.timer import timer
|
||||
|
||||
|
||||
class EventPublisherService:
|
||||
name = "publisher_service"
|
||||
|
||||
dispatch = EventDispatcher()
|
||||
|
||||
@rpc
|
||||
def publish(self, event_type, payload):
|
||||
self.dispatch(event_type, payload)
|
||||
|
||||
|
||||
class AnEventListenerService:
|
||||
name = "an_event_listener_service"
|
||||
|
||||
@event_handler("publisher_service", "an_event")
|
||||
def consume_an_event(self, payload):
|
||||
print("service {} received:".format(self.name), payload)
|
||||
|
||||
|
||||
class AnotherEventListenerService:
|
||||
name = "another_event_listener_service"
|
||||
|
||||
@event_handler("publisher_service", "another_event")
|
||||
def consume_another_event(self, payload):
|
||||
print("service {} received:".format(self.name), payload)
|
||||
|
||||
|
||||
class ListenBothEventsService:
|
||||
name = "listen_both_events_service"
|
||||
|
||||
@event_handler("publisher_service", "an_event")
|
||||
def consume_an_event(self, payload):
|
||||
print("service {} received:".format(self.name), payload)
|
||||
|
||||
@event_handler("publisher_service", "another_event")
|
||||
def consume_another_event(self, payload):
|
||||
print("service {} received:".format(self.name), payload)
|
|
@ -0,0 +1,25 @@
|
|||
import json
|
||||
from nameko.web.handlers import http
|
||||
from werkzeug.wrappers import Response
|
||||
from nameko_sentry import SentryReporter
|
||||
|
||||
|
||||
class HttpDemoService:
|
||||
name = "http_demo_service"
|
||||
sentry = SentryReporter()
|
||||
|
||||
@http("GET", "/broken")
|
||||
def broken(self, request):
|
||||
raise ConnectionRefusedError()
|
||||
|
||||
@http('GET', '/books/<string:uuid>')
|
||||
def demo_get(self, request, uuid):
|
||||
data = {'id': uuid, 'title': 'The unbearable lightness of being',
|
||||
'author': 'Milan Kundera'}
|
||||
return Response(json.dumps({'book': data}),
|
||||
mimetype='application/json')
|
||||
|
||||
@http('POST', '/books')
|
||||
def demo_post(self, request):
|
||||
return Response(json.dumps({'book': request.data.decode()}),
|
||||
mimetype='application/json')
|
|
@ -0,0 +1,9 @@
|
|||
nameko==2.14.1
|
||||
nameko-tracer==1.4.0
|
||||
logstash_formatter==0.5.17
|
||||
circuitbreaker==2.0.0
|
||||
statsd==4.0.1
|
||||
gutter==0.5.0
|
||||
request-id==1.0.1
|
||||
nameko-sentry==1.0.0
|
||||
pyopenssl==23.1.1
|
|
@ -0,0 +1,20 @@
|
|||
from nameko.rpc import rpc, RpcProxy
|
||||
|
||||
|
||||
class RpcResponderDemoService:
|
||||
name = "rpc_responder_demo_service"
|
||||
|
||||
@rpc
|
||||
def hello(self, name):
|
||||
return "Hello, {}!".format(name)
|
||||
|
||||
|
||||
class RpcCallerDemoService:
|
||||
name = "rpc_caller_demo_service"
|
||||
|
||||
remote = RpcProxy("rpc_responder_demo_service")
|
||||
|
||||
@rpc
|
||||
def remote_hello(self, value="John Doe"):
|
||||
res = u"{}".format(value)
|
||||
return self.remote.hello(res)
|
|
@ -0,0 +1,54 @@
|
|||
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='chassis',
|
||||
version='0.0.1',
|
||||
description='basic sdk',
|
||||
long_description=long_description,
|
||||
url='https://gitea.bearcatlog.com/BryantStudio/chassis',
|
||||
|
||||
author='BryantStudio Engineering',
|
||||
author_email='bryantsisu@qq.book',
|
||||
|
||||
license='MIT',
|
||||
|
||||
classifiers=[
|
||||
'Development Status :: 3 - Alpha',
|
||||
'Environment :: Web Environment',
|
||||
'Intended Audience :: Developers',
|
||||
'License :: OSI Approved :: Apache Software License',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
||||
|
||||
"Programming Language :: Python",
|
||||
"Operating System :: MacOS :: MacOS X",
|
||||
"Operating System :: POSIX",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Topic :: Internet",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
"Intended Audience :: Developers",
|
||||
],
|
||||
|
||||
keywords='microservices basic sdk',
|
||||
|
||||
packages=find_packages(exclude=['contrib', 'docs', 'tests']),
|
||||
|
||||
install_requires=[
|
||||
'nameko==2.14.1',
|
||||
'nameko-tracer==1.3.0',
|
||||
'logstash_formatter==0.5.17',
|
||||
'circuitbreaker==2.0.0',
|
||||
'statsd==4.0.1',
|
||||
'gutter==0.5.0',
|
||||
'request-id==1.0.1',
|
||||
'nameko-sentry==1.0.0',
|
||||
'pyopenssl==23.1.1',
|
||||
],
|
||||
)
|
Loading…
Reference in New Issue