Setelah perbandingan Python, Ruby, dan Golang baru-baru ini untuk aplikasi baris perintah, saya memutuskan untuk menggunakan pola yang sama untuk membandingkan membangun layanan web sederhana. Saya telah memilih Flask (Python), Sinatra (Ruby), dan Martini (Golang) untuk perbandingan ini. Ya, ada banyak opsi lain untuk pustaka aplikasi web dalam setiap bahasa, tetapi saya merasa ketiganya cocok untuk perbandingan.
Ikhtisar Perpustakaan
Berikut adalah perbandingan tingkat tinggi perpustakaan oleh Stackshare.
Flask (Python)
Flask adalah kerangka kerja mikro untuk Python berdasarkan Werkzeug, Jinja2 dan niat baik.
Untuk aplikasi yang sangat sederhana, seperti yang ditunjukkan dalam demo ini, Flask adalah pilihan yang tepat. Aplikasi Flask dasar hanya 7 baris kode (LOC) dalam satu file sumber Python. Keunggulan Flask dibandingkan pustaka web Python lainnya (seperti Django atau Pyramid) adalah bahwa Anda dapat memulai dari yang kecil dan membangun aplikasi yang lebih kompleks sesuai kebutuhan.
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run()
Sinatra (Ruby)
Sinatra adalah DSL untuk membuat aplikasi web dengan cepat di Ruby dengan sedikit usaha.
Sama seperti Flask, Sinatra sangat bagus untuk aplikasi sederhana. Aplikasi dasar Sinatra hanya 4 LOC dalam satu file sumber Ruby. Sinatra digunakan sebagai pengganti library seperti Ruby on Rails untuk alasan yang sama seperti Flask - Anda dapat memulai dari yang kecil dan memperluas aplikasi sesuai kebutuhan.
require 'sinatra'
get '/hi' do
"Hello World!"
end
Martini (Golang)
Martini adalah paket yang kuat untuk menulis aplikasi/layanan web modular dengan cepat di Golang.
Martini hadir dengan beberapa baterai yang disertakan daripada Sinatra dan Flask tetapi masih sangat ringan untuk memulai - hanya 9 LOC untuk aplikasi dasar. Martini mendapat beberapa kritik dari komunitas Golang tetapi masih memiliki salah satu proyek Github berperingkat tertinggi dari semua kerangka web Golang. Penulis Martini menanggapi langsung kritik di sini. Beberapa kerangka kerja lain termasuk Revel, Gin, dan bahkan perpustakaan net/http bawaan.
package main
import "github.com/go-martini/martini"
func main() {
m := martini.Classic()
m.Get("/", func() string {
return "Hello world!"
})
m.Run()
}
Tanpa dasar-dasarnya, mari buat aplikasi!
Deskripsi Layanan
Layanan yang dibuat menyediakan aplikasi blog yang sangat mendasar. Rute berikut dibuat:
GET /
:Mengembalikan blog (menggunakan template untuk merender).GET /json
:Mengembalikan konten blog dalam format JSON.POST /new
:Menambahkan postingan baru (judul, ringkasan, konten) ke blog.
Antarmuka eksternal ke layanan blog persis sama untuk setiap bahasa. Untuk kesederhanaan MongoDB akan digunakan sebagai penyimpanan data untuk contoh ini karena ini adalah yang paling sederhana untuk diatur dan kita tidak perlu khawatir tentang skema sama sekali. Dalam aplikasi "seperti blog" normal, database relasional mungkin diperlukan.
Tambahkan Pos
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
Melihat HTML
GET /
Lihat JSON
GET /json
[
{
content:"Lorem ipsum dolor sit amet, consectetur ...",
title:"Test Post 1",
_id:{
$oid:"558329927315660001550970"
},
summary:"The First Test Post"
}
]
Struktur Aplikasi
Setiap aplikasi dapat dipecah menjadi komponen-komponen berikut:
Pengaturan Aplikasi
- Inisialisasi aplikasi
- Jalankan aplikasi
Permintaan
- Tentukan rute di mana pengguna dapat meminta data (GET)
- Tentukan rute tempat pengguna dapat mengirimkan data (POST)
Tanggapan
- Render JSON (
GET /json
) - Membuat template (
GET /
)
Basis Data
- Inisialisasi koneksi
- Masukkan data
- Ambil data
Penerapan Aplikasi
- Pekerja Buruh!
Selebihnya dari artikel ini akan membandingkan setiap komponen ini untuk setiap perpustakaan. Tujuannya bukan untuk menyarankan bahwa salah satu perpustakaan ini lebih baik daripada yang lain - ini untuk memberikan perbandingan khusus antara ketiga alat:
- Flask (Python)
- Sinatra (Ruby)
- Martini (Golang)
Penyiapan Proyek
Semua proyek di-bootstrap menggunakan docker dan docker-compose. Sebelum menyelami bagaimana setiap aplikasi di-bootstrap di bawah tenda, kita bisa menggunakan buruh pelabuhan untuk menjalankan dan menjalankan masing-masing dengan cara yang persis sama - docker-compose up
Serius, itu dia! Sekarang untuk setiap aplikasi ada Dockerfile
dan docker-compose.yml
file yang menentukan apa yang terjadi ketika Anda menjalankan perintah di atas.
Python (flask) - Dockerfile
FROM python:3.4
ADD . /app
WORKDIR /app
RUN pip install -r requirements.txt
Dockerfile
ini mengatakan bahwa kita memulai dari gambar dasar dengan Python 3.4 terinstal, menambahkan aplikasi kita ke /app
direktori dan menggunakan pip untuk menginstal persyaratan aplikasi kami yang ditentukan dalam requirements.txt
.
Ruby (sinatra)
FROM ruby:2.2
ADD . /app
WORKDIR /app
RUN bundle install
Dockerfile
ini mengatakan bahwa kita memulai dari gambar dasar dengan Ruby 2.2 terinstal, menambahkan aplikasi kita ke /app
direktori dan menggunakan bundler untuk menginstal persyaratan aplikasi kami yang ditentukan dalam 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
Dockerfile
ini mengatakan bahwa kita memulai dari gambar dasar dengan Golang 1.3 terinstal, menambahkan aplikasi kita ke /go/src/github.com/kpurdon/go-blog
direktori dan mendapatkan semua dependensi yang diperlukan menggunakan go get
perintah.
Inisialisasi/Jalankan Aplikasi
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
Tentukan Rute (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) {
// ...
}
Render Respons JSON
Python (Flask)
Flask menyediakan metode jsonify() tetapi karena layanan menggunakan MongoDB, utilitas mongodb bson digunakan.
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
Merender Respons HTML (Templat)
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>
Koneksi Basis Data
Semua aplikasi menggunakan driver mongodb khusus untuk bahasa tersebut. Variabel lingkungan DB_PORT_27017_TCP_ADDR
adalah IP dari wadah buruh pelabuhan yang ditautkan (ip basis data).
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()
Menyisipkan Data Dari 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
Ambil 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)
Penerapan Aplikasi (Docker!)
Solusi hebat untuk menerapkan semua aplikasi ini adalah dengan menggunakan docker dan 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
Kesimpulan
Untuk menyimpulkan, mari kita lihat apa yang saya yakini sebagai beberapa kategori di mana perpustakaan yang disajikan terpisah satu sama lain.
Kesederhanaan
Sementara Flask sangat ringan dan membaca dengan jelas, aplikasi Sinatra adalah yang paling sederhana dari ketiganya di 23 LOC (dibandingkan dengan 46 untuk Flask dan 42 untuk Martini). Untuk alasan ini Sinatra adalah pemenang dalam kategori ini. Namun perlu dicatat bahwa kesederhanaan Sinatra disebabkan oleh "keajaiban" yang lebih default - misalnya, pekerjaan implisit yang terjadi di belakang layar. Bagi pengguna baru, hal ini sering kali menyebabkan kebingungan.
Berikut adalah contoh spesifik dari “sihir” di Sinatra:
params # the "request.form" logic in python is done "magically" behind the scenes in Sinatra.
Dan kode Flask yang setara:
from flask import request
params = {
'title': request.form['title'],
'summary': request.form['summary'],
'content': request.form['content']
}
Untuk pemula dalam memprogram Flask dan Sinatra tentu saja lebih sederhana, tetapi untuk programmer berpengalaman dengan waktu yang dihabiskan dalam bahasa lain yang diketik secara statis, Martini menyediakan antarmuka yang cukup sederhana.
Dokumentasi
Dokumentasi Flask adalah yang paling sederhana untuk dicari dan paling mudah didekati. Sementara Sinatra dan Martini didokumentasikan dengan baik, dokumentasi itu sendiri tidak begitu mudah didekati. Untuk itulah Flask adalah pemenang dalam kategori ini.
Komunitas
Flask adalah pemenang dalam kategori ini. Komunitas Ruby lebih sering dogmatis tentang Rails sebagai satu-satunya pilihan yang baik jika Anda membutuhkan sesuatu yang lebih dari layanan dasar (meskipun Padrino menawarkan ini di atas Sinatra). Komunitas Golang masih belum mencapai kesepakatan tentang satu (atau bahkan beberapa) kerangka kerja web, yang diharapkan karena bahasanya sendiri masih sangat muda. Namun Python telah merangkul sejumlah pendekatan untuk pengembangan web termasuk Django untuk aplikasi web berfitur lengkap dan Flask, Botol, CheryPy, dan Tornado untuk pendekatan kerangka mikro.
Penentuan Akhir
Perhatikan bahwa inti dari artikel ini bukanlah untuk mempromosikan satu alat, melainkan untuk memberikan perbandingan yang tidak memihak dari Flask, Sinatra, dan Martini. Dengan itu, saya akan memilih Flask (Python) atau Sinatra (Ruby). Jika Anda berasal dari bahasa seperti C atau Java, mungkin sifat Golang yang diketik secara statis mungkin menarik bagi Anda. Jika Anda seorang pemula, Flask mungkin menjadi pilihan terbaik karena sangat mudah untuk digunakan dan hanya ada sedikit "keajaiban" default. Rekomendasi saya adalah Anda fleksibel dalam mengambil keputusan saat memilih perpustakaan untuk proyek Anda.
pertanyaan? Masukan? Silakan komentar di bawah. Terima kasih!
Selain itu, beri tahu kami jika Anda tertarik untuk melihat beberapa tolok ukur.