Created
January 6, 2012 19:16
-
-
Save joaodrp/1571976 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Simple web service that looks for an image metadata record on a MySQL db given its ID and sends it back in JSON | |
# | |
# Ruby 1.9.3 | |
# All gems updated to the most recent version | |
# MBP i5 8gb Lion | |
# | |
# See source code above, combo is: | |
# Sinatra + Thin: | |
# - mysql_db.rb # mysql2 api class | |
# - sinatra_thin_server.rb | |
# | |
# Goliath: | |
# - em_mysql_db.rb # em-mysql2 api class | |
# - goliath_server.rb | |
# | |
# Database setup: | |
# | |
$ mysql -u root | |
CREATE DATABASE mydb; | |
CREATE USER 'mydb'@'localhost' IDENTIFIED BY 'mydb'; | |
SET PASSWORD FOR 'mydb'@'localhost' = PASSWORD('passwd'); | |
GRANT ALL PRIVILEGES ON mydb.* TO 'mydb'@'localhost'; | |
# create table | |
CREATE TABLE IF NOT EXISTS `mydb`.`images` ( | |
`_id` VARCHAR(36) NOT NULL , | |
`name` VARCHAR(45) NOT NULL , | |
`access` VARCHAR(45) NOT NULL , | |
`type` VARCHAR(45) NULL , | |
`format` VARCHAR(45) NULL , | |
`owner` VARCHAR(45) NULL , | |
`status` VARCHAR(45) NULL , | |
`size` INT NULL , | |
`created_at` DATETIME NULL , | |
`accessed_at` DATETIME NULL , | |
`access_count` INT NULL DEFAULT 0 , | |
`checksum` VARCHAR(255) NULL , | |
`others` VARCHAR(255) NULL, | |
PRIMARY KEY (`_id`) ) | |
ENGINE = InnoDB; | |
# insert sample record a couple of times, make sure to change some character in _id string at each insert: | |
INSERT INTO `mydb`.`images` (_id, name, access, type, format, owner, status, size, created_at, accessed_at, access_count, checksum, others) | |
VALUES ('13e2d8ca-cd9e-4ca9-8366-0f953fd0f43f', 'hihi', 'public', 'picture', 'png', 'someone', 'locked', 1024, '2012-01-06 22:11:15 +0000', '2012-01-07 13:14:15 +0000', 1000, null, null) | |
# DONE! | |
# Sample JSON document retrieved with #get_image at each request: | |
# | |
{ | |
"image": { | |
"_id": "13e2d8ca-cd9e-4ca9-8366-0f953fd0f43f", | |
"name": "hihi", | |
"access": "public", | |
"type": null, | |
"format": null, | |
"owner": null, | |
"status": "locked", | |
"size": 1024, | |
"created_at": "2012-01-06 22:11:15 +0000", | |
"accessed_at": "2012-01-07 13:14:15 +0000", | |
"access_count": 10002, | |
"checksum": null, | |
"others": null | |
} | |
} | |
# | |
# Start servers | |
# | |
$ be ruby goliath_server.rb -sv -p 4568 -e production | |
$ be ruby sinatra_thin_server.rb #will autostart in 4567 and production | |
# | |
# Results: | |
# | |
# Grab some _id record from db to do this tests | |
# | |
# --------------- Goliath ---------------------- | |
$ ab -n1000 -c100 "http://0.0.0.0:4568/images/13e2d8ca-cd9e-4ca9-8366-0f953fd0f43f" | |
This is ApacheBench, Version 2.3 <$Revision: 1139530 $> | |
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ | |
Licensed to The Apache Software Foundation, http://www.apache.org/ | |
Benchmarking 0.0.0.0 (be patient) | |
... | |
Finished 1000 requests | |
Server Software: PostRank | |
Server Hostname: 0.0.0.0 | |
Server Port: 4568 | |
Document Path: /images/53b75e9a-b17a-4bea-976a-1eba70f6ba70 | |
Document Length: 312 bytes | |
Concurrency Level: 100 | |
Time taken for tests: 2.971 seconds | |
Complete requests: 1000 | |
Failed requests: 0 | |
Write errors: 0 | |
Total transferred: 495000 bytes | |
HTML transferred: 312000 bytes | |
Requests per second: 336.64 [#/sec] (mean) | |
Time per request: 297.055 [ms] (mean) | |
Time per request: 2.971 [ms] (mean, across all concurrent requests) | |
Transfer rate: 162.73 [Kbytes/sec] received | |
Connection Times (ms) | |
min mean[+/-sd] median max | |
Connect: 0 1 1.5 1 7 | |
Processing: 84 288 58.0 283 415 | |
Waiting: 68 265 55.8 269 392 | |
Total: 84 290 57.6 287 416 | |
# --------------- Sinatra ---------------------- | |
$ ab -n1000 -c100 "http://0.0.0.0:4567/images/13e2d8ca-cd9e-4ca9-8366-0f953fd0f43f" | |
This is ApacheBench, Version 2.3 <$Revision: 1139530 $> | |
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ | |
Licensed to The Apache Software Foundation, http://www.apache.org/ | |
Benchmarking 0.0.0.0 (be patient) | |
... | |
Finished 1000 requests | |
Server Software: thin | |
Server Hostname: 0.0.0.0 | |
Server Port: 4567 | |
Document Path: /images/ce9a45b5-bf83-4cc5-8648-c590cce4e7e0 | |
Document Length: 223 bytes | |
Concurrency Level: 100 | |
Time taken for tests: 1.338 seconds | |
Complete requests: 1000 | |
Failed requests: 0 | |
Write errors: 0 | |
Total transferred: 373000 bytes | |
HTML transferred: 223000 bytes | |
Requests per second: 747.64 [#/sec] (mean) | |
Time per request: 133.754 [ms] (mean) | |
Time per request: 1.338 [ms] (mean, across all concurrent requests) | |
Transfer rate: 272.33 [Kbytes/sec] received | |
Connection Times (ms) | |
min mean[+/-sd] median max | |
Connect: 0 1 1.2 0 6 | |
Processing: 62 128 16.1 129 154 | |
Waiting: 22 115 17.9 118 143 | |
Total: 68 129 15.2 130 154 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "em-synchrony" | |
require "em-synchrony/mysql2" | |
require 'json' | |
require 'uri' | |
module SomeModule | |
module MyExceptions | |
class NotFound < StandardError; end | |
end | |
end | |
module MyModule | |
class EmMySQL | |
include SomeModule::MyExceptions | |
def self.connect(opts = {}) | |
uri = URI.parse(opts[:uri]) | |
opts[:host] = uri.host=='' ? '127.0.0.1' : uri.host | |
opts[:port] = uri.port=='' ? 3306 : uri.port | |
opts[:db] = uri.path=='' ? 'my_db' : uri.path.gsub('/', '') | |
opts[:user] = uri.user=='' ? 'my_db' : uri.user | |
opts[:password] = uri.password=='' ? 'passwd' : uri.password | |
self.new opts | |
end | |
def initialize(opts) | |
@host = opts[:host] | |
@port = opts[:port] | |
@db = opts[:db] | |
@user = opts[:user] | |
@password = opts[:password] | |
EventMachine.synchrony do | |
conn = connection | |
conn.query %[ | |
CREATE TABLE IF NOT EXISTS `#{@db}`.`images` ( | |
`_id` VARCHAR(36) NOT NULL , | |
`name` VARCHAR(45) NOT NULL , | |
`access` VARCHAR(45) NOT NULL , | |
`type` VARCHAR(45) NULL , | |
`format` VARCHAR(45) NULL , | |
`owner` VARCHAR(45) NULL , | |
`status` VARCHAR(45) NULL , | |
`size` INT NULL , | |
`created_at` DATETIME NULL , | |
`accessed_at` DATETIME NULL , | |
`access_count` INT NULL DEFAULT 0 , | |
PRIMARY KEY (`_id`) ) | |
ENGINE = InnoDB; | |
] | |
conn.close | |
EventMachine.stop | |
end | |
end | |
def connection | |
Mysql2::EM::Client.new(host: @host, port: @port, database: @db, | |
username: @user, password: @password) | |
end | |
def get_image(id) | |
conn = connection | |
meta = conn.query("SELECT * FROM images WHERE _id='#{id}'", symbolize_keys: true).first | |
raise NotFound, "No image found with id '#{id}'." if meta.nil? | |
set_protected_get(id, conn) | |
conn.close | |
meta | |
end | |
def set_protected_get(id, conn) | |
conn.query "UPDATE images SET accessed_at='#{Time.now}', access_count=access_count+1 WHERE _id='#{id}'" | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'goliath' | |
require 'json' | |
require File.join(File.dirname(__FILE__) , 'em_mysql_db') | |
DB = MyModule::EmMySQL.connect uri: 'mysql://visor:passwd@127.0.0.1:3306/visor' | |
module SomeModule | |
module MyExceptions | |
class NotFound < StandardError; end | |
end | |
end | |
module MyModule | |
class GetImage < Goliath::API | |
include SomeModule::MyExceptions | |
def response(env) | |
begin | |
image = DB.get_image(params[:id]) | |
[200, {}, {image: image}.to_json] | |
rescue NotFound => e | |
[404, {}, {code: 404, message: e.message}.to_json] | |
end | |
end | |
end | |
class GoliathServer < Goliath::API | |
use Goliath::Rack::Heartbeat | |
use Goliath::Rack::Params | |
use Goliath::Rack::Render | |
use Goliath::Rack::DefaultMimeType | |
use Goliath::Rack::Validation::RequestMethod, %w(GET POST PUT DELETE) | |
get '/images/:id', GetImage | |
# other routes not touched... | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "mysql2" | |
require 'json' | |
require 'uri' | |
module SomeModule | |
module MyExceptions | |
class NotFound < StandardError; end | |
end | |
end | |
module MyModule | |
class MySQL | |
include SomeModule::MyExceptions | |
def self.connect(opts = {}) | |
uri = URI.parse(opts[:uri]) | |
opts[:host] = uri.host=='' ? '127.0.0.1' : uri.host | |
opts[:port] = uri.port=='' ? 3306 : uri.port | |
opts[:db] = uri.path=='' ? 'mydb' : uri.path.gsub('/', '') | |
opts[:user] = uri.user=='' ? 'mydb' : uri.user | |
opts[:password] = uri.password=='' ? 'passwd' : uri.password | |
self.new opts | |
end | |
def initialize(opts) | |
@host = opts[:host] | |
@port = opts[:port] | |
@db = opts[:db] | |
@user = opts[:user] | |
@password = opts[:password] | |
conn = connection | |
conn.query %[ | |
CREATE TABLE IF NOT EXISTS `#{@db}`.`images` ( | |
`_id` VARCHAR(36) NOT NULL , | |
`name` VARCHAR(45) NOT NULL , | |
`access` VARCHAR(45) NOT NULL , | |
`type` VARCHAR(45) NULL , | |
`format` VARCHAR(45) NULL , | |
`owner` VARCHAR(45) NULL , | |
`status` VARCHAR(45) NULL , | |
`size` INT NULL , | |
`created_at` DATETIME NULL , | |
`accessed_at` DATETIME NULL , | |
`access_count` INT NULL DEFAULT 0 , | |
`checksum` VARCHAR(255) NULL , | |
`others` VARCHAR(255) NULL, | |
PRIMARY KEY (`_id`) ) | |
ENGINE = InnoDB; | |
] | |
conn.close | |
end | |
def connection | |
Mysql2::Client.new(host: @host, port: @port, database: @db, | |
username: @user, password: @password) | |
end | |
def get_image(id) | |
conn = connection | |
meta = conn.query("SELECT * FROM images WHERE _id='#{id}'", symbolize_keys: true).first | |
raise NotFound, "No image found with id '#{id}'." if meta.nil? | |
set_protected_get(id, conn) | |
conn.close | |
meta | |
end | |
def set_protected_get(id, conn) | |
conn.query "UPDATE images SET accessed_at='#{Time.now}', access_count=access_count+1 WHERE _id='#{id}'" | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'sinatra/base' | |
require 'json' | |
require File.join(File.dirname(__FILE__) , 'mysql_db') | |
module SomeModule | |
module MyExceptions | |
class NotFound < StandardError; end | |
end | |
end | |
module MyModule | |
class SinatraThinServer < Sinatra::Base | |
include SomeModule::MyExceptions | |
configure do | |
DB = MyModule::MySQL.connect uri: 'mysql://mydb:passwd@127.0.0.1:3306/mydb' | |
use Rack::CommonLogger, STDOUT | |
end | |
get '/images/:id' do |id| | |
content_type :json | |
begin | |
image = DB.get_image(id) | |
{image: image}.to_json | |
rescue NotFound => e | |
error 404, {code: 404, message: e.message}.to_json | |
end | |
end | |
end | |
end | |
MyModule::SinatraThinServer.run! port: 4567, environment: :production |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment