Database
 sql >> Baza danych >  >> RDS >> Database

Python, Ruby i Golang:porównanie aplikacji usług internetowych

Po ostatnim porównaniu Pythona, Ruby i Golanga dla aplikacji wiersza poleceń, zdecydowałem się użyć tego samego wzorca do porównania budowania prostej usługi internetowej. Do tego porównania wybrałem Flask (Python), Sinatra (Ruby) i Martini (Golang). Tak, istnieje wiele innych opcji bibliotek aplikacji internetowych w każdym języku, ale uważam, że te trzy dobrze nadają się do porównania.


Przeglądy bibliotek

Oto wysokopoziomowe porównanie bibliotek autorstwa Stackshare.


Kolba (Python)

Flask to mikro-framework dla Pythona oparty na Werkzeug, Jinja2 i dobrych intencjach.

W przypadku bardzo prostych aplikacji, takich jak ta pokazana w tym demo, Flask jest doskonałym wyborem. Podstawowa aplikacja Flask to tylko 7 linii kodu (LOC) w jednym pliku źródłowym Pythona. Przewaga Flaska nad innymi bibliotekami internetowymi Pythona (takimi jak Django lub Pyramid) polega na tym, że możesz zacząć od małych rzeczy i w razie potrzeby zbudować bardziej złożoną aplikację.

from flask import Flask
app = Flask(__name__)

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

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


Sinatra (Rubin)

Sinatra to DSL do szybkiego tworzenia aplikacji internetowych w Ruby przy minimalnym wysiłku.

Podobnie jak Flask, Sinatra świetnie nadaje się do prostych zastosowań. Podstawowa aplikacja Sinatra to tylko 4 LOC w jednym pliku źródłowym Ruby. Sinatra jest używana zamiast bibliotek, takich jak Ruby on Rails, z tego samego powodu, co Flask - możesz zacząć od małych i rozszerzać aplikację w razie potrzeby.

require 'sinatra'

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


Martini (Golang)

Martini to potężny pakiet do szybkiego pisania modularnych aplikacji/usług internetowych w Golang.

Martini jest dostarczany z kilkoma bateriami więcej niż Sinatra i Flask, ale nadal jest bardzo lekki na początek - tylko 9 LOC dla podstawowego zastosowania. Martini spotkał się z pewną krytyką społeczności Golang, ale nadal ma jeden z najwyżej ocenianych projektów Github spośród wszystkich frameworków internetowych Golang. Autor Martini odpowiedział tutaj bezpośrednio na krytykę. Niektóre inne frameworki obejmują Revel, Gin, a nawet wbudowaną bibliotekę net/http.

package main

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

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

Pozbywając się podstaw, zbudujmy aplikację!




Opis usługi

Stworzony serwis udostępnia bardzo podstawową aplikację blogową. Powstają następujące trasy:

  • GET / :Zwróć bloga (używając szablonu do renderowania).
  • GET /json :Zwróć zawartość bloga w formacie JSON.
  • POST /new :Dodaj nowy post (tytuł, streszczenie, treść) do bloga.

Zewnętrzny interfejs do usługi blogów jest dokładnie taki sam dla każdego języka. Dla uproszczenia MongoDB będzie używany jako magazyn danych dla tego przykładu, ponieważ jest najprostszy w konfiguracji i nie musimy się w ogóle martwić o schematy. W normalnej aplikacji podobnej do bloga prawdopodobnie potrzebna byłaby relacyjna baza danych.


Dodaj post

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


Wyświetl HTML

GET /



Wyświetl plik JSON

GET /json

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



Struktura aplikacji

Każdą aplikację można podzielić na następujące komponenty:


Konfiguracja aplikacji

  • Zainicjuj aplikację
  • Uruchom aplikację


Prośba

  • Zdefiniuj trasy, na których użytkownik może żądać danych (GET)
  • Zdefiniuj trasy, na których użytkownik może przesyłać dane (POST)


Odpowiedź

  • Renderuj JSON (GET /json )
  • Wyrenderuj szablon (GET / )


Baza danych

  • Zainicjuj połączenie
  • Wstaw dane
  • Pobierz dane


Wdrażanie aplikacji

  • Doker!

Pozostała część tego artykułu porówna każdy z tych składników dla każdej biblioteki. Celem nie jest sugerowanie, że jedna z tych bibliotek jest lepsza od drugiej – ma to na celu zapewnienie konkretnego porównania między trzema narzędziami:

  • Płytka (Python)
  • Sinatra (Rubin)
  • Martini (Golang)



Konfiguracja projektu

Wszystkie projekty są ładowane za pomocą docker i docker-compose. Zanim zagłębimy się w to, jak każda aplikacja jest ładowana pod maską, możemy po prostu użyć dockera, aby uruchomić każdą z nich w dokładnie ten sam sposób - docker-compose up

Poważnie, to wszystko! Teraz dla każdej aplikacji istnieje Dockerfile i docker-compose.yml plik, który określa, co się stanie po uruchomieniu powyższego polecenia.

Python (kolba) – Plik Dockera

FROM python:3.4

ADD . /app
WORKDIR /app

RUN pip install -r requirements.txt

Ten Dockerfile mówi, że zaczynamy od podstawowego obrazu z zainstalowanym Pythonem 3.4, dodając naszą aplikację do /app katalogu i za pomocą pip zainstalować nasze wymagania aplikacji określone w requirements.txt .

Rubin (Sinatra)

FROM ruby:2.2

ADD . /app
WORKDIR /app

RUN bundle install

Ten Dockerfile mówi, że zaczynamy od podstawowego obrazu z zainstalowanym Ruby 2.2, dodając naszą aplikację do /app i za pomocą bundlera zainstalować nasze wymagania aplikacji określone w pliku 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

Ten Dockerfile mówi, że zaczynamy od podstawowego obrazu z zainstalowanym Golangiem 1.3, dodając naszą aplikację do /go/src/github.com/kpurdon/go-blog katalogu i pobranie wszystkich niezbędnych zależności za pomocą go get polecenie.



Zainicjuj/uruchom aplikację

Python (kolba) – 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


Zdefiniuj trasę (GET/POST)

Python (kolba)

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

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

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


Wyrenderuj odpowiedź JSON

Python (kolba)

Flask udostępnia metodę jsonify(), ale ponieważ usługa korzysta z MongoDB, używane jest narzędzie mongodb bson.

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

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


Wyrenderuj odpowiedź HTML (szablon)

Python (kolba)

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>

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


Połączenie z bazą danych

Wszystkie aplikacje używają sterownika mongodb specyficznego dla danego języka. Zmienna środowiskowa DB_PORT_27017_TCP_ADDR to adres IP połączonego kontenera dockera (adres IP bazy danych).

Python (kolba)

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

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


Wstaw dane z POST

Python (kolba)

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

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


Pobierz dane

Python (kolba)

posts = db.blog.find()

Rubin (Sinatra)

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

Golang (Martini)

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


Wdrażanie aplikacji (Docker!)

Świetnym rozwiązaniem do wdrażania wszystkich tych aplikacji jest użycie docker i docker-compose.

Python (kolba)

Plik Dockera

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

Rubin (Sinatra)

Plik Dockera

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)

Plik Dockera

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


Wniosek

Na zakończenie przyjrzyjmy się, jak sądzę, kilku kategoriom, w których prezentowane biblioteki oddzielają się od siebie.


Prostota

Chociaż Flask jest bardzo lekki i wyraźnie się czyta, aplikacja Sinatra jest najprostszą z trzech w 23 LOC (w porównaniu z 46 dla Flask i 42 dla Martini). Z tych powodów Sinatra jest zwycięzcą w tej kategorii. Należy jednak zauważyć, że prostota Sinatry wynika z bardziej domyślnej „magii” – np. niejawnej pracy, która dzieje się za kulisami. Dla nowych użytkowników może to często prowadzić do zamieszania.

Oto konkretny przykład „magii” w Sinatrze:

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

I równoważny kod Flask:

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

Dla początkujących programistów Flask i Sinatra są z pewnością prostsze, ale dla doświadczonego programisty z czasem spędzonym w innych statycznie typowanych językach Martini zapewnia dość uproszczony interfejs.



Dokumentacja

Dokumentacja Flaska była najłatwiejsza do przeszukania i najbardziej przystępna. Chociaż Sinatra i Martini są dobrze udokumentowane, sama dokumentacja nie była tak przystępna. Z tego powodu Flask jest zwycięzcą w tej kategorii.



Społeczność

Flask jest zwycięzcą w tej kategorii. Społeczność Ruby jest najczęściej dogmatyczna, że ​​Railsy są jedynym dobrym wyborem, jeśli potrzebujesz czegoś więcej niż podstawowej usługi (mimo że Padrino oferuje to oprócz Sinatry). Społeczność Golang wciąż nie jest w pobliżu konsensusu co do jednego (lub nawet kilku) frameworków internetowych, czego można się spodziewać, ponieważ sam język jest tak młody. Python jednak przyjął wiele podejść do tworzenia stron internetowych, w tym Django dla gotowych, w pełni funkcjonalnych aplikacji internetowych oraz Flask, Bottle, CheryPy i Tornado dla podejścia mikro-frameworkowego.




Ostateczne określenie

Zauważ, że celem tego artykułu nie było promowanie jednego narzędzia, a raczej zapewnienie obiektywnego porównania Flask, Sinatry i Martini. Powiedziawszy to, wybrałbym Flask (Python) lub Sinatra (Ruby). Jeśli pochodzisz z języka takiego jak C lub Java, być może statycznie wpisana natura Golanga może ci się spodobać. Jeśli jesteś początkującym, Flask może być najlepszym wyborem, ponieważ jest bardzo łatwy do uruchomienia i ma bardzo mało domyślnej „magii”. Zalecam, abyś był elastyczny w swoich decyzjach przy wyborze biblioteki do swojego projektu.

Pytania? Informacja zwrotna? Proszę o komentarz poniżej. Dziękuję!

Daj nam też znać, jeśli chciałbyś zobaczyć niektóre testy porównawcze.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Wewnętrzne elementy Z SZYFROWANIEM

  2. Podobieństwa i różnice między funkcjami RANK, DENSE_RANK i ROW_NUMBER

  3. Samouczek DBMS:Kompletny kurs Crash na DBMS

  4. AKTUALIZACJA SQL:Dowiedz się, jak aktualizować wartości w tabeli

  5. Jak dodać kolumnę w SQL