sql >> Databáze >  >> RDS >> Database

Python, Ruby a Golang:Srovnání aplikací webových služeb

Po nedávném srovnání Pythonu, Ruby a Golangu pro aplikaci příkazového řádku jsem se rozhodl použít stejný vzor k porovnání vytváření jednoduché webové služby. Pro toto srovnání jsem vybral Flask (Python), Sinatra (Ruby) a Martini (Golang). Ano, existuje mnoho dalších možností pro knihovny webových aplikací v každém jazyce, ale cítil jsem, že tyto tři se hodí ke srovnání.


Přehledy knihoven

Zde je srovnání knihoven na vysoké úrovni od Stackshare.


Baňka (Python)

Flask je mikrorámec pro Python založený na Werkzeug, Jinja2 a dobrých úmyslech.

Pro velmi jednoduché aplikace, jako je ta ukázaná v této ukázce, je Flask skvělou volbou. Základní aplikace Flask má pouze 7 řádků kódu (LOC) v jediném zdrojovém souboru Pythonu. Výhodou Flasku oproti jiným webovým knihovnám Pythonu (jako je Django nebo Pyramid) je to, že můžete začít v malém a podle potřeby vytvořit složitější aplikaci.

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
    app.run()


Sinatra (Ruby)

Sinatra je DSL pro rychlé vytváření webových aplikací v Ruby s minimálním úsilím.

Stejně jako Flask je i Sinatra skvělá pro jednoduché aplikace. Základní aplikace Sinatra má pouze 4 LOC v jediném zdrojovém souboru Ruby. Sinatra se používá místo knihoven, jako je Ruby on Rails, ze stejného důvodu jako Flask – můžete začít v malém a aplikaci rozšířit podle potřeby.

require 'sinatra'

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


Martini (Golang)

Martini je výkonný balíček pro rychlé psaní modulárních webových aplikací/služeb v Golangu.

Martini je dodáváno s několika bateriemi více než Sinatra a Flask, ale je stále velmi lehké na začátek - pouze 9 LOC pro základní aplikaci. Martini se stal terčem kritiky ze strany komunity Golang, ale stále má jeden z nejlépe hodnocených projektů Github ze všech webových rámců Golang. Na kritiku zde reagoval přímo autor Martini. Některé další rámce zahrnují Revel, Gin a dokonce i vestavěnou knihovnu net/http.

package main

import "github.com/go-martini/martini"

func main() {
  m := martini.Classic()
  m.Get("/", func() string {
    return "Hello world!"
  })
  m.Run()
}

Se základy z cesty, pojďme vytvořit aplikaci!




Popis služby

Vytvořená služba poskytuje velmi základní blogovou aplikaci. Jsou vytvořeny následující trasy:

  • GET / :Vraťte blog (k vykreslení použijte šablonu).
  • GET /json :Vraťte obsah blogu ve formátu JSON.
  • POST /new :Přidejte na blog nový příspěvek (název, shrnutí, obsah).

Externí rozhraní k blogovací službě je pro každý jazyk naprosto stejné. Pro jednoduchost bude pro tento příklad použit jako úložiště dat MongoDB, protože je nejjednodušší na nastavení a nemusíme se vůbec starat o schémata. V normální „blogové“ aplikaci by pravděpodobně byla nezbytná relační databáze.


Přidat příspěvek

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


Zobrazit HTML

GET /



Zobrazit soubor JSON

GET /json

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



Struktura aplikace

Každou aplikaci lze rozdělit na následující součásti:


Nastavení aplikace

  • Inicializujte aplikaci
  • Spusťte aplikaci


Požadavek

  • Definujte trasy, na kterých může uživatel požadovat data (GET)
  • Definujte trasy, na kterých může uživatel odesílat data (POST)


Odpověď

  • Vykreslit JSON (GET /json )
  • Vykreslení šablony (GET / )


Databáze

  • Inicializovat připojení
  • Vložte data
  • Načíst data


Nasazení aplikace

  • Docker!

Zbytek tohoto článku porovná každou z těchto komponent pro každou knihovnu. Účelem není naznačovat, že jedna z těchto knihoven je lepší než druhá – jde o poskytnutí konkrétního srovnání mezi těmito třemi nástroji:

  • Baňka (Python)
  • Sinatra (Ruby)
  • Martini (Golang)



Nastavení projektu

Všechny projekty jsou bootstrapped pomocí docker a docker-compose. Než se ponoříme do toho, jak je každá aplikace zaváděna pod kapotou, stačí použít docker, aby se každá zprovoznila přesně stejným způsobem - docker-compose up

Vážně, to je ono! Nyní pro každou aplikaci existuje Dockerfile a docker-compose.yml soubor, který určuje, co se stane, když spustíte výše uvedený příkaz.

Python (flask) – Dockerfile

FROM python:3.4

ADD . /app
WORKDIR /app

RUN pip install -r requirements.txt

Tento Dockerfile říká, že začínáme od základního obrazu s nainstalovaným Pythonem 3.4 a přidáváme naši aplikaci do /app adresář a pomocí pip nainstalujte naše požadavky na aplikaci specifikované v requirements.txt .

Ruby (sinatra)

FROM ruby:2.2

ADD . /app
WORKDIR /app

RUN bundle install

Tento Dockerfile říká, že začínáme od základního obrazu s nainstalovaným Ruby 2.2 a přidáváme naši aplikaci do /app adresář a pomocí bundleru nainstalujte naše požadavky na aplikaci uvedené v Gemfile .

Golang (martini)

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

Tento Dockerfile říká, že začínáme od základního obrazu s nainstalovaným Golangem 1.3 a přidáváme naši aplikaci na /go/src/github.com/kpurdon/go-blog adresář a získání všech našich nezbytných závislostí pomocí go get příkaz.



Inicializovat/spustit aplikaci

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 (Sinatra) – app.rb

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

Golang (Martini) – 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


Definovat trasu (GET/POST)

Python (Flask)

# get
@app.route('/')  # the default is GET only
def blog():
    # ...

#post
@app.route('/new', methods=['POST'])
def new():
    # ...

Ruby (Sinatra)

# get
get '/' do
  # ...
end

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

Golang (Martini)

// 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) {
  // ...
}


Vykreslení odpovědi JSON

Python (Flask)

Flask poskytuje metodu jsonify(), ale protože služba používá MongoDB, používá se nástroj mongodb bson.

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

Ruby (Sinatra)

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

Golang (Martini)

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


Vykreslení HTML odpovědi (šablona)

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>

Ruby (Sinatra)

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>

Golang (Martini)

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>


Připojení k databázi

Všechny aplikace používají ovladač mongodb specifický pro daný jazyk. Proměnná prostředí DB_PORT_27017_TCP_ADDR je IP propojeného kontejneru dockeru (ip databáze).

Python (Flask)

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

Ruby (Sinatra)

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

Golang (Martini)

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


Vložit data z 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)

Ruby (Sinatra)

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

Golang (Martini)

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


Načíst data

Python (Flask)

posts = db.blog.find()

Ruby (Sinatra)

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

Golang (Martini)

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


Nasazení aplikace (Docker!)

Skvělým řešením pro nasazení všech těchto aplikací je použití dockeru a 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

Ruby (Sinatra)

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

Golang (Martini)

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


Závěr

Na závěr se pojďme podívat na to, o čem se domnívám, že je to několik kategorií, kde se prezentované knihovny od sebe oddělují.


Jednoduchost

Zatímco Flask je velmi lehký a čte se jasně, aplikace Sinatra je nejjednodušší ze tří na 23 LOC (ve srovnání s 46 pro Flask a 42 pro Martini). Z těchto důvodů je Sinatra vítězem v této kategorii. Je však třeba poznamenat, že Sinatrova jednoduchost je způsobena výchozím „kouzlem“ – například implicitní prací, která se odehrává v zákulisí. Pro nové uživatele to může často vést ke zmatku.

Zde je konkrétní příklad „magie“ v Sinatrovi:

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

A ekvivalentní kód baňky:

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

Pro začátečníky v programování jsou Flask a Sinatra jistě jednodušší, ale pro zkušeného programátora, který tráví čas v jiných staticky typovaných jazycích, Martini poskytuje poměrně zjednodušující rozhraní.



Dokumentace

Dokumentace k baňce byla nejjednodušší na vyhledávání a nejpřístupnější. Zatímco Sinatra a Martini jsou oba dobře zdokumentované, samotná dokumentace nebyla tak přístupná. Z tohoto důvodu je Flask vítězem v této kategorii.



Komunita

Flask je vítězem této kategorie. Komunita Ruby je často dogmatická o tom, že Rails je jedinou dobrou volbou, pokud potřebujete něco víc než základní službu (i když Padrino to nabízí nad Sinatrou). Komunita Golangu se stále ani zdaleka neblíží shodě ohledně jednoho (nebo dokonce několika) webových rámců, což lze očekávat, protože jazyk samotný je tak mladý. Python však přijal řadu přístupů k vývoji webu, včetně Django pro předpřipravené plnohodnotné webové aplikace a Flask, Bottle, CheryPy a Tornado pro mikrorámcový přístup.




Konečné určení

Všimněte si, že smyslem tohoto článku nebylo propagovat jediný nástroj, ale poskytnout nezaujaté srovnání Flask, Sinatra a Martini. Vzhledem k tomu bych si vybral Flask (Python) nebo Sinatra (Ruby). Pokud pocházíte z jazyka jako C nebo Java, možná vás osloví staticky typická povaha Golangu. Pokud jste začátečník, může být Flask tou nejlepší volbou, protože je velmi snadné jej uvést do provozu a je zde velmi málo výchozího „kouzla“. Moje doporučení je, abyste byli flexibilní ve svých rozhodnutích při výběru knihovny pro váš projekt.

otázky? Zpětná vazba? Prosím o komentář níže. Děkuji!

Dejte nám také vědět, pokud byste měli zájem o nějaké srovnávací hodnoty.



  1. Jaký je nejelegantnější způsob, jak uložit časové razítko s nanosec v postgresql?

  2. Automatické upgrady PostgreSQL clusterů v cloudu s téměř nulovým výpadkem (část II)

  3. Failover &Failback pro PostgreSQL v Microsoft Azure

  4. psql - uložit výsledky příkazu do souboru