Falcon Web Frameworkを使ったPythonでのREST APIの開発とデプロイメント

今回の記事では、**PyPy**上で**Falcon**のウェブフレームワークを使って**Python**でREST APIを開発・デプロイしていきます。

*本ブログは英語版からの翻訳です。オリジナルは[こちら](https://www.alibabacloud.com/blog/building-very-fast-app-backends-with-falcon-web-framework-on-pypy_594282)からご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。*

#はじめに：Falconとは何か？
>Falconは、スピーディーなWeb APIやアプリのバックエンドを構築するための最小限のWSGIライブラリです。HTTP APIの構築に関しては、他のフレームワークは膨大な量の依存関係や不要な抽象化に悩まされています。Falconは、HTTPとRESTアーキテクチャスタイルを採用したすっきりとしたデザインです。

あなたの頭に浮かぶ最初のことは、おそらく「なぜFalconを使うべきなのか？」ということでしょう。さて、Falconはflask、Django、Bottle、web.pyのような他のpythonのフレームワークと同じくらいパワフルで、高速で、拡張性があり、信頼性が高く、RESTfulなスタイルを奨励しています。[ベンチマーク](https://falconframework.org/?spm=a2c65.11461447.0.0.41b076d9xwWUxz#sectionBenchmarks)によると、FalconはflaskやDjangoよりもpypy上で21倍も高速です。LinkedIn、Leadpages、Wargaming、およびRackspaceのような組織は、すでにアプリケーションにFalconを使用しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/b5050193-61d1-ed77-4903-9546feb4636b.png)



#Alibaba Cloud ECSへのFalconのインストール
Alibaba [Elastic Compute Service](https://www.alibabacloud.com/product/ecs?spm=a2c65.11461447.0.0.41b076d92XPD05) (ECS)へのFalconのインストールは、PCへのインストールと同じくらい簡単です。

###Alibaba ECS上でUbuntu 16.04をプロビジョニングする
このチュートリアルを参考にして、[Alibaba_Cloud上のUbuntu Serverをセットアップ](https://www.alibabacloud.com/blog/how-to-set-up-your-first-ubuntu-16-04-server-on-alibaba-cloud_593747?spm=a2c65.11461447.0.0.41b076d92XPD05)してサーバーをプロビジョニングする手順を参考にしてください。

###Ubuntu 16.04にpypy3と依存関係をインストールする
コードを速く動かしたいなら、PyPy を使うべきです。

1、スピード：PythonのプログラムはPyPyの方が高速に動作することが多いです。(JITコンパイラとは何ですか？)
2、メモリ使用量: メモリを大量に消費するPythonプログラム(数百MB以上)はCPythonよりも少ないスペースで動作します。
3、互換性：PyPyは既存のPythonコードと非常に互換性があります。cffiをサポートしており、twistedやDjangoのような一般的なPythonライブラリを動かすことができます。
5、スタックレス： PyPyはデフォルトでスタックレスモードをサポートしています。

```
$ sudo apt-get install python3-dev
$ wget https://bitbucket.org/pypy/pypy/downloads/pypy3-v6.0.0-linux64.tar.bz2
$ tar -xvf pypy3-v6.0.0-linux64.tar.bz2  -C /opt
$ sudo ln -s /opt/pypy3-v6.0.0-linux64/bin/pypy3 /usr/local/bin/pypy3
$ sudo chown -R <youruser>:<yourgroup> pypy3-v6.0.0-linux64/
$ pypy3
Python 3.5.3 (fdd60ed87e94, Apr 24 2018, 06:10:04)
[PyPy 6.0.0 with GCC 6.2.0 20160901] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>> import this

```

pypy3をインストールしたら、pypy3のpipと互換性のあるpythonの依存関係をインストールしなければなりません。

```
$ wget https://bootstrap.pypa.io/get-pip.py
$ pypy3 get-pip.py   # pypy3 -m ensurepip   
$ sudo apt-get install python3-pip
$ pip3 install virtualenvwrapper
```

[virtualenvwrapper](https://virtualenvwrapper.readthedocs.io/en/latest/install.html?spm=a2c65.11461447.0.0.41b076d92XPD05) をインストールします。

.bashrc または .profile に以下の行を追加します。

```
export WORKON_HOME=$HOME/.virtualenvs
export PROJECT_HOME=$HOME/Devel
source /usr/local/bin/virtualenvwrapper.sh
```

pythonの依存関係はvirtualenvでインストールした方が良いです。

```
$ mkvirtualenv -p /usr/local/bin/pypy3 falconenv
(falconenv):~$ pypy3 -m pip install falcon mongoengine
```

###Ubuntu 16.04にMongodbをインストールする
>[MongoDB](https://www.alibabacloud.com/product/apsaradb-for-mongodb?spm=a2c65.11461447.0.0.41b076d92XPD05)はオープンソースのドキュメントデータベースで、高性能、高可用性、自動スケーリングを実現しています。

```
$ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927
$ echo "deb http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list
$ sudo apt-get update
$ sudo apt-get install -y mongodb-org 
$ sudo systemctl start mongod  
$ sudo systemctl status mongod
# This command will start mongod. Other options are stop | restart | status. 
? mongodb.service - High-performance, schema-free document-oriented database
   Loaded: loaded (/etc/systemd/system/mongodb.service; enabled; vendor preset: enabled)
   Active: active (running) since Mon 2016-04-25 14:57:20 EDT; 1min 30s ago
 Main PID: 4093 (mongod)
    Tasks: 16 (limit: 512)
   Memory: 47.1M
      CPU: 1.224s
   CGroup: /system.slice/mongodb.service
           ??4093 /usr/bin/mongod --quiet --config /etc/mongod.conf
```

#REST APIの構築
ユーザーがbookを作成したり、bookを更新したり、MongoDBからidでbookを取得したりするbookプロジェクトを作成します。

###Falconアプリのディレクトリ構造

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/71b20f1f-6ce6-c911-a88e-8e2ecfa1878b.png)

`book_project/book/`には以下のpythonファイルがあります。

models : modelsでは[mongoengine](http://docs.mongoengine.org/guide/defining-documents.html?spm=a2c65.11461447.0.0.41b076d92XPD05)のORMを使用しています。mongoengineのDocumentクラスを継承したクラスは、MongoDBデータベースに相当するコレクションを記述しています。ここでは、フィールド名、ISBN、著者名を持つリソースBookを作成しました。リソースのオブジェクトごとにユニークなIDを自動生成してくれます。

```
from mongoengine import StringField, IntField, Document

class Book(Document):
    name = StringField()
    isbn = IntField()
    author= StringField()
```

api : bookリソースには、on_get, on_post という enpoint メソッドがあります。各メソッドの使い方は以下のコードコメントに記載されています。

api.pyのソースコードは以下の通りです。

```
import falcon, json
from .models import Book

class BookResource(object):
    def on_get(self,req,resp,book_id):
        '''
        :param req: A request object
        :param resp: A response object
        :param book_id: book_id received in http path to query book object
        :return:
        '''
        book_obj= Book.objects.get(id=book_id)
        #Query book collection to get a record with id = book_id
        resp.body = json.dumps({'author':book_obj.author,'name':book_obj.name,'isbn':book_obj.isbn})
        #It will set response body as a json object of book fields.
        resp.status = falcon.HTTP_200
        #Finally return 200 response on success

    def on_post(self,req,resp):
        '''
         This method will recevie the book data in request body in order to store it in database
        :param req: It contains json of book's author, name and isbn.
        :param resp:
        :return:
        '''
        book_data = req.media
        #req.media will deserialize json object
        book_obj=Book.objects.create(**book_data)
        #passing book_data to create method. It will create a database document in book collection.
        resp.body = json.dumps({'book_id':str(book_obj.id),'message':'book succesfully created'})
        resp.status=falcon.HTTP_200
```

指定されたIDの本を取得するためのGETリクエストのエンドポイント：`http://<host>:<port>/<book_id>`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/9e70d06a-2d0f-837b-cee3-285674121980.png)

データを持つ本を作成するためのPOSTリクエストのエンドポイント：`http://<host>:<port>/<book_id> `

```
{"author":<author_name>,"name":<book_name>, "isbn":<isbn>}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/66fb3690-cb94-adab-0ce1-ebf99151f91e.png)


app：これは HTTP リクエストのエントリーポイントで、HTTP パスやその他の設定に基づいたリソースへのルーティングが含まれています。

```
import falcon
from mongoengine import connect
from .api import BookResource

connect('bookdb', host='127.0.0.1', port=27017)
app = application = falcon.API()
books = BookResource()
app.add_route('/book/{book_id}', books)
app.add_route('/books/', books)
```

**connect(, host=, port=)**, この行は、与えられた資格情報を使って自動的にmongodbデータベースへの接続を作成します。この接続は、アプリのスコープ全体でグローバルに使用されます。

**app = application = falcon.API()**, この行は、gunicorn (ウェブアプリケーションサーバ)が自動検索してwsgiアプリとして動作するアプリケーションインスタンスを作成します。

**books = BookResource()**, この行は、リソースのインスタンスを作成します。

**app.add_route('/book/{book_id}', books)とapp.add_route('/books/', books)**は、httpパスとメソッドをリソースのそれぞれのメソッドにルーティングします。

#GunicornとNginxでWebアプリをデプロイする
**Gunicorn**

>Gunicorn 'Green Unicorn'は、Python WSGI HTTP Server for UNIXです。プリフォークワーカーモデルです。Gunicornサーバは様々なWebフレームワークと幅広く互換性があり、シンプルに実装されており、サーバリソースが軽く、かなり高速です。

簡単に言えば、gunicornはHTTPリクエストを理解し、HTTPバイトをPythonオブジェクトに変換することで、Pythonアプリケーションでも理解できるようにします。

以下のコマンドを使ってubuntuマシン上でgunicornを実行してください。

```
(falconenv):~$ pypy3 -m pip install gunicorn
(falconenv):~/book_project$  gunicorn --reload book.app --bind 127.0.0.1:9000
```

**Nginx**

>NGINXは、ウェブサーバ、リバースプロキシ、キャッシング、ロードバランシング、メディアストリーミングなどのためのオープンソースソフトウェアです。NGINXは、最大のパフォーマンスと安定性を目指して設計されたWebサーバーとしてスタートしました。HTTPサーバー機能に加えて、NGINXは電子メール（IMAP、POP3、SMTP）のプロキシサーバーとして、またHTTP、TCP、UDPサーバーのリバースプロキシとロードバランサーとしても機能します。

簡単に説明すると、nginxはHTTPリクエストバイトを受信し、HTTPレスポンスバイトをクライアントに送信するソフトウェアです。NginxはHTTPリクエストバイトをpythonアプリケーションサーバ(gunicorn)に転送する役割を担っています。

```
$ sudo apt-get install nginx
```

`sudo vim falcon_nginx.conf`

```
upstream falcon_gunicorn {
    server 127.0.0.1:9000;
}
 
server {
    listen  80;
    server_name <ALIBABA ECS instance public ip>;
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;
    
    location / {
            proxy_pass         http://falcon_gunicorn;
            proxy_redirect     off;
            proxy_set_header   Host $host;
    }
}
```

```
$ sudo cp falcon_nginx.conf /etc/nginx/sites-enabled/nginx.conf
$ sudo service nginx restart
```

#概要
REST APIを開発して迅速にデプロイするためには、Falconが最良の選択肢の一つであることが証明されています。Alibaba Cloudの[Elastic Compute Service](https://www.alibabacloud.com/product/ecs?spm=a2c65.11461447.0.0.41b076d92XPD05) (ECS) Ubuntu Serverにpypy3, falcon, [MongoDB](https://www.alibabacloud.com/product/apsaradb-for-mongodb?spm=a2c65.11461447.0.0.41b076d92XPD05), gunicorn, Nginxとその他の依存関係をインストールする必要があります。falconのAPI構文は完全にRESTアーキテクチャに基づいており、適切なルーティング、モデル構造、およびビューを使用して非常に簡単に開発することができます。Falconはスケーラブルなソリューションであり、gunicorn, uwsgi, waitressのようなWSGIサーバ上に簡単にデプロイすることができます。gunicornサーバの上にNginxを追加することで、マイクロサービスのスケーラビリティ、分散性、安全性を向上させることができます。

*アリババクラウドは日本に２つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
[アリババクラウドジャパン公式ページ](https://www.alibabacloud.com/ja)*