Лекция 9
Ахтаров Данил
Tinkoff Python
Python
Микросервисы
Про что говорим
- Как работает интерпретатор
- Логирование
- Микросервисы, сервисы, макросервисы
- Архитектура web приложений
- Мониторинг
- CI/CD
- ...
Зачем?
Проще диагностировать проблемы
Меньше магии
Всегда знаем куда пойти посмотреть
Что такое интерпретатор?
Достоинство?
Исходный текст
(test.py)
Байт код
(test.pyc)
Выполнение
(PVM)
Зачем?
Компактное представление
Ограниченный набор команд
Можно сразу интерпретировать без дополнительных шагов
Tokenizer
def say_hello():
print("Hello, World!")
say_hello()
$ python -m tokenize hello.py
0,0-0,0: ENCODING 'utf-8'
1,0-1,3: NAME 'def'
1,4-1,13: NAME 'say_hello'
1,13-1,14: OP '('
1,14-1,15: OP ')'
1,15-1,16: OP ':'
1,16-1,17: NEWLINE '\n'
2,0-2,4: INDENT ' '
2,4-2,9: NAME 'print'
2,9-2,10: OP '('
2,10-2,25: STRING '"Hello, World!"'
2,25-2,26: OP ')'
2,26-2,27: NEWLINE '\n'
3,0-3,1: NL '\n'
4,0-4,0: DEDENT ''
4,0-4,9: NAME 'say_hello'
4,9-4,10: OP '('
4,10-4,11: OP ')'
4,11-4,12: NEWLINE '\n'
5,0-5,0: ENDMARKER ''
Можно получить ошибки несоответствия грамматике
Грамматика языка
...
comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not'
star_expr: '*' expr
expr: xor_expr ('|' xor_expr)*
xor_expr: and_expr ('^' and_expr)*
and_expr: shift_expr ('&' shift_expr)*
shift_expr: arith_expr (('<<'|'>>') arith_expr)*
arith_expr: term (('+'|'-') term)*
...
Abstract Syntax Tree
![](https://upload.wikimedia.org/wikipedia/commons/thumb/c/c7/Abstract_syntax_tree_for_Euclidean_algorithm.svg/1200px-Abstract_syntax_tree_for_Euclidean_algorithm.svg.png)
Можно получить синтаксические ошибки
AST
import ast
tree = ast.parse('print("Hello world")')
tree
# <_ast.Module at 0x10a3ba0b8>
AST
import ast
node = ast.UnaryOp()
node.op = ast.USub()
node.operand = ast.Constant()
node.operand.value = 5
node.operand.lineno = 0
node.operand.col_offset = 0
node.lineno = 0
node.col_offset = 0
Дизассемблирование байткода
import dis
def foo(y):
x = 1
return x + y
dis.dis(foo)
4 0 LOAD_CONST 1 (1)
2 STORE_FAST 1 (x)
5 4 LOAD_FAST 1 (x)
6 LOAD_FAST 0 (y)
8 BINARY_ADD
10 RETURN_VALUE
Дизассемблирование байткода
def f(num):
if num == 42:
return True
return False
(1)|(2)|(3)|(4)| (5) |(6)| (7)
---|---|---|---|----------------------|---|-------
2| | | 0|LOAD_FAST | 0|(num)
|-->| | 2|LOAD_CONST | 1|(42)
| | | 4|COMPARE_OP | 2|(==)
| | | 6|POP_JUMP_IF_FALSE | 12|
| | | | | |
3| | | 8|LOAD_CONST | 2|(True)
| | | 10|RETURN_VALUE | |
| | | | | |
4| |>> | 12|LOAD_CONST | 3|(False)
| | | 14|RETURN_VALUE | |
// ceval.c
for (;;) {
switch (opcode) {
case TARGET(NOP): {
FAST_DISPATCH();
}
case TARGET(LOAD_FAST): {
...
}
case TARGET(LOAD_CONST): {
PREDICTED(LOAD_CONST);
PyObject *value = GETITEM(consts, oparg);
Py_INCREF(value);
PUSH(value);
FAST_DISPATCH();
}
case TARGET(STORE_FAST): {
PREDICTED(STORE_FAST);
PyObject *value = POP();
SETLOCAL(oparg, value);
FAST_DISPATCH();
}
case TARGET(POP_TOP): {
...
}
case TARGET(ROT_TWO): {
...
}
case TARGET(ROT_THREE): {
...
}
case TARGET(ROT_FOUR): {
...
}
case TARGET(DUP_TOP): {
...
}
case TARGET(DUP_TOP_TWO): {
...
}
case TARGET(UNARY_POSITIVE): {
...
}
case TARGET(UNARY_NEGATIVE): {
...
}
case TARGET(UNARY_NOT): {
...
}
case TARGET(UNARY_INVERT): {
...
}
case TARGET(BINARY_POWER): {
...
}
case TARGET(BINARY_MULTIPLY): {
...
}
case TARGET(BINARY_MATRIX_MULTIPLY): {
...
}
case TARGET(BINARY_TRUE_DIVIDE): {
...
}
case TARGET(BINARY_FLOOR_DIVIDE): {
PyObject *divisor = POP();
PyObject *dividend = TOP();
PyObject *quotient = PyNumber_FloorDivide(dividend, divisor);
Py_DECREF(dividend);
Py_DECREF(divisor);
SET_TOP(quotient);
if (quotient == NULL)
goto error;
DISPATCH();
}
case TARGET(BINARY_MODULO): {
...
}
...
}
}
CPython
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1266151/images/7231565/githubpython.png)
Что есть что в репо?
- Grammar - граматика питона
- Include - .h файлы для C кода (здесь же объявляются почти все основные структуры)
- Lib - стандартная библиотека на питоне
- Modules - стандартная библиотека на C
- Objects - объектная система питона, встроенные структуры данных
- Parser - парсинг исходного кода (до ast)
- Python - интерпретатор, компилятор байт кода
/* Minimal main program -- everything is loaded from the library */
#include "Python.h"
#include "pycore_pylifecycle.h"
#ifdef MS_WINDOWS
int
wmain(int argc, wchar_t **argv)
{
return Py_Main(argc, argv);
}
#else
int
main(int argc, char **argv)
{
return Py_BytesMain(argc, argv);
}
#endif
Parser/
- token.c
- tokenizer.c
- parser.c
Python/
- ast.c
- ceval.c
// ceval.c
for (;;) {
switch (opcode) {
case TARGET(NOP): {
FAST_DISPATCH();
}
case TARGET(LOAD_FAST): {
...
}
case TARGET(LOAD_CONST): {
PREDICTED(LOAD_CONST);
PyObject *value = GETITEM(consts, oparg);
Py_INCREF(value);
PUSH(value);
FAST_DISPATCH();
}
case TARGET(STORE_FAST): {
PREDICTED(STORE_FAST);
PyObject *value = POP();
SETLOCAL(oparg, value);
FAST_DISPATCH();
}
case TARGET(POP_TOP): {
...
}
case TARGET(ROT_TWO): {
...
}
case TARGET(ROT_THREE): {
...
}
case TARGET(ROT_FOUR): {
...
}
case TARGET(DUP_TOP): {
...
}
case TARGET(DUP_TOP_TWO): {
...
}
case TARGET(UNARY_POSITIVE): {
...
}
case TARGET(UNARY_NEGATIVE): {
...
}
case TARGET(UNARY_NOT): {
...
}
case TARGET(UNARY_INVERT): {
...
}
case TARGET(BINARY_POWER): {
...
}
case TARGET(BINARY_MULTIPLY): {
...
}
case TARGET(BINARY_MATRIX_MULTIPLY): {
...
}
case TARGET(BINARY_TRUE_DIVIDE): {
...
}
case TARGET(BINARY_FLOOR_DIVIDE): {
PyObject *divisor = POP();
PyObject *dividend = TOP();
PyObject *quotient = PyNumber_FloorDivide(dividend, divisor);
Py_DECREF(dividend);
Py_DECREF(divisor);
SET_TOP(quotient);
if (quotient == NULL)
goto error;
DISPATCH();
}
case TARGET(BINARY_MODULO): {
...
}
...
}
}
Фреймы
code, args, kwargs, const, etc.
code, args, kwargs, const, etc.
code, args, kwargs, const, etc.
current frame
Модуль
main
sort
sort_file
Получение текущего фрэйма
import inspect
current_frame = inspect.currentframe()
current_frame
# <frame at 0x1068357a8, file '<...>', line 1, code <module>>
current_frame.f_back
# <frame at 0x7fa91b874df8, file './.py', line 2981, code run_code>
>>> import requests
>>> requests.get(42)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3/dist-packages/requests/api.py", line 55, in get
return request('get', url, **kwargs)
File "/usr/lib/python3/dist-packages/requests/api.py", line 44, in request
return session.request(method=method, url=url, **kwargs)
File "/usr/lib/python3/dist-packages/requests/sessions.py", line 421, in request
prep = self.prepare_request(req)
File "/usr/lib/python3/dist-packages/requests/sessions.py", line 359, in prepare_request
hooks=merge_hooks(request.hooks, self.hooks),
File "/usr/lib/python3/dist-packages/requests/models.py", line 287, in prepare
self.prepare_url(url, params)
File "/usr/lib/python3/dist-packages/requests/models.py", line 338, in prepare_url
"Perhaps you meant http://{0}?".format(url))
requests.exceptions.MissingSchema: Invalid URL '42': No schema supplied. Perhaps you meant http://42?
// ceval.c
for (;;) {
switch (opcode) {
case TARGET(NOP): {
FAST_DISPATCH();
}
case TARGET(LOAD_FAST): {
...
}
case TARGET(LOAD_CONST): {
PREDICTED(LOAD_CONST);
PyObject *value = GETITEM(consts, oparg);
Py_INCREF(value);
PUSH(value);
FAST_DISPATCH();
}
case TARGET(STORE_FAST): {
PREDICTED(STORE_FAST);
PyObject *value = POP();
SETLOCAL(oparg, value);
FAST_DISPATCH();
}
case TARGET(POP_TOP): {
...
}
case TARGET(ROT_TWO): {
...
}
case TARGET(ROT_THREE): {
...
}
case TARGET(ROT_FOUR): {
...
}
case TARGET(DUP_TOP): {
...
}
case TARGET(DUP_TOP_TWO): {
...
}
case TARGET(UNARY_POSITIVE): {
...
}
case TARGET(UNARY_NEGATIVE): {
...
}
case TARGET(UNARY_NOT): {
...
}
case TARGET(UNARY_INVERT): {
...
}
case TARGET(BINARY_POWER): {
...
}
case TARGET(BINARY_MULTIPLY): {
...
}
case TARGET(BINARY_MATRIX_MULTIPLY): {
...
}
case TARGET(BINARY_TRUE_DIVIDE): {
...
}
case TARGET(BINARY_FLOOR_DIVIDE): {
PyObject *divisor = POP();
PyObject *dividend = TOP();
PyObject *quotient = PyNumber_FloorDivide(dividend, divisor);
Py_DECREF(dividend);
Py_DECREF(divisor);
SET_TOP(quotient);
if (quotient == NULL)
goto error;
DISPATCH();
}
case TARGET(BINARY_MODULO): {
...
}
...
}
}
Выводы
- Python - компилируемый и интерпретируемый язык
- Интерпретатор - стековый
- Байт код кэшируется
- Фреймы создаются всегда при вызове функции
- Оптимизаций мало
Logging
Зачем?
А что с print-ом не так?
Использование
import logging
logger = logging.getLogger(__name__)
def foo():
logger.info('Some log text')
Конфигурирование
import logging
logging.basicConfig(filename="file.log", level=logging.INFO)
dictLogConfig = {
"version":1,
"handlers":{
"fileHandler":{
"class":"logging.FileHandler",
"formatter":"myFormatter",
"filename":"config2.log"
}
},
"loggers":{
"exampleApp":{
"handlers":["fileHandler"],
"level":"INFO",
}
},
"formatters":{
"myFormatter":{
"format":"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
}
}
}
logging.config.dictConfig(dictLogConfig)
[loggers]
keys=root,exampleApp
[handlers]
keys=fileHandler, consoleHandler
[formatters]
keys=myFormatter
[logger_root]
level=CRITICAL
handlers=consoleHandler
[logger_exampleApp]
level=INFO
handlers=fileHandler
qualname=exampleApp
[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=myFormatter
args=(sys.stdout,)
[handler_fileHandler]
class=FileHandler
formatter=myFormatter
args=("config.log",)
[formatter_myFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=
import logging
logging.config.fileConfig('logging.conf')
Уровни логирования
- DEBUG
- INFO
- WARNING
- ERROR
- CRITICAL
Exception
import logging
logging.basicConfig(filename="filname.log", level=logging.INFO)
logger = logging.getLogger(__name__)
try:
raise RuntimeError
except RuntimeError:
logger.exception("Error!")
Форматирование
FORMAT = '%(asctime)-15s %(clientip)s %(user)-8s %(message)s'
logging.basicConfig(format=FORMAT)
d = {'clientip': '192.168.0.1', 'user': 'fbloggs'}
logging.warning('Protocol problem: %s', 'connection reset', extra=d)
2006-02-08 22:20:02,165 192.168.0.1 fbloggs Protocol problem: connection reset
Handlers
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
# create the logging file handler
fh = logging.FileHandler("filename.log")
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)
# add handler to logger object
logger.addHandler(fh)
logger.info("Program started")
Всегда используйте logging
Микросервисы
Зачем?
![](https://hsto.org/files/717/e67/4f7/717e674f7a714fe4a0e00eee50a55e9a.jpg)
Сервисы
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1266151/images/7232668/service.png)
Микросервисы
Worker, Consumer, REST API Server, Connector, ...
Макросервисы
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1266151/images/7232668/service.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1266151/images/7232668/service.png)
Stateless
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1266151/images/7232710/host-services.png)
Host 1
Host 2
Host 3
External Storages
Message brokers
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1266151/images/7232716/host-services-queue.png)
Databases
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1266151/images/7232753/host-services-sql.png)
Network drive
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1266151/images/7232761/host-services-file.png)
Messenger
Монолит
Можно обмениваться сообщениями в комнатах (PtP)
Messenger
Messenger
Messenger
DB
Messenger
Authorization
Consumer 1
Consumer 2
Producer 1
Producer 2
Settings
Messages
Producer 3
Авторизация
Зачем?
Использую Basic Auth и не парюсь!
Token
- 12231244124
- 8178e09f-4c9b-46c4-b98c-dcd9d3c1e5ca
- RRFHFGW183H9o5yb5jPH0fLFDEjDF2R2
Пара токенов
Access token - 15 минут (много раз)
Refresh token - 1 неделя (один раз)
Требования
- Хочу подписывать токен ключом
- Хочу хранить информацию внутри токена
JWT
json web token
![](https://processa-aspen.readthedocs.io/en/latest/JWT.png)
![](https://cdn.auth0.com/content/jwt/jwt-diagram.png)
Структура
{
"alg": "HS256",
"typ": "JWT"
}
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
iss, sub, aud, exp, nbf, jti, iat
>>> import jwt
>>> encoded_jwt = jwt.encode({'some': 'payload'}, 'secret', algorithm='HS256')
>>> encoded_jwt
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg'
>>> jwt.decode(encoded_jwt, 'secret', algorithms=['HS256'])
{'some': 'payload'}
OAuth2
![](https://enum.ru/Content/Images/enum_biz_ru.png)
![](https://upload.wikimedia.org/wikipedia/commons/thumb/9/96/Button_Icon_White.svg/1200px-Button_Icon_White.svg.png)
![](https://cdn4.iconfinder.com/data/icons/bettericons/354/google-2-circle-full-512.png)
Архитектура
Серебряная пуля
Основные компоненты
API Gateway
![](https://www.express-gateway.io/assets/img/api-gw-role.png)
Auth Service
Storage
Your Service
![](https://miro.medium.com/max/3488/1*J7H4_WUCciiv_M8W_uflvA.png)
Вопросы?
Мониторинг
Зачем?
Logging
![](https://calazanblog-assets.s3.amazonaws.com/media/editor-uploads/graylog_message_stream.png)
Метрики
GET /metrics
python_threads_total 10
...
app_version_info 1.0
app_connections_total{role="admin"} 10
app_connections_total{role="user"} 110
...
Metric types
- Counter
- Gauge
- Summary
- Histogram
- Info
- Enum (Current state "stopped")
Визуализации
Alerts
![](https://grafana.com/static/img/docs/v4/drag_handles_gif.gif)
CI/CD
![](https://hackernoon.com/drafts/uu14j32lz.png)
Gitflow
- master
- develop
- feature/*
- bugfix/*
- release/*
- hotfix/*
- master
- develop
- feature/STORY-1
- bugfix/TASK-1
- release/v1.0
- hotfix/v1.0.1
- TASK-6
Gitflow
![](https://hsto.org/storage/4bf7e68c/49e29c35/3a01bd6b/782a1be3.png)
![](https://www.goetas.com/img/posts/blue-green.png)
Configs
Переменные окружения
Pydantic
from pydantic import BaseSettings
class Config(BaseSettings):
MAX_SIZE: int = 1024
KEY: str
class Config:
env_prefix = 'MY_APP_'
config = Config()
The Twelve-Factor App
Двенадцать факторов
- Кодовая база
- Зависимости
- Конфигурация
- Сторонние службы (Backing Services)
- Сборка, релиз, выполнение
- Процессы
- Привязка портов (Port binding)
- Параллелизм
- Утилизируемость (Disposability)
- Паритет разработки/работы приложения
- Журналирование (Logs)
- Задачи администрирования
Tinkoff Python 2020 - 9
By Afonasev Evgeniy
Tinkoff Python 2020 - 9
Как устроен Python интерпретатор. Микросервисы, архитектура построения web приложений.
- 590