sql >> データベース >  >> RDS >> Database

Python、Ruby、およびGolang:Webサービスアプリケーションの比較

    コマンドラインアプリケーションのPython、Ruby、Golangを最近比較した後、同じパターンを使用して単純なWebサービスの構築を比較することにしました。この比較には、Flask(Python)、Sinatra(Ruby)、およびMartini(Golang)を選択しました。 はい、各言語のWebアプリケーションライブラリには他にも多くのオプションがありますが、これら3つは比較に適していると感じました。


    ライブラリの概要

    Stackshareによるライブラリの概要は次のとおりです。


    フラスコ(Python)

    Flaskは、Werkzeug、Jinja2、および善意に基づくPython用のマイクロフレームワークです。

    このデモに示されているような非常に単純なアプリケーションの場合、Flaskは最適な選択肢です。基本的なFlaskアプリケーションは、単一のPythonソースファイルに含まれるわずか7行のコード(LOC)です。他のPythonWebライブラリ(DjangoやPyramidなど)に対するFlaskの利点は、小規模から始めて、必要に応じてより複雑なアプリケーションを構築できることです。

    from flask import Flask
    app = Flask(__name__)
    
    @app.route("/")
    def hello():
        return "Hello World!"
    
    if __name__ == "__main__":
        app.run()
    


    シナトラ(ルビー)

    Sinatraは、最小限の労力でRubyでWebアプリケーションをすばやく作成するためのDSLです。

    Flaskと同様に、Sinatraは単純なアプリケーションに最適です。基本的なSinatraアプリケーションは、単一のRubyソースファイルに含まれる4つのLOCのみです。 Flaskと同じ理由で、Ruby on Railsなどのライブラリの代わりにSinatraが使用されます。小規模から始めて、必要に応じてアプリケーションを拡張できます。

    require 'sinatra'
    
    get '/hi' do
      "Hello World!"
    end
    


    マティーニ(ゴラン)

    Martiniは、GolangでモジュラーWebアプリケーション/サービスをすばやく作成するための強力なパッケージです。

    マティーニには、シナトラとフラスコの両方よりもいくつかのバッテリーが含まれていますが、それでも最初は非常に軽量です-基本的なアプリケーションではわずか9LOCです。 Martiniは、Golangコミュニティからいくつかの批判を受けていますが、GolangWebフレームワークの中で最も評価の高いGithubプロジェクトの1つです。マティーニの作者はここでの批判に直接応えました。他のフレームワークには、Revel、Gin、さらには組み込みのnet/httpライブラリが含まれます。

    package main
    
    import "github.com/go-martini/martini"
    
    func main() {
      m := martini.Classic()
      m.Get("/", func() string {
        return "Hello world!"
      })
      m.Run()
    }
    

    基本がわからなくなったら、アプリを作成しましょう!




    サービスの説明

    作成されたサービスは、非常に基本的なブログアプリケーションを提供します。次のルートが構築されます:

    • GET / :ブログを返します(テンプレートを使用してレンダリングします)。
    • GET /json :ブログのコンテンツをJSON形式で返します。
    • POST /new :ブログに新しい投稿(タイトル、概要、コンテンツ)を追加します。

    ブログサービスへの外部インターフェースは、各言語でまったく同じです。簡単にするために、この例のデータストアとしてMongoDBを使用します。これは、セットアップが最も簡単で、スキーマについてまったく心配する必要がないためです。通常の「ブログのような」アプリケーションでは、リレーショナルデータベースが必要になる可能性があります。


    投稿を追加

    POST /new

    $ curl --form title='Test Post 1' \
         --form summary='The First Test Post' \
         --form content='Lorem ipsum dolor sit amet, consectetur ...' \
         http://[IP]:[PORT]/new
    


    HTMLを表示

    GET /



    JSONを表示

    GET /json

    [
       {
          content:"Lorem ipsum dolor sit amet, consectetur ...",
          title:"Test Post 1",
          _id:{
             $oid:"558329927315660001550970"
          },
          summary:"The First Test Post"
       }
    ]
    



    アプリケーション構造

    各アプリケーションは、次のコンポーネントに分類できます。


    アプリケーションのセットアップ

    • アプリケーションを初期化します
    • アプリケーションを実行する


    リクエスト

    • ユーザーがデータをリクエストできるルートを定義する(GET)
    • ユーザーがデータを送信できるルートを定義する(POST)


    応答

    • JSONをレンダリングします(GET /json
    • テンプレートをレンダリングします(GET /


    データベース

    • 接続を開始します
    • データを挿入
    • データを取得する


    アプリケーションの展開

    • Docker!

    この記事の残りの部分では、ライブラリごとにこれらの各コンポーネントを比較します。 目的は、これらのライブラリの1つが他のライブラリよりも優れていることを示唆することではなく、3つのツール間の特定の比較を提供することです。

    • フラスコ(Python)
    • シナトラ(ルビー)
    • マティーニ(ゴラン)



    プロジェクトの設定

    すべてのプロジェクトは、dockerおよびdocker-composeを使用してブートストラップされます。各アプリケーションが内部でブートストラップされる方法に飛び込む前に、dockerを使用して、まったく同じ方法で各アプリケーションを起動して実行することができます-docker-compose up

    真剣に、それだけです!これで、アプリケーションごとにDockerfileがあります。 およびdocker-compose.yml 上記のコマンドを実行したときに何が起こるかを指定するファイル。

    Python(フラスコ)- Dockerfile

    FROM python:3.4
    
    ADD . /app
    WORKDIR /app
    
    RUN pip install -r requirements.txt
    

    このDockerfile Python 3.4がインストールされたベースイメージから開始し、アプリケーションを/appに追加するとします。 ディレクトリとpipを使用して、requirements.txtで指定されたアプリケーション要件をインストールします 。

    ルビー(シナトラ)

    FROM ruby:2.2
    
    ADD . /app
    WORKDIR /app
    
    RUN bundle install
    

    このDockerfile Ruby 2.2がインストールされたベースイメージから開始し、アプリケーションを/appに追加するとします。 ディレクトリとbundlerを使用して、Gemfileで指定されたアプリケーション要件をインストールします 。

    ゴラン(マティーニ)

    FROM golang:1.3
    
    ADD . /go/src/github.com/kpurdon/go-blog
    WORKDIR /go/src/github.com/kpurdon/go-blog
    
    RUN go get github.com/go-martini/martini && \
        go get github.com/martini-contrib/render && \
        go get gopkg.in/mgo.v2 && \
        go get github.com/martini-contrib/binding
    

    このDockerfile Golang 1.3がインストールされたベースイメージから開始し、アプリケーションを/go/src/github.com/kpurdon/go-blogに追加するとします。 ディレクトリを作成し、go getを使用して必要なすべての依存関係を取得します コマンド。



    アプリケーションの初期化/実行

    Python(Flask)- app.py

    # initialize application
    from flask import Flask
    app = Flask(__name__)
    
    # run application
    if __name__ == '__main__':
        app.run(host='0.0.0.0')
    
    $ python app.py
    

    Ruby(シナトラ)- app.rb

    # initialize application
    require 'sinatra'
    
    $ ruby app.rb
    

    ゴラン(マティーニ)- app.go

    // initialize application
    package main
    import "github.com/go-martini/martini"
    import "github.com/martini-contrib/render"
    
    func main() {
        app := martini.Classic()
        app.Use(render.Renderer())
    
        // run application
        app.Run()
    }
    
    $ go run app.go
    


    ルートの定義(GET / POST)

    Python(Flask)

    # get
    @app.route('/')  # the default is GET only
    def blog():
        # ...
    
    #post
    @app.route('/new', methods=['POST'])
    def new():
        # ...
    

    ルビー(シナトラ)

    # get
    get '/' do
      # ...
    end
    
    # post
    post '/new' do
      # ...
    end
    

    ゴラン(マティーニ)

    // define data struct
    type Post struct {
      Title   string `form:"title" json:"title"`
      Summary string `form:"summary" json:"summary"`
      Content string `form:"content" json:"content"`
    }
    
    // get
    app.Get("/", func(r render.Render) {
      // ...
    }
    
    // post
    import "github.com/martini-contrib/binding"
    app.Post("/new", binding.Bind(Post{}), func(r render.Render, post Post) {
      // ...
    }
    


    JSON応答をレンダリングする

    Python(Flask)

    Flaskはjsonify()メソッドを提供しますが、サービスはMongoDBを使用しているため、mongodbbsonユーティリティが使用されます。

    from bson.json_util import dumps
    return dumps(posts) # posts is a list of dicts [{}, {}]
    

    ルビー(シナトラ)

    require 'json'
    content_type :json
    posts.to_json # posts is an array (from mongodb)
    

    ゴラン(マティーニ)

    r.JSON(200, posts) // posts is an array of Post{} structs
    


    HTML応答のレンダリング(テンプレーティング)

    Python(Flask)

    return render_template('blog.html', posts=posts)
    
    <!doctype HTML>
    <html>
      <head>
        <title>Python Flask Example</title>
      </head>
      <body>
        {% for post in posts %}
          <h1> {{ post.title }} </h1>
          <h3> {{ post.summary }} </h3>
          <p> {{ post.content }} </p>
          <hr>
        {% endfor %}
      </body>
    </html>
    

    ルビー(シナトラ)

    erb :blog
    
    <!doctype HTML>
    <html>
      <head>
        <title>Ruby Sinatra Example</title>
      </head>
      <body>
        <% @posts.each do |post| %>
          <h1><%= post['title'] %></h1>
          <h3><%= post['summary'] %></h3>
          <p><%= post['content'] %></p>
          <hr>
        <% end %>
      </body>
    </html>
    

    ゴラン(マティーニ)

    r.HTML(200, "blog", posts)
    
    <!doctype HTML>
    <html>
      <head>
        <title>Golang Martini Example</title>
      </head>
      <body>
        {{range . }}
          <h1>{{.Title}}</h1>
          <h3>{{.Summary}}</h3>
          <p>{{.Content}}</p>
          <hr>
        {{ end }}
      </body>
    </html>
    


    データベース接続

    すべてのアプリケーションは、その言語に固有のmongodbドライバーを使用しています。環境変数DB_PORT_27017_TCP_ADDR リンクされたDockerコンテナのIP(データベースIP)です。

    Python(Flask)

    from pymongo import MongoClient
    client = MongoClient(os.environ['DB_PORT_27017_TCP_ADDR'], 27017)
    db = client.blog
    

    ルビー(シナトラ)

    require 'mongo'
    db_ip = [ENV['DB_PORT_27017_TCP_ADDR']]
    client = Mongo::Client.new(db_ip, database: 'blog')
    

    ゴラン(マティーニ)

    import "gopkg.in/mgo.v2"
    session, _ := mgo.Dial(os.Getenv("DB_PORT_27017_TCP_ADDR"))
    db := session.DB("blog")
    defer session.Close()
    


    POSTからデータを挿入

    Python(Flask)

    from flask import request
    post = {
        'title': request.form['title'],
        'summary': request.form['summary'],
        'content': request.form['content']
    }
    db.blog.insert_one(post)
    

    ルビー(シナトラ)

    client[:posts].insert_one(params) # params is a hash generated by sinatra
    

    ゴラン(マティーニ)

    db.C("posts").Insert(post) // post is an instance of the Post{} struct
    


    データの取得

    Python(Flask)

    posts = db.blog.find()
    

    ルビー(シナトラ)

    @posts = client[:posts].find.to_a
    

    ゴラン(マティーニ)

    var posts []Post
    db.C("posts").Find(nil).All(&posts)
    


    アプリケーションのデプロイ(Docker!)

    これらすべてのアプリケーションをデプロイするための優れたソリューションは、dockerとdocker-composeを使用することです。

    Python(Flask)

    Dockerfile

    FROM python:3.4
    
    ADD . /app
    WORKDIR /app
    
    RUN pip install -r requirements.txt
    

    docker-compose.yml

    web:
      build: .
      command: python -u app.py
      ports:
        - "5000:5000"
      volumes:
        - .:/app
      links:
        - db
    db:
      image: mongo:3.0.4
      command: mongod --quiet --logpath=/dev/null
    

    ルビー(シナトラ)

    Dockerfile

    FROM ruby:2.2
    
    ADD . /app
    WORKDIR /app
    
    RUN bundle install
    

    docker-compose.yml

    web:
      build: .
      command: bundle exec ruby app.rb
      ports:
        - "4567:4567"
      volumes:
        - .:/app
      links:
        - db
    db:
      image: mongo:3.0.4
      command: mongod --quiet --logpath=/dev/null
    

    ゴラン(マティーニ)

    Dockerfile

    FROM golang:1.3
    
    ADD . /go/src/github.com/kpurdon/go-todo
    WORKDIR /go/src/github.com/kpurdon/go-todo
    
    RUN go get github.com/go-martini/martini && go get github.com/martini-contrib/render && go get gopkg.in/mgo.v2 && go get github.com/martini-contrib/binding
    

    docker-compose.yml

    web:
      build: .
      command: go run app.go
      ports:
        - "3000:3000"
      volumes: # look into volumes v. "ADD"
        - .:/go/src/github.com/kpurdon/go-todo
      links:
        - db
    db:
      image: mongo:3.0.4
      command: mongod --quiet --logpath=/dev/null
    


    結論

    結論として、提示されたライブラリが互いに分離しているいくつかのカテゴリであると私が信じているものを見てみましょう。


    シンプルさ

    Flaskは非常に軽量で読みやすくなっていますが、Sinatraアプリは23 LOCの3つのうちで最も単純です(Flaskの46とMartiniの42と比較して)。これらの理由から、シナトラはこのカテゴリーの勝者です。ただし、Sinatraの単純さは、よりデフォルトの「魔法」(たとえば、舞台裏で行われる暗黙の作業)によるものであることに注意してください。新規ユーザーの場合、これはしばしば混乱を招く可能性があります。

    シナトラの「魔法」の具体例を次に示します。

    params # the "request.form" logic in python is done "magically" behind the scenes in Sinatra.
    

    そして同等のFlaskコード:

    from flask import request
    params = {
        'title': request.form['title'],
        'summary': request.form['summary'],
        'content': request.form['content']
    }
    

    プログラミングの初心者にとって、FlaskとSinatraは確かに簡単ですが、他の静的に型付けされた言語に時間を費やした経験豊富なプログラマーにとって、Martiniはかなり単純なインターフェースを提供します。



    ドキュメント

    Flaskのドキュメントは、検索が最も簡単で、最も親しみやすいものでした。 SinatraとMartiniはどちらも十分に文書化されていますが、文書自体はそれほど親しみやすいものではありませんでした。このため、Flaskがこのカテゴリの勝者です。



    コミュニティ

    Flaskは、このカテゴリの勝者です。 Rubyコミュニティは、基本的なサービス以外のものが必要な場合に、Railsが唯一の良い選択であることに独断的であることがよくあります(PadrinoはSinatraの上にこれを提供していますが)。 Golangコミュニティは、言語自体が非常に若いため、1つ(またはいくつか)のWebフレームワークに関するコンセンサスにはまだほど遠いです。ただし、Pythonは、すぐに使用できるフル機能のWebアプリケーション用のDjangoや、マイクロフレームワークアプローチ用のFlask、Bottle、CheryPy、Tornadoなど、Web開発への多くのアプローチを採用しています。




    最終決定

    この記事のポイントは、単一のツールを宣伝することではなく、Flask、Sinatra、およびMartiniの公平な比較を提供することであることに注意してください。そうは言っても、Flask(Python)またはSinatra(Ruby)を選択します。 CやJavaのような言語を使用している場合は、静的に型付けされたGolangの性質が魅力的かもしれません。初心者の場合は、起動と実行が非常に簡単で、デフォルトの「魔法」がほとんどないため、Flaskが最適な選択肢となる可能性があります。プロジェクトのライブラリを選択するときは、柔軟に決定することをお勧めします。

    質問?フィードバック?以下にコメントしてください。ありがとうございます!

    また、いくつかのベンチマークをご覧になりたい場合はお知らせください。



    1. インデックスとソートの列順序に関する考慮事項

    2. バージョン11のlocaldbの接続文字列は何ですか

    3. オラクルのempテーブルで上位3つの最高給与を見つける方法は?

    4. 複数の外部キーを使用してテーブルを作成し、混乱しないようにする方法