Database
 sql >> база данни >  >> RDS >> Database

Python, Ruby и Golang:Сравнение на приложения за уеб услуги

След скорошно сравнение на Python, Ruby и Golang за приложение от команден ред реших да използвам същия модел, за да сравня изграждането на проста уеб услуга. Избрах Flask (Python), Sinatra (Ruby) и Martini (Golang) за това сравнение. Да, има много други опции за библиотеки на уеб приложения на всеки език, но смятам, че тези три са подходящи за сравнение.


Прегледи на библиотеката

Ето сравнение на високо ниво на библиотеките от Stackshare.


Flask (Python)

Flask е микро-рамка за Python, базирана на Werkzeug, Jinja2 и добри намерения.

За много прости приложения, като това, показано в тази демонстрация, Flask е чудесен избор. Основното приложение на Flask е само 7 реда код (LOC) в един изходен файл на Python. Привличането на Flask пред други уеб библиотеки на Python (като Django или Pyramid) е, че можете да започнете с малко и да надградите до по-сложно приложение, ако е необходимо.

from flask import Flask
app = Flask(__name__)

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

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


Синатра (Рубин)

Sinatra е DSL за бързо създаване на уеб приложения в Ruby с минимални усилия.

Точно като Flask, Sinatra е чудесен за прости приложения. Основното приложение на Sinatra е само 4 LOC в един изходен файл на Ruby. Sinatra се използва вместо библиотеки като Ruby on Rails по същата причина като Flask – можете да започнете с малко и да разширите приложението според нуждите.

require 'sinatra'

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


Мартини (Голанг)

Martini е мощен пакет за бързо писане на модулни уеб приложения/услуги на Golang.

Martini се предлага с няколко батерии повече, отколкото както Sinatra, така и Flask, но все още е много лек за начало - само 9 LOC за основното приложение. Martini е подложен на известна критика от общността на Golang, но все още има един от най-високо оценените проекти на Github от всяка уеб рамка на Golang. Авторът на Martini отговори директно на критиките тук. Някои други рамки включват 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!

В останалата част от тази статия ще се сравни всеки от тези компоненти за всяка библиотека. Целта не е да се подскаже, че една от тези библиотеки е по-добра от другата – тя е да предостави конкретно сравнение между трите инструмента:

  • Flask (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

Руби (Синатра) - 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, се използва помощната програма mongodb bson.

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 е 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 (в сравнение с 46 за Flask и 42 за Martini). Поради тези причини Синатра е победител в тази категория. Трябва да се отбележи обаче, че простотата на Синатра се дължи на повече „магия“ по подразбиране – например неявна работа, която се случва зад кулисите. За новите потребители това често може да доведе до объркване.

Ето конкретен пример за „магия“ в Синатра:

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 беше най-лесната за търсене и най-достъпната. Докато Синатра и Мартини са добре документирани, самата документация не беше толкова достъпна. Поради тази причина Flask е победител в тази категория.



Общност

Flask е победителят в тази категория. Общността на Ruby често е догматична за това, че Rails е единственият добър избор, ако имате нужда от нещо повече от основна услуга (въпреки че Padrino предлага това на върха на Sinatra). Общността на Golang все още не е близо до консенсус относно една (или дори няколко) уеб рамки, което може да се очаква, тъй като самият език е толкова млад. Python обаче е приел редица подходи към уеб разработката, включително Django за готови напълно функционални уеб приложения и Flask, Bottle, CheryPy и Tornado за подход на микро-рамка.




Окончателно определяне

Имайте предвид, че целта на тази статия не беше да популяризира един инструмент, а по-скоро да предостави безпристрастно сравнение на Flask, Sinatra и Martini. С това казано, бих избрал Flask (Python) или Sinatra (Ruby). Ако идвате от език като C или Java, може би статично въведената природа на Golang може да ви хареса. Ако сте начинаещ, Flask може да е най-добрият избор, тъй като е много лесен за стартиране и работа и има много малко „магия“ по подразбиране. Моята препоръка е да бъдете гъвкави в решенията си, когато избирате библиотека за вашия проект.

Въпроси? Обратна връзка? Моля, коментирайте по-долу. Благодаря ви!

Също така, уведомете ни, ако се интересувате да видите някои сравнителни показатели.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. UML нотация

  2. Забавление с компресия (columnstore) на много голяма маса – част 2

  3. Синтетично генериране на данни

  4. Съвети за заключвания за четене/запис в зависимост от нивото на изолация на транзакциите в MSSQL

  5. Доклад за база данни с отворен код за 2019 г.:Топ бази данни, публичен облак срещу локален, устойчивост на полиглот