å®å
šãªã¡ãŒã«ããŒã±ãã£ã³ã°ãµãŒãã¹ãæ§ç¯ããã®ã¯ã©ããããé£ããã§ããïŒ ãã®ããã«äœãæ³å®ããå¿
èŠããããŸããïŒ éçºè
ã®å¿ãæ¢æ±ããéçšã§ãã©ããªèœãšã穎ã«ééããå¯èœæ§ããããŸããïŒ

äžç·ã«èããŠã¿ãŸãããã ããã€ãã®èšäºã§ãç§ã1幎以äžã«ããã£ãŠèªåã®ã¡ãŒã«ãã¥ãŒã¹ã¬ã¿ãŒãµãŒãã¹ãã©ã®ããã«è¡ã£ãŠããããã©ã®ãããªæèšãèªåã®ããã«åŠãã ãããããŠããããã¹ãŠã§æ¬¡ã«äœãããã€ãããã«ã€ããŠã話ããŸãã
èšäºãåé¡ã®æè¡é¢ã®ã¿ãèæ
®ããŠããããšãçŽã¡ã«äºçŽããŠãã ããã
æåã®éšåã¯
ããã§
èªãããšã
ã§ããŸã ã
èªåã«ã€ããŠç°¡åã«
ç§ã¯5幎éPythonã§æžããŠããŸããäž»ã«PostgreSQLã®Djangoã䜿çšããŠããŸããjQuery+ KnockoutJSã¬ãã«ã§JavaScriptãæºåã§ããŸããReactã§æžãããšããããŸãã 空ãæéã«ã¯ãUpWorkãšèªåã®ã€ã³ã¿ãŒããããããžã§ã¯ãã§ããªãŒã©ã³ã¹ã®ä»äºãããŸããããã®ãã¡ã®1ã€ã«ã€ããŠã¯ä»ãã話ããŸãã ç§ã¯ãã®ãããžã§ã¯ãã«çŽ1å¹Žåæºãã£ãŠããŸãã
ãã®èšäºã®ç®çã¯äœã§ããïŒ
æåã®éšåã§ã¯ã補åïŒPythonãDjangoãPostgreSQLïŒã®äž»ãªæè¡ãšããŠäœ¿çšããæè¡ãé»åã¡ãŒã«è¿œè·¡ã®ä»çµã¿ãããã³ãµãŒãã¹ã«é¢ããçµ±èšæ
å ±ã«ã€ããŠç°¡åã«èª¬æããŸããã
ãã®ããŒãã§ã¯ã次ã®ããšãäŒããŸãã
1.éåæã¿ã¹ã¯ã䜿çšããŠãŠãŒã¶ãŒãèŠçããå®ãæ¹æ³ã
Celeryã䜿çšããŠè€æ°ã®ã¿ã¹ã¯ãåæã«å®è¡ããããšãæ€èšããŠãã ããã
2.ãªãœãŒã¹ãéåžžã«éãããŠãããµãŒããŒã§ãæ°çŸäžã®ã¬ã³ãŒããå«ãããŒãã«ã䜿çšããŠäœæ¥ãç·šæããæ¹æ³ã 鿣èŠåãããããŒãã«ãé©åãªå Žåãããçç±ã説æããŸãã
3.ã¢ããªã·ãã¯Djangoãããžã§ã¯ãã®æ©èœã®äžéšãåå¥ã®ãã€ã¯ããµãŒãã¹ã«ç§»è¡ããããã«äœ¿çšãããã¯ãããžãŒãæå°éã®æéã³ã¹ãã§è¿
éã«ç®¡çããæ¹æ³ã
ããã§ã¯å§ããŸãããã
Pythonãããžã§ã¯ãã®éåæã¿ã¹ã¯ããŸãã¯ãŠãŒã¶ãŒãåŸ
ããªãçç±
é
ããæ©ãããç¹ã«åæã¢ãŒãã§ãŠãŒã¶ãŒãã©ãŠã¶ãŒããã®èŠæ±ãåŠçããWebããŒã¹ã®ã¢ããªã±ãŒã·ã§ã³ã«ã€ããŠè©±ããŠããå Žåãæé·ãããããžã§ã¯ãã¯ãã¹ãŠããã©ãŒãã³ã¹ã®åé¡ã«çŽé¢ããŸãã
æãç°¡åãªäŸ
pythonãããžã§ã¯ãã«å
žåçãªãã¯ãããžãŒã¹ã¿ãã¯ã䜿çšãããšããŸãããïŒuwsgiã®æ°ãããã«ããnginxãWebãµãŒããŒ+ pythonã¢ããªã±ãŒã·ã§ã³ã Nginxã¯éçãã¡ã€ã«ãæäŸããã«ãŒããuwsgiã«è»¢éããŸãã uwsgiæ§æã§ã¯ãN人ã®ã¯ãŒã«ãŒãããŸãã
ãããã¯ãã¹ãŠã倧éã®ãéãããªã¯ãšã¹ãïŒå€§ããªãã¡ã€ã«ã®èªã¿èŸŒã¿ãªã©ïŒã«ééãããŸã§ã¹ã ãŒãºã«æ©èœããŸããããã«ã¯ãharakiri uwsgiã«è¿ãããŸãã¯ããããé·ãåŠçæéãå¿
èŠã«ãªããŸãã ãã®åŸãuwsgiã¯ãŒã«ãŒã¯ãŠãŒã¶ãŒããã®ãªã¯ãšã¹ããåŠçããæéããªããªãããŠãŒã¶ãŒã¯nginx 502ãããšã©ãŒãåãåããããµãŒãã¹ãã«é¢ããã³ã¡ã³ããèªããŸãã
ãã¡ããããã«ããŒããasyncio + aiohttpãªã©ã®ãœãªã¥ãŒã·ã§ã³ã«é Œãããšãã§ããŸãããããã¯å°ãç°ãªããã®ã§ããæ¢ã«åæDjangoã¢ããªã±ãŒã·ã§ã³ããããæžãæããç Žæ£ããã®ã¯éåžžã«æ®å¿µã§ãã
奜å¥å¿readerçãªèªè
ã¯ãnginxã«è€æ°ã®ã¢ããã¹ããªãŒã ã䜿çšããããšã§Djangoãšaiohttpãçµã¿åãããããšãã§ãããšèšãã§ãããããä»ã¯ãã£ãšæšæºçãªã¢ãããŒããæ€èšããããšæããŸããçŸåšãåæPythonãããžã§ã¯ãã®æãæšæºçãªã¢ãããŒãã¯ãã©ã€ãã©ãªã䜿çšããŠ
Celeryéåæã¿ã¹ã¯ãåŠçããããšã§ãã
以äžã®ãã¹ãŠã®æ
å ±ã¯ãããŒãžã§ã³3.xã§æå¹ã§ãã ç§ã¯ããã䜿çšããŸãã Celeryã®ããŒãžã§ã³4ã§ã¯ãå€ãã倿ŽãããŸããããã®ã©ã€ãã©ãªã«ãŸã æ
£ããŠããªãèªè
ã®ããã«ã以äžã®æ¹æ³ãç¥ã£ãŠãããšããç°¡åãªã³ã¡ã³ãïŒ
1. Celeryãéåæã«å®è¡ããïŒãŸãã¯beatã䜿çšããå¿
èŠããããšãã«å®è¡ããããã«å®è¡ããïŒã¿ã¹ã¯ïŒéåžžã®æ©èœïŒãäœæã§ããŸãã
ã·ã¹ãã é»åã¡ãŒã«ãéä¿¡ããåçŽãªã¿ã¹ã¯ã®äŸïŒ
from celery import current_app ... @current_app.task() def send_service_message(subject, recipients, template, html_template, context): html_mail( subject=subject, template=template, html_template=html_template, recipients=recipients, context=context )
ãã®ã¿ã¹ã¯ã®èª²é¡ã¯æ¬¡ã®ãšããã§ãã
from app.message.tasks import send_service_message ... send_service_message.delay( _(u'%s: ' % subscriber.list.project.domain), [user.email], '', 'site/subscriber/email/subscription_notification.html', context )
2. Djangoã䜿çšãããšããããã®ã¿ã¹ã¯ã管çããããã®éåžžã«äŸ¿å©ãªã€ã³ã¿ãŒãã§ã€ã¹ãåŸãããŸããããã«ãããããšãã°ãå®è¡æã®åã¿ã¹ã¯ã®èµ·åãã©ã¡ãŒã¿ãŒãå®çŸ©ã§ããŸãã
3.ãã®ã·ã¹ãã å
šäœã¯ãã»ãšãã©ã®å ŽåãRabbitMQïŒã¡ãã»ãŒãžãããŒã«ãŒãšããŠäœ¿çšïŒã«åºã¥ããŠå®è¡ãããã»ãšãã©ã®å Žåãsupervisordã䜿çšããŠå¶åŸ¡ãããŸãã ãŸããããŒã¿ããŒã¹ã¯ãããŒã«ãŒïŒæãé
ããªãã·ã§ã³ã§ãããéçºã«ã¯é©ããŠããŸãïŒãRedisïŒãããã³ã«ã®ç¶æãéåžžã«é£ãããããCeleryã®äœæè
ã¯äœ¿çšãæšå¥šããŸããïŒãAmazon SQSãšããŠäœ¿çšã§ããŸãã
ç§ã®ãããžã§ã¯ãã§ã®äœ¿çšäŸ
ç§ã®ãããžã§ã¯ãã§ã¯ããŠãŒã¶ãŒã«ã¡ãŒã«ãã¥ãŒã¹ã¬ã¿ãŒãšãã©ã³ã¶ã¯ã·ã§ã³ã¡ãŒã«ãµãŒãã¹ãæäŸããŠããŸãã
ãããã£ãŠãæœåšçã«2çš®é¡ã®éåæã¿ã¹ã¯ããããŸãã
1.é·ãã¿ã¹ã¯ã éµäŸ¿ç©ã¯ãããã20ãã25000éã®æçŽã§ãæçŽã®éä¿¡ã«å¿
èŠãªã¿ã€ã ã¢ãŠãã®ããã«6-7æé以å
ã«å®è¡ã§ããŸãã
2.çãã¿ã¹ã¯ã ãŠãŒã¶ãŒããµã€ãã«ç»é²ãããã®ããã¯ãšã³ããç»é²ç¢ºèªãèšèŒããã¡ãŒã«ãç§ã®ãµãŒãã¹ã®APIã«éä¿¡ãããªã¯ãšã¹ããéä¿¡ããŸãããæ°ç§åŸïŒãŸãã¯ãã以äžã®éãïŒã«ããŠãŒã¶ãŒã¯åä¿¡ãã¬ã€ã«ãã®æçŽãèŠãã¯ãã§ãã
1ã€ã®ãã¥ãŒå
ã§ãã¹ãŠã®ã¿ã¹ã¯ã®èšç»ãšå®è¡ãå¿ãããšïŒã€ãŸããããã©ã«ãã§ïŒããã§ã«å®è¡ãããŠããé·ãã¿ã¹ã¯ãæ¢ã«ããå Žåããã¹ãŠã®çãã¿ã¹ã¯ã¯ãã®å®äºãåŸ
ã¡ãŸãã
6ã7æéåŸã«ãã©ã³ã¶ã¯ã·ã§ã³ã¬ã¿ãŒãåãåãããšã¯ãªãã·ã§ã³ã§ã¯ãªãããšã¯æããã§ãããããã¿ã¹ã¯ã®äžŠååŠçã«è€æ°ã®ãã¥ãŒã䜿çšããããšã«ãªããŸãã
ãããè¡ãã«ã¯ããããžã§ã¯ãã§ãã¥ãŒèªäœãæ§æããŸãã
ç§ã®ãããžã§ã¯ãã§ã¯ã3ã€ã®ãã¥ãŒã䜿çšããŸãã
1.ãã©ã³ã¶ã¯ã·ã§ã³-ãã©ã³ã¶ã¯ã·ã§ã³ã¬ã¿ãŒãéä¿¡ããããã®ãã¥ãŒ
2.ãã«ã¯-ãã¥ãŒã¹ã¬ã¿ãŒãéä¿¡ããããã®ãã¥ãŒ
3.ã»ããª-æ®ãã®ã¿ã¹ã¯ã®é çªã
ãããã£ãŠãsupervisor.dã§ã¯ãæ§æã¯æ¬¡ã®åœ¢åŒãåããŸãã
[program:celery] command=<PROJECT_ROOT>/venv/bin/celery worker -A conf.celery -l INFO --concurrency=4 --pidfile=/var/run/celery/celery.pid -Q celery ... [program:bulk] command=<PROJECT_ROOT>/venv/bin/celery worker -A conf.celery -l INFO --concurrency=4 --pidfile=/var/run/celery/bulk.pid -Q bulk ... [program:transactional] command=<PROJECT_ROOT>/venv/bin/celery worker -A conf.celery -l INFO --concurrency=4 --pidfile=/var/run/celery/transactional.pid -Q transactional ...
ããã§ã¯å€ãã®èšå®ãªãã·ã§ã³ãçç¥ãããŠããããšãçè§£ãããŸãã èŠããã«ãåã¿ãŒã³ã§ã¹ãŒããŒãã€ã¶ãŒã§ããã»ã¹ãå®è¡ããå¿
èŠããããšããããšã§ãã
ç¹å®ã®ãã¥ãŒã«ã¿ã¹ã¯ãèšå®ããæ¹æ³ã¯æ¬¡ã®ãšããã§ãã
send_message.apply_async(kwargs={'mailer': self, 'message': message}, queue='transactional')
ãã®çµæãè€æ°ã®ã¿ã¹ã¯ãåæã«åŠçã§ãããŠãŒã¶ãŒã¯æºè¶³ããŠããããã«èŠããŸãããµãŒãã¹ã¯ååã«é«éã«åäœããuwsgiã¯ãŒã«ãŒã¯ç¡æã§ãã
ã¿ã¹ã¯ã¹ããŒã¿ã¹ã®è¿œè·¡
ã¿ã¹ã¯ã®ã¹ããŒã¿ã¹ã®è¡šç€ºã¯éåžžã«ç°¡åã§ãã
celery -A conf.celery inspect scheduled celery -A conf.celery inspect active celery -A conf.celery inspect reserved
ãŸãããã䟿å©ãªç£èŠã®ããã«ã
Flowerãœãªã¥ãŒã·ã§ã³ã䜿çšã§ããŸãã
ç§ã¯èªåèªèº«ãã»ããªã®è¶
å°éå®¶ãšã¯èããŠããŸãããèªè
ãããã䜿ã£ãçµéšãå
±æããŠããããå¬ããã§ãã
倧ããªããŒãã«ã®éæ£èŠå-é·æãšçæ
é
ããæ©ãããããŒã¿ãèç©ããŸãã ãããŠã圌ãã¯äœãšãããŠåããªããã°ãªããŸããã
ç§ã®ãµãŒãã¹ã¯1ãæãããçŽ40äžä»¶ã®ã¡ãŒã«ãåŠçããŸãïŒããã¯éåžžã«å°ããã§ãïŒãã90æ¥ä»¥äžåã®ã¡ãŒã«ã«é¢ããããŒã¿ãæ¶å»ããŠããŸããã远跡ããŒã¿ãå«ãããŒãã«ã«ã¯çŽ200äžä»¶ã®ã¬ã³ãŒããæ®ã£ãŠããŸãã
ãããã®ããŒã¿ã¯çµ±èšã«è¡šç€ºããå¿
èŠããããããããæäœããå¿
èŠããããã¹ãããŒãç¹å®ããããã«ããããåæããå¿
èŠããããŸãã
æåã¯ãSQLã®ãã¹ãŠã®æšæºã«åŸã£ãŠæ§ç¯ãããããŒãã«ãè¶
æ£èŠåãããŸããã ããããçŸå®ã«ã¯ãORM Djangoã¯ããŒãã«éã®ããªãè€éãªJOINã®ããã«ãããªããšããªãã®ãããžã§ã¯ãã®ç掻ãå°é£ã«ããããšããããããŸãã
ããã«ãç¹ã«åå¿è
ããããããŒã®å Žåãã¯ãšãªã»ãããç¹°ãè¿ããšãã«ã¯ãšãªãããã€çºçãããã¯å¿
ãããæããã§ã¯ãããŸããã
ãã®çµæãããªãåçŽãªã¯ãšãªãèšç®ããã«ã¯ïŒ
SELECT d.date, SUM(CASE WHEN me.status = 'SENT' THEN 1 ELSE 0 END) AS sent_count, SUM(CASE WHEN me.status = 'OPENED' THEN 1 ELSE 0 END) AS opened_count, SUM(CASE WHEN me.status = 'REDIRECTED' THEN 1 ELSE 0 END) AS redirected_count, SUM(CASE WHEN me.status = 'HARDBOUNCED' THEN 1 ELSE 0 END) AS hardbounced_count, SUM(CASE WHEN me.status = 'SOFTBOUNCED' THEN 1 ELSE 0 END) AS softbounced_count, SUM(CASE WHEN me.status = 'UNSUBSCRIBED' THEN 1 ELSE 0 END) AS unsubscribed_count FROM ( SELECT TO_CHAR(date_trunc('day', ('2017-03-01'::DATE - offs)), 'YYYY-MM-DD') AS date FROM generate_series(0, 30, 1) AS offs ) d LEFT OUTER JOIN MESSAGE_MESSAGEEVENT me ON (d.date=to_char(date_trunc('day', me.date_created), 'YYYY-MM-DD') AND status IN ('SENT', 'OPENED', 'REDIRECTED', 'HARDBOUNCED', 'SOFTBOUNCED', 'UNSUBSCRIBED') AND id IN (...)) GROUP BY date ORDER BY date
çŽ100ç§ããããŸããã æãããæéã§ããã ããã¯ããã€ãã®çç±ã§èµ·ãããŸããã
1.æåã«ãããã絶察ã«è¡ããªãã§ãã ããïŒ
AND id INïŒ...ïŒé
åã«çŽ100,000åã®èŠçŽ ãããå Žå:)
2.第äºã«ã
d.date = to.charïŒdate_truncïŒ 'day'ãme.date_createdïŒã 'YYYY-MM-DD'ïŒããã
d.date = me.date_created :: dateãšæžãæ¹ãã¯ããã«è¯ãã§ãã
3.第äžã«ã衚ã«ã¯ä»ã®ããã€ãã®ããŒã®å€éšããŒããããŸããã JOINã®åäœãéåžžã«é
ãããã
äœã«æ¥ãã®
1.å€éšããŒïŒéšåçã«éæ£èŠåãããããŒãã«ïŒã«å ããŠãé¢é£ã¬ã³ãŒãã®æç€ºçãªå€ã®ãã£ãŒã«ãã远å ããŸããã
2.ãªã¯ãšã¹ããæžãçŽããŸããã
SELECT d.date, SUM(CASE WHEN me.status = 'SENT' THEN 1 ELSE 0 END) AS sent, SUM(CASE WHEN me.status = 'OPENED' THEN 1 ELSE 0 END) AS opened, SUM(CASE WHEN me.status = 'HARDBOUNCED' THEN 1 ELSE 0 END) AS hardbounced, SUM(CASE WHEN me.status = 'SOFTBOUNCED' THEN 1 ELSE 0 END) AS softbounced, SUM(CASE WHEN me.status = 'UNSUBSCRIBED' THEN 1 ELSE 0 END) AS unsubscribed, SUM(CASE WHEN me.status = 'REDIRECTED' THEN 1 ELSE 0 END) AS redirected FROM ( SELECT ('2017-03-01'::DATE - offs)::date AS date FROM generate_series(0, 30, 1) AS offs ) d LEFT OUTER JOIN MESSAGE_MESSAGEEVENT me ON ( d.date=me.date_created::date AND me.date_created >= '2017-02-01 00:00:00' AND me.date_created <= '2017-03-01 23:59:59' AND true = true AND me.project_id = ... ) GROUP BY date ORDER BY date;
ãã®çµæããã®èŠæ±ã®åŠçæéã¯3ç§ã«ççž®ãããŸããããããã¯ãã§ã«ããªãèããããŸãã 説æããïŒ
GroupAggregate (cost=61552.47..82232.11 rows=200 width=11) (actual time=2445.092..2986.524 rows=31 loops=1) -> Merge Left Join (cost=61552.47..73614.51 rows=491920 width=11) (actual time=2445.048..2857.069 rows=69314 loops=1) Merge Cond: ((('2017-03-01'::date - offs.offs)) = ((me.date_created)::date)) -> Sort (cost=62.33..64.83 rows=1000 width=4) (actual time=0.127..0.174 rows=31 loops=1) Sort Key: (('2017-03-01'::date - offs.offs)) Sort Method: quicksort Memory: 26kB -> Function Scan on generate_series offs (cost=0.00..12.50 rows=1000 width=4) (actual time=0.034..0.069 rows=31 loops=1) -> Materialize (cost=61490.14..62719.94 rows=98384 width=15) (actual time=2444.913..2695.888 rows=69312 loops=1) -> Sort (cost=61490.14..61736.10 rows=98384 width=15) (actual time=2444.908..2539.290 rows=69312 loops=1) Sort Key: ((me.date_created)::date) Sort Method: external merge Disk: 2152kB -> Bitmap Heap Scan on message_messageevent me (cost=11442.78..51647.59 rows=98384 width=15) (actual time=1922.446..2253.982 rows=69312 loops=1) Recheck Cond: (project_id = ...) Filter: ((date_created >= '2017-02-01 00:00:00+01'::timestamp with time zone) AND (date_created <= '2017-03-01 23:59:59+01'::timestamp with time zone)) -> Bitmap Index Scan on message_messageevent_b098ad43 (cost=0.00..11418.19 rows=236446 width=0) (actual time=1870.742..1870.742 rows=284445 loops=1) Index Cond: (project_id = ...) Total runtime: 2988.107 ms
ãã®ãœãªã¥ãŒã·ã§ã³ã®æ¬ ç¹ã¯äœã§ããïŒ
ãŸããããŒãã«ã¯ããå€ãã®ãã£ã¹ã¯å®¹éãå æããŸãã 第äºã«ã鿣èŠåãããããŒãã«ã®æååå€ãé¢é£ããå¿
èŠããããŸãã ã€ãŸã 芪ããŒãã«ã®æååèå¥åã倿Žããå Žåã¯ãåã®éæ£èŠåããŒãã«ã§ãæååèå¥åã倿Žããå¿
èŠããããŸãã 幞ããªããšã«ãDjangoã®ç§»è¡ã«ããããããéåžžã«ç°¡åã«å®è£
ã§ããŸãã
ã¢ããªã·ãã¯ãããžã§ã¯ãæ©èœã®ãã€ã¯ããµãŒãã¹ãžã®ç§»è¡

å€ããå°ãªããå€§èŠæš¡ãªãããžã§ã¯ããæé·ãããããšã®åé¡ã®1ã€ã¯ãç¶æããããšããŸããŸãé£ãããªãããšã§ãã ã³ãŒãããŒã¹ã¯æé·ãããã¹ãã®æ°ã¯å¢å ããŠããŸãã ããã«ããããžã§ã¯ãã®äžéšã§ãã¹ããããšãä»ã®äººããã®ããã«èŠããå¯èœæ§ããããŸãã ããšãã°ãã©ã³ãã ãªSyntaxErrorã¯ãããžã§ã¯ãå
šäœãå§åããŸãã
ãŸããæ¢ã«èª¬æãã倧éã®ã¯ãšãªã«é¢é£ããããã©ãŒãã³ã¹ã®åé¡ã¯ããŸããŸãéèŠã«ãªã£ãŠããŸãã
解決çã¯ãæ©èœã®äžéšãåå¥ã®ãã€ã¯ããµãŒãã¹ã«åé¢ããããšã§ãã
1.ãã®æ©èœã«é¢é£ããã³ãŒãããŒã¹ãšãã¹ããå¥ã®ãããžã§ã¯ãã«å
¥ããŸãã
2.åå¥ã®uwsgiã€ã³ã¹ã¿ã³ã¹ãšãnginxã®åå¥ã®ã¢ããã¹ããªãŒã ãŸãã¯ãµãŒããŒãäœæããŸãã
3. RabbitMQãHTTP APIãªã©ã®ãããŒã«ãŒã䜿çšããŠã¡ã€ã³ãããžã§ã¯ãã«æ¥ç¶ããŸãã
...
4.å©çïŒ
ãã€ã¯ããµãŒãã¹ã¢ãŒããã¯ãã£ã®é·æïŒ
1.ããããã®æ©èœéšåã®ãšã©ãŒã«ãããããžã§ã¯ãã倱æããå±éºæ§ã¯ãªããªããŸããã
2.ä»ã®ãããžã§ã¯ãã§åå©çšã§ããŸãã ç¹ã«ãããããçš®é¡ã®ççž®ãªã³ã¯ãç»åå§çž®æ©ãªã©ã®ã¢ããªã±ãŒã·ã§ã³ãã€ã¯ããµãŒãã¹ã«é¢é£ããŠããŸãã
3.ãµãŒãã¹ã®äŸåé¢ä¿ãäºãã«ç¬ç«ããŠæŽæ°ã§ããŸãã ããšãã°ãã¡ã€ã³ãããžã§ã¯ãã§Djangoã®ã¬ã¬ã·ãŒããŒãžã§ã³ã䜿çšããå ŽåãéèŠãªã³ãŒãããŒã¹ãé©åãããå¿
èŠãªãããã€ã¯ããµãŒãã¹ã§æ°ããããŒãžã§ã³ã䜿çšã§ããŸãã
ç§ã®å Žåããããžã§ã¯ãã管çããããã«ç°¡åãªç®¡çããã«ãå®è£
ããå¿
èŠããããŸããã ãŠãŒã¶ãŒã«ãããµãŒãã¹ã®äœ¿çšã«é¢ããããªãéãã¬ããŒããå«ãŸããŠãããããåå¥ã«åäœããå¿
èŠããããã¡ã€ã³ãããžã§ã¯ãã®uwsgiã¯ãŒã«ãŒãæ»æããããããŸããã§ããã
ç§ã䜿çšããæè¡ïŒ
1.ã¡ã€ã³ãããžã§ã¯ãã®äžéšãšããŠHTTP APIãæ§ç¯ããããã®
Django Rest Framework ïŒä»¥é-DRFïŒã
2.管çã·ã¹ãã ã®ã¯ã©ã€ã¢ã³ã-ReactJSãNodeJSãBabelãES6ãããã³ããã³ããšã³ãã®äžçããç§ã«ã¯çè§£ã§ããªãèšèãæžãããã«ã
ãã«ãAPI
DRFã䜿çšãããšãæ¢åã®ãããžã§ã¯ãã§äŸ¿å©ãªREST APIãè¿
éã«æ§ç¯ã§ããŸãã ç§ã®å Žåãæ¬¡ã®èŠä»¶ãæºããã€ã³ã¿ãŒãã§ãŒã¹ãäœæããå¿
èŠããããŸããã
1. APIãã³ãã«ãžã®ã¢ã¯ã»ã¹ã¯å®å
šã§ãªããã°ãªããŸããã 管çè
æš©éãæã€ç¹å®ã®ãŠãŒã¶ãŒã®ã¿ã管çã·ã¹ãã ã«ã¢ã¯ã»ã¹ã§ããŸãã
2.ã€ã³ã¿ãŒãã§ã€ã¹ã¯ãã¯ã©ã€ã¢ã³ãSPAã¢ããªã±ãŒã·ã§ã³ã«äŸ¿å©ãªå¯Ÿè©±æ¹æ³ãæäŸããå¿
èŠããããŸãã
3.ããã«äœ¿çšã§ããããã«ããã³ã¯ãªããžã§ã¯ãã®ãªã¹ããç¹å®ã®ãªããžã§ã¯ãã«é¢ããæ
å ±ã®ååŸããªããžã§ã¯ãã®åé€ãªã©ã«æ©èœããå¿
èŠããããŸãã äžè¬ã«ãAPIã¯ã¯ã©ã€ã¢ã³ãåŽã§éåžžã®CRUDã€ã³ã¿ãŒãã§ã€ã¹ãæ§ç¯ããã®ã«ååãªã¯ãã§ãã
ãã°ã€ã³
ã¯ã©ã€ã¢ã³ãããã®èŠæ±ãæ¿èªããããã«ãDRFã®æ¢æã®ãœãªã¥ãŒã·ã§ã³-ããŒã¯ã³èªèšŒã䜿çšããŸããã
ãããæå¹ã«ããããã«å¿
èŠãªãã¹ãŠïŒ
1.ãrest_framework.authtokenããINSTALLED_APPSã«è¿œå ããŸãã
2.ç§»è¡ãå®è¡ããŸãã
3.管çã·ã¹ãã ãžã®ã¢ã¯ã»ã¹æš©ãå¿
èŠãªãŠãŒã¶ãŒã®ããŒã¯ã³ãäœæããŸãã
次ã«ãå¿
èŠãªãã¥ãŒã»ããã«ããã¯ã¹ããå
±éã®ããã¯ã¹ã€ã³ãäœæããŸãã
ããã«ããã®ããã¯ã¹ã€ã³ã䜿çšããŠãæ¿èªãViewSetã«æ¥ç¶ããã ãã§ãã
... from app.contrib.api.views import AdminAuthMixin, MultiSerializerViewSet ... class CampaignsViewSet(AdminAuthMixin, MultiSerializerViewSet): queryset = Campaign.objects.filter(date_archived__isnull=True) filter_class = CampaignsFilter serializers = { 'list': CampaignsListSerializer, 'retrieve': CampaignDetailSerializer, }
顧客ã€ã³ã¿ã©ã¯ã·ã§ã³
axiosã©ã€ãã©ãªã¯ãã¯ã©ã€ã¢ã³ãåŽã§APIãšããåãããã®ã«
æé©ã§ããã
APIãªã¯ãšã¹ãã®äŸïŒ
axios.get('http://example.com/api/entrypoint/', {params: this.state.query_params}) .then(response => { const items = response.data.results.map(obj => obj); this.setState({ is_loading: false, items: items, count: response.data.count }); });
Reactã¢ããªã±ãŒã·ã§ã³ã®ã³ã³ããŒãã³ãã®ã³ãŒãå
šäœãæäŸããŸããããªããªãã ããã¯ããªãæšæºã§ãã
PS
以äžã®èšäºã§ã¯ãlogstashãkibanaãelasticsearchã«åºã¥ããå€§èŠæš¡ãããžã§ã¯ãã§ãèŠåŽããã«ãã°ãæŽçããæ¹æ³ãããã³HelpScoutããã³GitBookãœãªã¥ãŒã·ã§ã³ã«åºã¥ãã¯ã©ã€ã¢ã³ãã®ããã¥ã¡ã³ããšãµããŒãã«è§Šããæ¹æ³ã«ã€ããŠèª¬æããŸãã
ãæž
èŽããããšãããããŸããïŒ