sql >> Database teknologi >  >> RDS >> Database

Python, Ruby og Golang:A Web Service Application Comparison

Efter en nylig sammenligning af Python, Ruby og Golang for en kommandolinjeapplikation besluttede jeg at bruge det samme mønster til at sammenligne opbygning af en simpel webtjeneste. Jeg har valgt Flask (Python), Sinatra (Ruby) og Martini (Golang) til denne sammenligning. Ja, der er mange andre muligheder for webapplikationsbiblioteker på hvert sprog, men jeg følte, at disse tre egner sig godt til sammenligning.


Bibliotek oversigter

Her er en sammenligning på højt niveau af bibliotekerne fra Stackshare.


Flaske (Python)

Flask er en mikroramme for Python baseret på Werkzeug, Jinja2 og gode intentioner.

Til meget simple applikationer, såsom den, der er vist i denne demo, er Flask et godt valg. Den grundlæggende Flask-applikation er kun 7 linjer kode (LOC) i en enkelt Python-kildefil. Fordelen med Flask i forhold til andre Python-webbiblioteker (såsom Django eller Pyramid) er, at du kan starte i det små og bygge op til en mere kompleks 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 er en DSL til hurtigt at skabe webapplikationer i Ruby med minimal indsats.

Ligesom Flask er Sinatra fantastisk til simple applikationer. Den grundlæggende Sinatra-applikation er kun 4 LOC i en enkelt Ruby-kildefil. Sinatra bruges i stedet for biblioteker såsom Ruby on Rails af samme grund som Flask - du kan starte i det små og udvide applikationen efter behov.

require 'sinatra'

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


Martini (Golang)

Martini er en kraftfuld pakke til hurtigt at skrive modulære webapplikationer/tjenester i Golang.

Martini kommer med et par flere batterier inkluderet end både Sinatra og Flask, men er stadig meget let til at starte med - kun 9 LOC til den grundlæggende applikation. Martini er blevet kritiseret af Golang-samfundet, men har stadig et af de højest bedømte Github-projekter af enhver Golang-webramme. Forfatteren af ​​Martini reagerede direkte på kritikken her. Nogle andre rammer inkluderer Revel, Gin og endda det indbyggede net/http-bibliotek.

package main

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

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

Lad os bygge en app med det grundlæggende af vejen!




Tjenestebeskrivelse

Den oprettede tjeneste giver en meget grundlæggende blogapplikation. Følgende ruter er konstrueret:

  • GET / :Returner bloggen (ved hjælp af en skabelon til at gengive).
  • GET /json :Returner blogindholdet i JSON-format.
  • POST /new :Tilføj et nyt indlæg (titel, resumé, indhold) til bloggen.

Den eksterne grænseflade til blogtjenesten er nøjagtig den samme for hvert sprog. For nemheds skyld vil MongoDB blive brugt som datalager for dette eksempel, da det er det enkleste at konfigurere, og vi behøver slet ikke bekymre os om skemaer. I en normal "blog-lignende" applikation vil en relationsdatabase sandsynligvis være nødvendig.


Tilføj et indlæg

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


Se HTML

GET /



Se JSON

GET /json

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



Applikationsstruktur

Hver applikation kan opdeles i følgende komponenter:


Opsætning af applikation

  • Initialiser en applikation
  • Kør applikationen


Anmodning

  • Definer ruter, som en bruger kan anmode om data på (GET)
  • Definer ruter, som en bruger kan indsende data på (POST)


Svar

  • Gengiv JSON (GET /json )
  • Gengiv en skabelon (GET / )


Database

  • Initialiser en forbindelse
  • Indsæt data
  • Hent data


Applikationsimplementering

  • Docker!

Resten af ​​denne artikel vil sammenligne hver af disse komponenter for hvert bibliotek. Formålet er ikke at antyde, at det ene af disse biblioteker er bedre end det andet - det er at give en specifik sammenligning mellem de tre værktøjer:

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



Projektopsætning

Alle projekter er bootstrapped ved hjælp af docker og docker-compose. Inden vi dykker ned i, hvordan hver applikation er bootstrapped under emhætten, kan vi bare bruge docker til at få hver enkelt op at køre på nøjagtig samme måde - docker-compose up

Seriøst, det er det! For hver applikation er der nu en Dockerfile og en docker-compose.yml fil, der angiver, hvad der sker, når du kører ovenstående kommando.

Python (kolbe) - Dockerfile

FROM python:3.4

ADD . /app
WORKDIR /app

RUN pip install -r requirements.txt

Denne Dockerfile siger, at vi starter fra et basisbillede med Python 3.4 installeret, og tilføjer vores applikation til /app bibliotek og bruge pip til at installere vores applikationskrav specificeret i requirements.txt .

Ruby (sinatra)

FROM ruby:2.2

ADD . /app
WORKDIR /app

RUN bundle install

Denne Dockerfile siger, at vi starter fra et basisbillede med Ruby 2.2 installeret, og tilføjer vores applikation til /app bibliotek og bruge bundler til at installere vores applikationskrav specificeret 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

Denne Dockerfile siger, at vi starter fra et basisbillede med Golang 1.3 installeret, og tilføjer vores applikation til /go/src/github.com/kpurdon/go-blog bibliotek og få alle vores nødvendige afhængigheder ved hjælp af go get kommando.



Initialiser/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


Definer en rute (GET/POST)

Python (kolbe)

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


Gengiv et JSON-svar

Python (kolbe)

Flask giver en jsonify()-metode, men da tjenesten bruger MongoDB, bruges mongodb bson-værktøjet.

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


Gengiv et HTML-svar (skabelon)

Python (kolbe)

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>


Databaseforbindelse

Alle applikationerne bruger den mongodb-driver, der er specifik for sproget. Miljøvariablen DB_PORT_27017_TCP_ADDR er IP-adressen for en linket docker-container (databasens ip).

Python (kolbe)

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


Indsæt data fra en POST

Python (kolbe)

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


Hent data

Python (kolbe)

posts = db.blog.find()

Ruby (Sinatra)

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

Golang (Martini)

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


Applikationsimplementering (Docker!)

En god løsning til at implementere alle disse applikationer er at bruge docker og docker-compose.

Python (kolbe)

Dockerfil

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)

Dockerfil

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)

Dockerfil

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


Konklusion

Lad os afslutningsvis tage et kig på, hvad jeg mener er nogle få kategorier, hvor de præsenterede biblioteker adskiller sig fra hinanden.


Enkelhed

Mens Flask er meget let og læser klart, er Sinatra-appen den enkleste af de tre ved 23 LOC (sammenlignet med 46 for Flask og 42 for Martini). Af disse grunde er Sinatra vinderen i denne kategori. Det skal dog bemærkes, at Sinatras enkelhed skyldes mere standard "magi" - f.eks. implicit arbejde, der sker bag kulisserne. For nye brugere kan dette ofte føre til forvirring.

Her er et specifikt eksempel på "magi" i Sinatra:

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

Og den tilsvarende Flask-kode:

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

For begyndere at programmere er Flask og Sinatra bestemt enklere, men for en erfaren programmør med tid brugt på andre statisk indtastede sprog giver Martini en ret forenklet grænseflade.



Dokumentation

Flask-dokumentationen var den nemmeste at søge og mest tilgængelig. Mens Sinatra og Martini begge er veldokumenterede, var selve dokumentationen ikke så tilgængelig. Af denne grund er Flask vinderen i denne kategori.



Fællesskab

Flask er vinderen uden tvivl i denne kategori. Ruby-samfundet er oftere dogmatisk over for Rails som det eneste gode valg, hvis du har brug for mere end en grundlæggende service (selvom Padrino tilbyder dette oven i Sinatra). Golang-fællesskabet er stadig ikke i nærheden af ​​en konsensus om en (eller endda nogle få) web-frameworks, hvilket kan forventes, da sproget i sig selv er så ungt. Python har imidlertid omfavnet en række tilgange til webudvikling, herunder Django til out-of-the-box fuldfunktionelle webapplikationer og Flask, Bottle, CheryPy og Tornado til en mikrorammetilgang.




Endelig afgørelse

Bemærk, at pointen med denne artikel ikke var at promovere et enkelt værktøj, snarere at give en upartisk sammenligning af Flask, Sinatra og Martini. Når det er sagt, ville jeg vælge Flask (Python) eller Sinatra (Ruby). Hvis du kommer fra et sprog som C eller Java, kan den statisk-typede natur af Golang måske appellere til dig. Hvis du er nybegynder, kan Flask være det bedste valg, da det er meget nemt at komme i gang, og der er meget lidt standard "magi". Min anbefaling er, at du er fleksibel i dine beslutninger, når du vælger et bibliotek til dit projekt.

Spørgsmål? Feedback? Kommenter venligst nedenfor. Tak!

Fortæl os også, hvis du er interesseret i at se nogle benchmarks.



  1. Afvejninger i Hot Standby-implementeringer

  2. SQL ORDER BY klausul for begyndere

  3. Håndtering af primære nøglekonflikter ved indsættelse af data i SQLite

  4. MySQL:flere tabeller eller en tabel med mange kolonner?