Правда ли, что Vapor на самом деле такой быстрый и безопасный, как говорят его авторы? Сегодня мы посмотрим на скорость работы Swift в серверной части вашего приложения в сравнении с Ruby, PHP, JS, Python, Java, C#, Go!

В своей прошлой публикации я затронул тему скорости работы такого веб-фреймворка как Vapor, разработчики обещают что он будет работать до 100 раз быстрее других фреймворков в ваших проектах, но одних лишь слов мало. Давайте взглянем на результаты официальных бенчмарков от Qutheory ( далее перевод )
Участники теста:
- Vapor (Swift)
- Ruby on Rails (Ruby)
- Laravel (PHP)
- Lumen (PHP)
- Express (JavaScript)
- Django (Python)
- Flask (Python)
- Spring (Java)
- Nancy (C#)
- Go (без фреймворков)
Тесты:
- Простой текст
- JSON
- Случайный SQLite запрос
Следующие команды запускались трижды для каждого фреймворка на раздельном Digital Ocean Droplet.
wrk -d 10 -t 4 -c 128 http://<host>:<port>/plaintext wrk -d 10 -t 4 -c 128 http://<host>:<port>/json wrk -d 10 -t 4 -c 128 http://<host>:<port>/sqlite-fetch
Простой текст
Тест на обработку простого текста является самым простым, а его результаты показывают максимальную скорость работы для каждого фреймворка. Удивительно насколько близко Vapor подобрался к Go. Чистый Swift HTTP сервер основан на потоках, в то время как Go использует сопрограммы. В некоторых случаях сопрограммы намного быстрее, но они требуют дополнительных библиотек и установки. Вполне возможно что Vapor примет этот способ параллелизма в будущем. Кроме того, Swift на Linux еще в бете, поэтому компилируется неоптимизированными toolchains. С новым компилятором Swift имеет все шансы свергнуть Go.

JSON
Будучи написанным на JavaScript, Express получает в этом тесте преимущество (JSON означает JavaScript Object Notation, если кто не знал). Vapor занимает почетное третье место из-за несовершенного синтаксического анализа JSON на Linux, но все равно остается как минимум в три раза быстрее большинства фреймворков.

SQLite запрос
С огромным отрывом Express вырвался вперед, а Go на удивление занимает четвертую позицию в данном тесте. Еще более удивительным является то, что Vapor стал вторым, будучи единственным фреймворком, кроме Spring, использующим ORM.

Код и конфигурация
Вы можете посмотреть код тестовых запросов и конфигурацию для каждого из фреймворков
Vapor
Vapor был запущен с на POSIX-thread HTTP, который компилировался используя Swift’s 06–06 toolchain с релизной конфигурацией и оптимизацией
vapor run --release --port=8000
Vapor CLI сделал создание и запуск приложений на этом фреймворке очень простым, это весь код, который мы использовали для теста.
import Vapor import Fluent import FluentSQLite let app = Application() do { let driver = try SQLiteDriver(path: "/home/helper/database/test.sqlite") Database.default = Database(driver: driver) } catch { print("Could not open SQLite database: \(error)") } app.get("plaintext") { request in return "Hello, world!" } app.get("json") { request in return JSON([ "array": [1, 2, 3], "dict": ["one": 1, "two": 2, "three": 3], "int": 42, "string": "test", "double": 3.14, "null": nil ]) } app.get("sqlite-fetch") { request in guard let user = try User.random() else { throw Abort.notFound } return user } app.globalMiddleware = [] app.start()
Настройка базы данных оказалась весьма простой с использованием Fluent, а Swift обеспечивает вашему приложению защиту от сбоев, даже если база данных не там, где мы думаем.
Ruby
«Рельсы» были запущены с помощью прилагаемого сервера, а база данных и маршруты сгенерированы в виде отдельных файлов.
bin/rails s — binding=107.170.131.198 -p 8600 -e production
class BenchmarkController < ActionController::Base def plaintext render plain: "Hello world" end def json a = [1, 2, 3] d = {"one" => 1, "two" => 2, "three" => 3} r = {"array" => a, "dict" => d, "int" => 42, "string" => "test", "double" => 3.14, "null" => nil} render :json => r end def sqlite r = ActiveRecord::Base.connection.exec_query("SELECT * FROM users ORDER BY random() LIMIT 1") render :json => r end end
production: <<: *default database: /home/helper/database/test.sqlite
Rails.application.routes.draw do get 'plaintext' => 'benchmark#plaintext' get 'json' => 'benchmark#json' get 'sqlite-fetch' => 'benchmark#sqlite' end
Nancy
Nancy является open-source проектом для .NET, в преимуществах у него простое тестирование, легкий вес и расширяемость. Имея более чем 250 соавторов и активное сообщество, Nancy показывает насколько C# может быть хорош в вебе.
using System; using System.IO; using System.Linq; using System.Reflection; using Dapper; using Microsoft.Data.Sqlite; using Nancy; namespace NancyVsVapor { public class HomeModule : NancyModule { private static string connstring = string.Concat("Data Source=", Path.Combine( Path.GetDirectoryName(typeof(HomeModule).GetTypeInfo().Assembly.Location), "test.sqlite")); private static Random random = new Random(); public HomeModule() { Get("/plaintext", _ => "Hello, World!"); Get("/json", _ => { return Response.AsJson(new JsonModel()); }); Get("sqlite-fetch", async (_, __) => { using (var conn = new SqliteConnection(connstring)) { var users = await conn.QueryAsync<User>("select * from users where id = @id", new { id = random.Next(1, 3) }); return Response.AsJson(users.FirstOrDefault()); } }); } } }
Laravel
Laravel был организован используя Nginx и PHP 5.
server { listen 8701; root /home/helper/laravel-tanner/benchmark/public; index index.php index.html index.htm; server_name 107.170.131.198; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { try_files $uri /index.php =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass unix:/var/run/php5-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }
<?php use DB; Route::get('/plaintext', function() { return 'Hello, world!'; }); Route::get('/json', function() { return [ 'array' => [1, 2, 3], 'dict' => [ 'one' => 1, 'two' => 2, 'three' => 3 ], 'int' => 42, 'string' => 'test', 'double' => 3.14, 'null' => null ]; }); Route::get('/sqlite-fetch', function() { return DB::select('SELECT * FROM users ORDER BY random() LIMIT 1'); });
Lumen
Lumen был организован аналогично Laravel.
<?php $app->get('/plaintext', function() { return 'Hello, world!'; }); $app->get('/json', function() { return [ 'array' => [1, 2, 3], 'dict' => [ 'one' => 1, 'two' => 2, 'three' => 3 ], 'int' => 42, 'string' => 'test', 'double' => 3.14, 'null' => null ]; }); $app->get('/sqlite-fetch', function() { return \DB::select('SELECT * FROM users ORDER BY random() LIMIT 1'); });
Express
Express был запущен используя NPM и кластер.
npm run cluster
var cluster = require('cluster'); if(cluster.isMaster) { var cpuCount = require('os').cpus().length; for(var i = 0; i < cpuCount; i += 1) { cluster.fork(); } cluster.on('exit', function(worker) { console.log('Worker %d died, replacing', worker.id); cluster.fork(); }); } else { var app = require('./app.js'); app.app.listen(app.port, function() { console.log('Benchmarking worker %d listening on %d', cluster.worker.id, app.port) }); }
const express = require('express'); const app = express(); const sqlite3 = require('sqlite3'); const db = new sqlite3.Database('../database/test.sqlite'); app.get('/plaintext', function(req, res) { res.setHeader('Content-Type', 'text/plain'); res.send('Hello, World!'); }); app.get('/json', function(req, res) { res.send({ array: [1, 2, 3], dict: { one: 1, two: 2, 'three': 3 }, int: 42, string: 'test', double: 3.14, 'null': null }); }); app.get('/sqlite-fetch', function(req, res) { db.get('select * from users where id = ?', Math.floor(Math.random() * 3) + 1, function(err, row) { if(err) { res.send(err.message); } else { res.send(row); } }); }); module.exports = { app: app, port: 8400 }
Express получает такую скорость из-за того, что под капотом у него находится высокопроизводительный C, даже если вы пишете на JavaScript запросы обрабатываются используя C библиотеки.
Django
Django был запущен используя wsgi и gunicorn.
from django.conf.urls import url from django.http import HttpResponse from django.http import JsonResponse from django.db import connection def plaintext(request): return HttpResponse('Hello, world') def json(request): return JsonResponse({ "array": [1, 2, 3], "dict": {"one": 1, "two": 2, "three": 3}, "int": 42, "string": "test", "double": 3.14, "null": None }) def sqlite(request): cursor = connection.cursor() cursor.execute("SELECT * FROM users ORDER BY random() LIMIT 1") row = cursor.fetchone() return JsonResponse(row, safe=False) urlpatterns = [ url(r'^plaintext', plaintext), url(r'^json', json), url(r'^sqlite-fetch', sqlite) ]
Flask
К Flask тот же подход.
import sys import flask import random import sqlite3 import logging import socket logging.basicConfig(level=logging.WARNING, format='%(levelname)s: %(message)s') app = flask.Flask(__name__) application = app db = sqlite3.connect('./test.sqlite') conn = db.cursor() conn.row_factory = sqlite3.Row @app.route("/plaintext") def plaintext(): return "Hello, world!" @app.route("/json") def json(): return flask.jsonify(**{ "array": [1, 2, 3], "dict": {"one": 1, "two": 2, "three": 3}, "int": 42, "string": "test", "double": 3.14, "null": None }) @app.route("/sqlite-fetch") def sqlite_fetch(): id = random.randint(1, 3) r = conn.execute("select * from users where id = ?", (id,)).fetchone() if r is not None: d = dict(zip(r.keys(), r)) return flask.jsonify(d) else: flask.abort(404) if __name__ == "__main__": port = 8137 print 'Listening on port %s' % port while True: try: app.run(port=port, host="107.170.131.198") sys.exit(0) except socket.error as e: logging.warn("socket error: %s" % e)
Go
Go использует веб-сервер, маршрутизатор, а все приложение уместилось в одном файле.
package main import ( "encoding/json" "flag" "fmt" "io" "log" "net/http" "github.com/jmoiron/sqlx" _ "github.com/mattn/go-sqlite3" ) func Plaintext(w http.ResponseWriter, req *http.Request) { io.WriteString(w, "Hello World!\n") } type JSONStruct struct { Array []int `json:"array"` Dict map[string]int `json:"dict"` Int int `json:"int"` String string `json:"string"` Double float64 `json:"double"` Null interface{} `json:"null"` } func JSON(w http.ResponseWriter, req *http.Request) { j := JSONStruct{Array: []int{1, 2, 3}, Dict: map[string]int{"one": 1, "two": 2, "three": 3}, Int: 42, String: "test", Double: 3.14, Null: nil} b, _ := json.MarshalIndent(j, "", " ") io.WriteString(w, string(b)) } type User struct { ID int `db:"id" json:"id,omitempty"` Name string `db:"name" json:"name,omitempty"` Email string `db:"email" json:"email,omitempty"` } // typical usage would keep or cache the open DB connection var db, _ = sqlx.Open("sqlite3", "../database/test.sqlite") func SQLiteFetch(w http.ResponseWriter, req *http.Request) { user := User{} rows, err := db.Queryx("select * from users order by random() limit 1") if err != nil { log.Fatal(err) } defer rows.Close() for rows.Next() { err = rows.StructScan(&user) if err != nil { log.Fatal(err) } b, _ := json.MarshalIndent(user, "", " ") io.WriteString(w, string(b)) } } var portNumber int func main() { flag.IntVar(&portNumber, "port", 8300, "port number to listen on") flag.Parse() http.HandleFunc("/plaintext", Plaintext) http.HandleFunc("/json", JSON) http.HandleFunc("/sqlite-fetch", SQLiteFetch) log.Println("bench running on", fmt.Sprintf("%d", portNumber)) err := http.ListenAndServe(fmt.Sprintf(":%d", portNumber), nil) if err != nil { log.Fatal(err) } }
Spring
Java была запущена с помощью Spring Boot на JVM.
package com.hlprmnky.vapor_spring_benchmark; import java.util.Arrays; import java.util.List; import java.util.Random; import java.util.concurrent.atomic.AtomicLong; import com.google.common.collect.ImmutableMap; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class ApplicationController { private final AtomicLong counter = new AtomicLong(); private final Random random = new Random(); @Autowired private UserRepository userRepository; @RequestMapping("/json") public Json json() { return new Json(counter.incrementAndGet(), Arrays.asList(1, 2, 3), ImmutableMap.of("one", 1, "two", 2, "three", 3), "test", 42, 3.14); } @RequestMapping("/plaintext") public String plaintext() { return "Hello, World!"; } @RequestMapping("/sqlite-fetch") public User sqliteFetch() { List<User> allUsers = userRepository.findAll(); return allUsers.get(random.nextInt(allUsers.size())); } }
Спасибо за внимание, источник по ссылке.
ссылка на оригинал статьи https://habrahabr.ru/post/317006/
Добавить комментарий