python meetup

85

Upload: iqspace

Post on 23-Jan-2018

187 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: Python Meetup
Page 2: Python Meetup
Page 3: Python Meetup
Page 4: Python Meetup
Page 5: Python Meetup

Оптимизация работы веб сервера с

базой данных на примере Django

Одесса Python meetup

Page 6: Python Meetup

Кто я?

Евгений Гетманский

Разработка веб сайтов на протяжении более пяти лет

Работаю тимлидом в Steelkiwi inc.

Page 7: Python Meetup

Часть 1

Что такое оптимизация и зачем она нужна?

Page 8: Python Meetup

Требования к проектам

Page 9: Python Meetup

Требования к проектам

Функциональные требования

Page 10: Python Meetup

Требования к проектам

Функциональные требования

Соблюдение стандартов

Page 11: Python Meetup

Требования к проектам

Функциональные требования

Соблюдение стандартов

Безопасность

Page 12: Python Meetup

Требования к проектам

Функциональные требования

Соблюдение стандартов

Безопасность

Скорость работы

Page 13: Python Meetup
Page 14: Python Meetup

504 Gateway Timeout Error

Увеличение таймаута

Асинхронная обработка данных

Оптимизация Python кода

Оптимизация запросов к базе данных

Page 15: Python Meetup

Часть 2

Оптимизация работы с базой данных

Page 16: Python Meetup

Когда нужно

оптимизировать

Бывает ли преждевременная оптимизация

Page 17: Python Meetup

Когда нужно

оптимизировать

Бывает ли преждевременная оптимизация

Анализ требований и проектирование до старта проекта

Page 18: Python Meetup

Когда нужно

оптимизировать

Бывает ли преждевременная оптимизация

Анализ требований и проектирование до старта проекта

Во время написания кода

Page 19: Python Meetup

Когда нужно

оптимизировать

Бывает ли преждевременная оптимизация

Анализ требований и проектирование до старта проекта

Во время написания кода

Когда уже не работает

Page 20: Python Meetup

Приемы и примеры кода

Как в Django писать оптимизированный код

Page 21: Python Meetup

Немного кодаclass Category(models.Model):

name = models.CharField(max_length=50)

class Product(models.Model):

name = models.CharField(max_length=50)

category = models.ForeignKey(Category,

related_name='products')

brand = models.ForeignKey(Brand, related_name='products')

class CartItem(models.Model):

product = models.ForeignKey(Product)

class Brand(models.Model):

title = models.CharField(max_length=50)

Page 22: Python Meetup

Использование ID

связанного объекта

используйте

product.category_id

вместо

product.category.id

Page 23: Python Meetup

Использование ID связанного

объекта

for item in CartItem.objects.filter(

product__category_id__in={

p.category.id for p in products}):

# Do something

Page 24: Python Meetup

Использование ID связанного

объекта

for item in CartItem.objects.filter(

product__category_id__in={

p.category_id for p in products}):

# Do something

Page 25: Python Meetup
Page 26: Python Meetup
Page 27: Python Meetup

Бывает и такое

if parent.invoices.filter(status=Invoice.PAID).count():

return

parent.invoices.filter(status=Invoice.PAID).order_by(

'payed_period_end').last()

return None

Page 28: Python Meetup

Проверки

if queryset

len(queryset)

count

exists

Page 29: Python Meetup

Используем select_related

for product in Product.objects.select_related('category'):

data.append(

{'product': product.name,

'category': product.category.name})

Page 30: Python Meetup

Используем select_related

Что это и зачем нужно?

Что происходит когда выполняется?

Как это помогает оптимизировать работу сервера

Page 31: Python Meetup
Page 32: Python Meetup
Page 33: Python Meetup

Используем prefetch_related

for category in Category.objects.prefetch_related('products'):

data.append({'category': category,

'products': category.products.all()})

Page 34: Python Meetup

Используем prefetch_related

Что это и зачем нужно?

Что происходит когда выполняется?

Как это помогает оптимизировать работу сервера

А что можно добавить?

Page 35: Python Meetup
Page 36: Python Meetup
Page 37: Python Meetup

Используем prefetch_related

prefetch = Prefetch('products',

queryset=Product.objects.select_related('brand'))

for category in Category.objects.only('name').prefetch_related(prefetch):

data.append({'category': category.name,

'products': [(product.name, product.brand.name) for

product in category.products.all()]})

Page 38: Python Meetup

Неужели все так хорошо или

есть что то еще?

Join на таблице в 300000 записей

Иногда лучше проверить

Django debug toolbar и debug panel

Page 39: Python Meetup

Создание записей

Все те же грабли с циклами

А что же делать?

Page 40: Python Meetup

Используем bulk_create

parsed_data = parse_data(data)

batch_size = 100

items = []

for row in parsed_data:

items.append(Item(**row))

if len(items) >= batch_size:

Item.objects.bulk_create(items)

items = []

if items:

Item.objects.bulk_create(items)

Page 41: Python Meetup
Page 42: Python Meetup

Редактирование записей

Вы не поверите, но тут опять все хорошо пока нет циклов

bulk_update?

Нет, но есть кое что получше - F object

Page 43: Python Meetup

Вот это чаще всего не хорошо

for product in category.products.all():

product.priority += 1

product.save()

Page 44: Python Meetup

А это уже неплохо

category.products.update(field=F('priority')+1)

Page 45: Python Meetup

Читайте документацию

https://docs.djangoproject.com/en/1.10/topics/db/optimization/

Page 46: Python Meetup

Часть 3

Немного примеров из жизни

Page 47: Python Meetup

Пересчет рейтинга

Приложение по работе с данными о авиакомпаниях, самолетах и

поиске маршрутов

Page 48: Python Meetup
Page 49: Python Meetup

Решения

увеличить таймаут

увеличить таймаут

прикручивать асинхронную обработку?

что делать???

Page 50: Python Meetup
Page 51: Python Meetup

Загрузка данных из CSV

много записей и много проверок

Page 52: Python Meetup
Page 53: Python Meetup
Page 54: Python Meetup

А теперь можно задавать вопросы

Page 55: Python Meetup

Немного ссылок

https://www.facebook.com/evgeniy.getmansky

http://steelkiwi.com

https://github.com/steelkiwi/meetup.demo

Page 56: Python Meetup

Шаблон проекта.

Использование Vagrant, VirtualEnv и

Ansible provisioner. Зачем это

необходимо?

Page 57: Python Meetup

О себе

1. Степанов Александр

2. Python Team Lead in SteelKiwi Inc.

3. Участие в 15+ проектах

4. AngularJS, BackBoneJS

5. Ionic Framework (не вздумайте)

Page 58: Python Meetup

Что мы хотели бы получить?

1. Скорость инициализации

2. Удобен при командной разработке

3. Прост с точки зрения деплоя

4. Легко масштабируется

5. Приятная структура

6. Имеет изолированное окружение

Page 59: Python Meetup

Какие мы знаем шаблоны?

- Default django template

- Two Scoops of Django

- Cookiecutter Django

Page 60: Python Meetup

Default django template

$ django-admin.py startproject myproject

myproject/manage.pymyproject/

__init__.pysettings.pyurls.pywsgi.py

myapp/

$ python manage.py startapp myapp

Page 61: Python Meetup

Django two scoops

$ django-admin.py startproject myproject2 --template=https://github.com/twoscoops/django-twoscoops-project/archive/develop.zip

myproject_2/docs/requirements/myproject_2/

manage.pystatic/templates/myproject_2/

__init__.pysettings/__init__.pylocal.pybase.pyproduction.pytest.py

urls.pywsgi.py

Page 62: Python Meetup

Сookiecutter django

$ pip install "cookiecutter>=1.4.0"

$ cookiecutter https://github.com/pydanny/cookiecutter-django

myproject_3/manage.pyconfig/

__init__.pysettings/

__init__.pylocal.pycommon.pyproduction.pytest.py

urls.pywsgi.py

requirements/myproject_3/static/templates/myapp/

Page 63: Python Meetup

Сравнение структуры

myproject_3/manage.pyconfig/

__init__.pysettings/

__init__.pylocal.pycommon.pyproduction.pytest.py

urls.pywsgi.py

requirements/base.txtlocal.txtproduction.txttest.txt

myproject_3/static/templates/myapp/

myproject_2/docs/requirements/

base.txtlocal.txtproduction.txttest.txt

myproject_2/manage.py

static/templates/myproject_2/

__init__.pysettings/

__init__.pylocal.pybase.pyproduction.pyTest.py

urls.pywsgi.py

myproject/manage.py

myproject/__init__.pysettings.pyurls.pyWsgi.py

myapp/

Default Django Two Scoops of Django Cookiecutter Django

Page 64: Python Meetup
Page 65: Python Meetup

Что мы сделали?

1. Только необходимые настройки

2. Изменили скрипт инициализации проекта

3. Разные database backend

4. Единый файл настроек

5. Переменные окружения по умолчанию

Page 66: Python Meetup

Скрипт инициализации проекта

{

"project_name":

"project_name",

"repo_name": "project_name",

"python_version": ["2", "3"],

"use_postgresql": "y",

"use_postgis": "n",

"use_mysql": "n",

"use_celery": "n",

"use_allauth": "n",

"use_redis": "n",

"use_rabbitmq": "n",

"use_supervisor": "n",

"use_apache": "n",

"use_nginx": "n"

}

Page 67: Python Meetup

Единый файл settings.py

env = environ.Env(

DJANGO_DEBUG=(bool, False),

DJANGO_SECRET_KEY=(str, 'CHANGEME!!!8l=tkv+t@)ilij-w=hogt8qgs-zf(i3or-k7w_d(7_1=&l1##l'),

DJANGO_ADMINS=(list, []),

DJANGO_ALLOWED_HOSTS=(list, []),

DJANGO_STATIC_ROOT=(str, str(APPS_DIR('staticfiles'))),

DJANGO_MEDIA_ROOT=(str, str(APPS_DIR('media'))),

DJANGO_DATABASE_URL=(str, 'postgres:///myproject4'),

DJANGO_EMAIL_URL=(environ.Env.email_url_config, 'consolemail://'),

DJANGO_DEFAULT_FROM_EMAIL=(str, '[email protected]'),

DJANGO_EMAIL_BACKEND=(str, 'django.core.mail.backends.smtp.EmailBackend'),

DJANGO_SERVER_EMAIL=(str, '[email protected]'),

DJANGO_CELERY_BROKER_URL=(str, 'redis://localhost:6379/0'),

DJANGO_CELERY_BACKEND=(str, 'redis://localhost:6379/0'),

DJANGO_CELERY_ALWAYS_EAGER=(bool, False),

DJANGO_USE_DEBUG_TOOLBAR=(bool, False),

DJANGO_TEST_RUN=(bool, False),

)

Page 68: Python Meetup

DJANGO DATABASE URL

Engine URL

PostgreSQL postgres://USER:PASSWORD@HOST:PORT/NAME

PostGIS postgis://USER:PASSWORD@HOST:PORT/NAME

MySQL mysql://USER:PASSWORD@HOST:PORT/NAME

MySQL (GIS) mysqlgis://USER:PASSWORD@HOST:PORT/NAME

SQLite sqlite:///PATH

Page 69: Python Meetup

DJANGO EMAIL URL

SMTP: smtp://

SMTP+SSL: smtp+ssl://

SMTP+TLS: smtp+tls://

Console mail: consolemail://

File mail: filemail://

LocMem mail: memorymail://

Dummy mail: dummymail://

# Example

smtp+tls://user:SuperSecretPassword@smtp_server.com:587`

Page 70: Python Meetup

А как же локальная разработка и тесты?

if env.bool('DJANGO_USE_DEBUG_TOOLBAR'):

MIDDLEWARE_CLASSES += ('debug_toolbar.middleware.DebugToolbarMiddleware', )

INSTALLED_APPS += ('debug_toolbar', )

DEBUG_TOOLBAR_CONFIG = {

'DISABLE_PANELS': [

'debug_toolbar.panels.redirects.RedirectsPanel',

],

'SHOW_TEMPLATE_CONTEXT': True,

}

if env.bool('DJANGO_TEST_RUN'):

pass

Page 71: Python Meetup

Virtual environment

$ pip install virtualenv

$ cd my_project_folder

$ virtualenv venv

$ source venv/bin/activate

$ pip install requests

$ pip install -r requirements.txt

Версия Python$ virtualenv -p /usr/bin/python2.7 venv

Установка пакетов

Page 72: Python Meetup

Vagrant

Vagrant это cli для VirtalBox, VMWare, Amazon EC2, LXC

Преимущества

1. Установка утилит в изолированное окружение

2. Уникальное окружение в контексте проекта

3. Поддержка в любой операционной системеПочему не Docker

1. См. Пункт 3 преимуществ

2. Изолированное окружение, повторяющее реальную рабочую среду

Page 73: Python Meetup

Настройки Vagrant1. Используемый box

2. Маппинг портов и папок

3. Способ наполнения изолированного окружения

4. Используемые ресурсы#### Box

develop.vm.box = "develop"develop.vm.box_url = "http://vagrant.steel.kiwi/vagrant/develop/"

#### Networkdevelop.vm.network :forwarded_port, guest: 8000, host: 8000develop.vm.network :private_network, ip: '192.168.80.50'develop.vm.synced_folder ".", "/vagrant", type: "nfs"

#### ansible_provisiondevelop.vm.provision "ansible" do |ansible|ansible.verbose = "v"ansible.playbook = "ansible/vagrant.yml"end

#### system resourcesdevelop.vm.hostname = "develop"

develop.vm.provider "virtualbox" do |v|v.memory = 1024v.cpus = 1

Page 74: Python Meetup

Как наполнить окружение?

#!/bin/bash

echo "deb http://apt.postgresql.org/pub/repos/apt/ trusty-pgdg main" >>

/etc/apt/sources.list.d/pgdg.list

wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | \

apt-key add -

apt-get update

apt-get install -y postgresql-9.4 postgresql-contrib libgeos-dev

Page 75: Python Meetup
Page 76: Python Meetup

Ansible ubuntu/trusty64---

- name: Configure the PostgreSQL APT key

apt_key: url=https://www.postgresql.org/media/keys/ACCC4CF8.asc state=present

- name: Configure the PostgreSQL APT repositories

apt_repository: repo="deb http://apt.postgresql.org/pub/repos/apt/ trusty-pgdg main"

state=present

- name: Update PostgreSQL Cache

apt: update_cache=yes

- name: Install PostgreSQL

apt: name={{ item }} force=yes state=installed

with_items:

- postgresql-9.4

- postgresql-contrib-9.4

- python-psycopg2

tags: packages

Page 77: Python Meetup

Ansible steelkiwi.box---

- name: enable postgresql

service: >

name=postgresql

enabled=yes

runlevel=default

state=started

tags: postgresql

- name: Create database

postgresql_db: >

login_user={{ db_user }}

name={{ db_name }}

owner={{ db_user }}

encoding='UTF-8'

lc_collate='en_US.UTF-8'

lc_ctype='en_US.UTF-8'

state=present

become: yes

become_user: vagrant

tags: create_db

Page 78: Python Meetup

В чем же преимущество Ansible?

Всё работает через SSH;

Написан на Python;

YAML;

Хорошая, постоянно обновляемая документация;

Последовательное обновление узлов.

Page 79: Python Meetup

Variables---

project_name: myproject4

application_name: myproject4

# Database settings.

db_user: "vagrant"

db_name: "{{ application_name }}"

db_password: ""

# Application settings.

virtualenv_path: "/home/vagrant/venv"

project_path: "/vagrant"

requirements_file: "{{ project_path }}/requirements/local.txt"

django_settings_path: "{{ project_path }}/config"

django_settings: "config.settings"

#Python version - change if you needed

python_version: 3

Page 80: Python Meetup

Связь Variables c Vagrant playbook# vars/vagrant.ymlmysql: noapache: nonginx: nopostgresql: truerabbitmq: noredis: truesupervisor: noapp: true

# vagrant.yml

vars_files:- vars/vagrant.yml

roles:- { role: mysql, when: "mysql == true" }- { role: apache, when: "apache == true" }- { role: nginx, when: "nginx == true" }- { role: postgresql, when: "postgresql == true" }- { role: rabbitmq, when: "rabbitmq == true" }- { role: redis, when: "redis == true" }- { role: supervisor, when: "supervisor == true" }- { role: app, when: "app == true" }

Page 81: Python Meetup

Ansible & Django

---

- name: Install packages required by the Django app inside virtualenv

pip: >

virtualenv={{ virtualenv_path }}

requirements={{ requirements_file }}

- name: Run Django database migrations

django_manage: >

command=migrate

app_path="{{ project_path }}"

virtualenv="{{ virtualenv_path }}"

settings="{{ django_settings }}"

when: run_django_db_migrations is defined and run_django_db_migrations

become: yes

become_user: vagrant

tags: django.migrate

Page 82: Python Meetup

Старт нового проекта

$ cookiecutter https://github.com/steelkiwi/django-template.git

$ vagrant up

$ vagrant ssh

$ cd /vagrant

$ cp env.example ./config/.env

$ python manage.py runserver 0.0.0.0:8000

А если проект уже в разработке?

$ git clone https://github.com/steelkiwi/old_poject.git

$ vagrant up

$ vagrant ssh

$ cd /vagrant

$ cp env.example ./config/.env

$ python manage.py runserver 0.0.0.0:8000

Page 83: Python Meetup
Page 84: Python Meetup

Выводы

1. Получили легко настраиваемый и гибкий шаблон проекта

2. Добавили возможность использовать разные database backend and

python version

3. Каждый проект находится в изолированном окружение

4. Одинаковая среда разработки

5. Добавили удобный способ наполнения и разворачивания проекта.

Page 85: Python Meetup

Useful links

https://github.com/steelkiwi/django-template

https://www.vagrantup.com/

https://www.ansible.com/

https://github.com/kennethreitz/dj-database-url

https://github.com/joke2k/django-environ