sql >> Databasteknik >  >> RDS >> Database

Python, Ruby och Golang:A Web Service Application Comparison

Efter en nyligen jämförelse av Python, Ruby och Golang för en kommandoradsapplikation bestämde jag mig för att använda samma mönster för att jämföra att bygga en enkel webbtjänst. Jag har valt Flask (Python), Sinatra (Ruby) och Martini (Golang) för denna jämförelse. Ja, det finns många andra alternativ för webbapplikationsbibliotek på varje språk, men jag tyckte att dessa tre lämpar sig bra för jämförelse.


Bibliotekets översikter

Här är en jämförelse på hög nivå av biblioteken av Stackshare.


Kolv (Python)

Flask är ett mikroramverk för Python baserat på Werkzeug, Jinja2 och goda avsikter.

För mycket enkla applikationer, som den som visas i denna demo, är Flask ett utmärkt val. Den grundläggande Flask-applikationen är endast 7 rader kod (LOC) i en enda Python-källfil. Draget av Flask framför andra Python-webbbibliotek (som Django eller Pyramid) är att du kan börja i det små och bygga upp till en mer komplex applikation efter behov.

from flask import Flask
app = Flask(__name__)

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

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


Sinatra (Ruby)

Sinatra är en DSL för att snabbt skapa webbapplikationer i Ruby med minimal ansträngning.

Precis som Flask är Sinatra bra för enkla applikationer. Den grundläggande Sinatra-applikationen är bara 4 LOC i en enda Ruby-källfil. Sinatra används istället för bibliotek som Ruby on Rails av samma anledning som Flask - du kan börja smått och utöka applikationen efter behov.

require 'sinatra'

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


Martini (Golang)

Martini är ett kraftfullt paket för att snabbt skriva modulära webbapplikationer/tjänster i Golang.

Martini kommer med några fler batterier inkluderade än både Sinatra och Flask men är fortfarande väldigt lätt till att börja med - endast 9 LOC för grundapplikationen. Martini har fått en del kritik av Golang-communityt men har fortfarande ett av de högst rankade Github-projekten i något Golang-webbramverk. Författaren till Martini svarade direkt på kritiken här. Några andra ramverk inkluderar Revel, Gin och till och med det inbyggda net/http-biblioteket.

package main

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

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

Med grunderna ur vägen, låt oss bygga en app!




Tjänstebeskrivning

Tjänsten som skapas ger en mycket grundläggande bloggapplikation. Följande rutter är konstruerade:

  • GET / :Returnera bloggen (med en mall för att rendera).
  • GET /json :Returnera blogginnehållet i JSON-format.
  • POST /new :Lägg till ett nytt inlägg (titel, sammanfattning, innehåll) i bloggen.

Det externa gränssnittet till bloggtjänsten är exakt detsamma för varje språk. För enkelhetens skull kommer MongoDB att användas som datalager för detta exempel eftersom det är det enklaste att ställa in och vi behöver inte oroa oss för scheman alls. I en normal "bloggliknande" applikation skulle troligen en relationsdatabas vara nödvändig.


Lägg till ett inlägg

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


Visa HTML

GET /



Visa JSON

GET /json

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



Applikationsstruktur

Varje applikation kan delas upp i följande komponenter:


Programinställningar

  • Initiera ett program
  • Kör programmet


Begäran

  • Definiera rutter på vilka en användare kan begära data (GET)
  • Definiera rutter på vilka en användare kan skicka data (POST)


Svar

  • Gör JSON (GET /json )
  • Gör en mall (GET / )


Databas

  • Initiera en anslutning
  • Infoga data
  • Hämta data


Applikationsdistribution

  • Docker!

Resten av den här artikeln kommer att jämföra var och en av dessa komponenter för varje bibliotek. Syftet är inte att antyda att ett av dessa bibliotek är bättre än det andra - det är att ge en specifik jämförelse mellan de tre verktygen:

  • Kolv (Python)
  • Sinatra (Ruby)
  • Martini (Golang)



Projektinställning

Alla projekt är bootstrappade med docker och docker-compose. Innan vi dyker in i hur varje applikation är stängd under huven kan vi bara använda docker för att få igång var och en på exakt samma sätt - docker-compose up

Seriöst, det är det! Nu finns det en Dockerfile för varje applikation och en docker-compose.yml fil som anger vad som händer när du kör kommandot ovan.

Python (flaska) - Dockerfile

FROM python:3.4

ADD . /app
WORKDIR /app

RUN pip install -r requirements.txt

Denna Dockerfile säger att vi utgår från en basavbildning med Python 3.4 installerat och lägger till vår applikation i /app katalog och använda pip för att installera våra applikationskrav specificerade i requirements.txt .

Ruby (sinatra)

FROM ruby:2.2

ADD . /app
WORKDIR /app

RUN bundle install

Denna Dockerfile säger att vi utgår från en basavbildning med Ruby 2.2 installerad och lägger till vår applikation i /app katalog och använda bundler för att installera våra applikationskrav specificerade i 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

Denna Dockerfile säger att vi utgår från en basavbildning med Golang 1.3 installerat och lägger till vår applikation i /go/src/github.com/kpurdon/go-blog katalogen och få alla våra nödvändiga beroenden med hjälp av go get kommando.



Initiera/kör en applikation

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


Definiera en rutt (GET/POST)

Python (kolv)

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


Gör ett JSON-svar

Python (kolv)

Flask tillhandahåller en jsonify()-metod men eftersom tjänsten använder MongoDB används mongodb bson-verktyget.

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


Gör ett HTML-svar (mall)

Python (kolv)

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>


Databasanslutning

Alla applikationer använder mongodb-drivrutinen som är specifik för språket. Miljövariabeln DB_PORT_27017_TCP_ADDR är IP-adressen för en länkad dockningsbehållare (databasens ip).

Python (kolv)

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()


Infoga data från en POST

Python (kolv)

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


Hämta data

Python (kolv)

posts = db.blog.find()

Ruby (Sinatra)

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

Golang (Martini)

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


Application Deployment (Docker!)

En bra lösning för att distribuera alla dessa applikationer är att använda docker och docker-compose.

Python (kolv)

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


Slutsats

Låt oss avslutningsvis ta en titt på vad jag tror är några kategorier där de presenterade biblioteken skiljer sig från varandra.


Enkelhet

Medan Flask är väldigt lätt och läser tydligt, är Sinatra-appen den enklaste av de tre vid 23 LOC (jämfört med 46 för Flask och 42 för Martini). Av dessa skäl är Sinatra vinnaren i denna kategori. Det bör dock noteras att Sinatras enkelhet beror på mer standard "magi" - t.ex. implicit arbete som sker bakom kulisserna. För nya användare kan detta ofta leda till förvirring.

Här är ett specifikt exempel på "magi" i Sinatra:

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

Och motsvarande kolvkod:

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

För nybörjare är det verkligen enklare att programmera Flask och Sinatra, men för en erfaren programmerare som spenderar tid på andra statiskt skrivna språk ger Martini ett ganska förenklat gränssnitt.



Dokumentation

Flask-dokumentationen var den enklaste att söka och mest lättillgänglig. Även om Sinatra och Martini båda är väldokumenterade, var själva dokumentationen inte lika lättillgänglig. Av denna anledning är Flask vinnaren i denna kategori.



Community

Flask är vinnaren i den här kategorin. Ruby-gemenskapen är oftare dogmatisk om att Rails är det enda bra valet om du behöver något mer än en grundläggande tjänst (även om Padrino erbjuder detta ovanpå Sinatra). Golang-gemenskapen är fortfarande inte i närheten av en konsensus om ett (eller till och med några) webbramverk, vilket kan förväntas eftersom språket i sig är så ungt. Python har dock anammat ett antal tillvägagångssätt för webbutveckling, inklusive Django för färdiga webbapplikationer med alla funktioner och Flask, Bottle, CheryPy och Tornado för ett mikroramverk.




Slutlig avgörande

Observera att poängen med den här artikeln inte var att marknadsföra ett enda verktyg, snarare att tillhandahålla en opartisk jämförelse av Flask, Sinatra och Martini. Med det sagt skulle jag välja Flask (Python) eller Sinatra (Ruby). Om du kommer från ett språk som C eller Java kanske den statiskt skrivna naturen hos Golang tilltalar dig. Om du är nybörjare kan Flask vara det bästa valet eftersom det är väldigt lätt att komma igång och det finns väldigt lite standard "magi". Min rekommendation är att du är flexibel i dina beslut när du väljer bibliotek för ditt projekt.

Frågor? Respons? Kommentera gärna nedan. Tack!

Meddela oss också om du är intresserad av att se några riktmärken.



  1. Hur får man antalet rader i MySQL-tabellen med PHP?

  2. Ändra kolumntyp och ange inte null

  3. PostgreSQL:Skiftlägesokänslig strängjämförelse

  4. Hur kör jag PHP som är lagrad i en MySQL-databas?