evented-memcache-client 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +113 -0
- data/COPYING +348 -0
- data/README +75 -0
- data/Rakefile +123 -0
- data/doc/classes/EventMachine.html +146 -0
- data/doc/classes/EventMachine/Protocols.html +135 -0
- data/doc/classes/EventMachine/Protocols/Memcache.html +153 -0
- data/doc/classes/EventMachine/Protocols/Memcache/Client.html +121 -0
- data/doc/classes/EventMachine/Protocols/Memcache/Connectable.html +615 -0
- data/doc/classes/EventMachine/Protocols/Memcache/Connection.html +192 -0
- data/doc/classes/EventMachine/Protocols/Memcache/Sender.html +389 -0
- data/doc/classes/EventMachine/Protocols/Memcache/Server.html +463 -0
- data/doc/classes/EventMachine/Protocols/Memcache/TenaciousMC.html +277 -0
- data/doc/classes/MyHandler.html +197 -0
- data/doc/created.rid +1 -0
- data/doc/files/COPYING.html +473 -0
- data/doc/files/README.html +187 -0
- data/doc/files/extras/consumer_rb.html +110 -0
- data/doc/files/extras/producer_rb.html +110 -0
- data/doc/files/extras/server_rb.html +110 -0
- data/doc/files/lib/evented-memcache-client_rb.html +125 -0
- data/doc/files/lib/evented_memcache_client/client_rb.html +120 -0
- data/doc/files/lib/evented_memcache_client/connectable_rb.html +120 -0
- data/doc/files/lib/evented_memcache_client/connection_rb.html +120 -0
- data/doc/files/lib/evented_memcache_client/sender_rb.html +112 -0
- data/doc/files/lib/evented_memcache_client/server_rb.html +120 -0
- data/doc/files/lib/evented_memcache_client/tenacious_rb.html +120 -0
- data/doc/fr_class_index.html +36 -0
- data/doc/fr_file_index.html +38 -0
- data/doc/fr_method_index.html +66 -0
- data/doc/index.html +24 -0
- data/doc/rdoc-style.css +208 -0
- data/extras/consumer.rb +41 -0
- data/extras/producer.rb +22 -0
- data/extras/server.rb +33 -0
- data/lib/evented-memcache-client.rb +37 -0
- data/lib/evented_memcache_client/client.rb +36 -0
- data/lib/evented_memcache_client/connectable.rb +324 -0
- data/lib/evented_memcache_client/connection.rb +73 -0
- data/lib/evented_memcache_client/sender.rb +184 -0
- data/lib/evented_memcache_client/server.rb +118 -0
- data/lib/evented_memcache_client/tenacious.rb +106 -0
- metadata +115 -0
@@ -0,0 +1,73 @@
|
|
1
|
+
#############################################################################
|
2
|
+
#
|
3
|
+
# Author:: Colin Steele (colin@colinsteele.org)
|
4
|
+
# Homepage::
|
5
|
+
#
|
6
|
+
#----------------------------------------------------------------------------
|
7
|
+
#
|
8
|
+
# Copyright (C) 2008 by Colin Steele. All Rights Reserved.
|
9
|
+
# colin@colinsteele.org
|
10
|
+
#
|
11
|
+
# This program is free software; you can redistribute it and/or modify
|
12
|
+
# it under the terms of either: 1) the GNU General Public License
|
13
|
+
# as published by the Free Software Foundation; either version 2 of the
|
14
|
+
# License, or (at your option) any later version; or 2) Ruby's License.
|
15
|
+
#
|
16
|
+
# See the file COPYING for complete licensing information.
|
17
|
+
#
|
18
|
+
#---------------------------------------------------------------------------
|
19
|
+
#
|
20
|
+
#
|
21
|
+
#############################################################################
|
22
|
+
|
23
|
+
require 'rubygems'
|
24
|
+
require 'eventmachine'
|
25
|
+
|
26
|
+
module EventMachine
|
27
|
+
module Protocols
|
28
|
+
module Memcache
|
29
|
+
|
30
|
+
# Implements a memcache protocol connection.
|
31
|
+
|
32
|
+
class Connection < EventMachine::Connection
|
33
|
+
|
34
|
+
include Connectable
|
35
|
+
|
36
|
+
######################################################################
|
37
|
+
# EventMachine::Connection entry points
|
38
|
+
######################################################################
|
39
|
+
|
40
|
+
# Create a new connection. Args are a hash of callbacks (or a
|
41
|
+
# Module -- see Connectable#set_handler).
|
42
|
+
#
|
43
|
+
# Note that when you're using EventMachine, you won't be
|
44
|
+
# calling Connection::new yourself - EM does it for you when a
|
45
|
+
# connection is established.
|
46
|
+
#
|
47
|
+
# Eg:
|
48
|
+
# callbacks = {
|
49
|
+
# :open => Proc.new { |conn|
|
50
|
+
# conn.set(:key => 'x', :data => Time.now.to_s)
|
51
|
+
# },
|
52
|
+
# :stored => Proc.new { |conn, data, args|
|
53
|
+
# puts "Stored a value"
|
54
|
+
# EventMachine::stop
|
55
|
+
# },
|
56
|
+
# }
|
57
|
+
# EventMachine::run {
|
58
|
+
# EventMachine::connect('127.0.0.1',
|
59
|
+
# 12345,
|
60
|
+
# EventMachine::Protocols::Memcache::Connection,
|
61
|
+
# callbacks)
|
62
|
+
# }
|
63
|
+
#
|
64
|
+
def initialize(*args)
|
65
|
+
super(*args)
|
66
|
+
return if !args || args.empty?
|
67
|
+
set_handler(args[0])
|
68
|
+
end
|
69
|
+
|
70
|
+
end # Connection
|
71
|
+
end # Memcache
|
72
|
+
end # module Protocols
|
73
|
+
end # module EventMachine
|
@@ -0,0 +1,184 @@
|
|
1
|
+
#############################################################################
|
2
|
+
#
|
3
|
+
# Author:: Colin Steele (colin@colinsteele.org)
|
4
|
+
# Homepage::
|
5
|
+
#
|
6
|
+
#----------------------------------------------------------------------------
|
7
|
+
#
|
8
|
+
# Copyright (C) 2008 by Colin Steele. All Rights Reserved.
|
9
|
+
# colin@colinsteele.org
|
10
|
+
#
|
11
|
+
# This program is free software; you can redistribute it and/or modify
|
12
|
+
# it under the terms of either: 1) the GNU General Public License
|
13
|
+
# as published by the Free Software Foundation; either version 2 of the
|
14
|
+
# License, or (at your option) any later version; or 2) Ruby's License.
|
15
|
+
#
|
16
|
+
# See the file COPYING for complete licensing information.
|
17
|
+
#
|
18
|
+
#---------------------------------------------------------------------------
|
19
|
+
#
|
20
|
+
#
|
21
|
+
#############################################################################
|
22
|
+
|
23
|
+
module EventMachine
|
24
|
+
module Protocols
|
25
|
+
module Memcache
|
26
|
+
|
27
|
+
# Interface for sending memcache protocol messages like STORE,
|
28
|
+
# END, etc. See
|
29
|
+
# http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt
|
30
|
+
# for more details.
|
31
|
+
#
|
32
|
+
# NB: since several methods are defined dynamically here using
|
33
|
+
# define_method, they don't show up in the RDoc-generated
|
34
|
+
# documentation. Sorry.
|
35
|
+
|
36
|
+
module Sender
|
37
|
+
|
38
|
+
# Keeps stats on message sends.
|
39
|
+
def book_it(msg_type)
|
40
|
+
@snd_stats[msg_type] ||= 0
|
41
|
+
@snd_stats[msg_type] += 1
|
42
|
+
end
|
43
|
+
|
44
|
+
# Wish these could be properly rdoc'd...
|
45
|
+
%w(set add replace append prepend).each { |cmd|
|
46
|
+
class_eval do
|
47
|
+
define_method "#{cmd}" do |args|
|
48
|
+
book_it(cmd.to_sym)
|
49
|
+
key = args[:key]
|
50
|
+
flags = args[:flags] || 0
|
51
|
+
exptime = args[:exptime] || 0
|
52
|
+
noreply = args[:noreply] || false
|
53
|
+
data = args[:data]
|
54
|
+
raise ArgumentError.new(':data cannot be nil') unless data
|
55
|
+
raise ArgumentError.new(':key cannot be nil') unless key
|
56
|
+
cmd_str = "#{cmd} #{key} #{flags} #{exptime} #{data.length}"
|
57
|
+
cmd_str << " noreply" if noreply
|
58
|
+
send_to_peer "#{cmd_str}\r\n#{data}\r\n"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
}
|
62
|
+
|
63
|
+
# Send a cas request to the memcached server.
|
64
|
+
def cas(*args)
|
65
|
+
book_it(:cas)
|
66
|
+
key = args[:key]
|
67
|
+
flags = args[:flags] || 0
|
68
|
+
exptime = args[:exptime] || 0
|
69
|
+
cas_uniq = args[:cas_uniq]
|
70
|
+
noreply = args[:noreply] || false
|
71
|
+
data = args[:data]
|
72
|
+
raise ArgumentError.new(':data cannot be nil') unless data
|
73
|
+
raise ArgumentError.new(':key cannot be nil') unless key
|
74
|
+
raise ArgumentError.new(':cas_uniq cannot be nil') unless cas_uniq
|
75
|
+
cmd_str = "cas #{key} #{flags} #{exptime} #{data.length} #{cas_uniq}"
|
76
|
+
cmd_str << " noreply" if noreply
|
77
|
+
send_to_peer "#{cmd_str}\r\n#{data}\r\n"
|
78
|
+
end
|
79
|
+
|
80
|
+
# Send a get request to the memcached server. +args+ must
|
81
|
+
# have a :keys key if it is a hash, otherwise it's assumed
|
82
|
+
# that it is a single key. Eg., get('queue') or
|
83
|
+
# get(:keys=>['queue_a', 'queue_b']) or get(:key=>'queue').
|
84
|
+
def get(args)
|
85
|
+
if args.respond_to?(:keys)
|
86
|
+
keys = args[:keys] || [args[:key]]
|
87
|
+
else
|
88
|
+
keys = [args]
|
89
|
+
end
|
90
|
+
raise ArgumentError.new('No keys specified.') if keys.empty?
|
91
|
+
send_to_peer "get #{keys.join(' ')}\r\n"
|
92
|
+
end
|
93
|
+
alias_method :gets, :get
|
94
|
+
|
95
|
+
# Send a delete request to the memcached server. +args+ is a
|
96
|
+
# Hash with a required :key member, and optional :time and
|
97
|
+
# :noreply members.
|
98
|
+
def delete(*args)
|
99
|
+
book_it(:delete)
|
100
|
+
key = args[:key]
|
101
|
+
time = args[:time] || 0
|
102
|
+
noreply = args[:noreply] || false
|
103
|
+
raise ArgumentError.new(':key cannot be nil') unless key
|
104
|
+
cmd_str = "delete #{key}"
|
105
|
+
cmd_str << " #{time}" if time > 0
|
106
|
+
cmd_str << " noreply" if noreply
|
107
|
+
send_to_peer "#{cmd_str}\r\n"
|
108
|
+
end
|
109
|
+
|
110
|
+
# Wish these could be properly rdoc'd...
|
111
|
+
%w(incr decr).each { |cmd|
|
112
|
+
class_eval do
|
113
|
+
define_method "#{cmd}" do |args|
|
114
|
+
book_it(cmd.to_sym)
|
115
|
+
key = args[:key]
|
116
|
+
value = args[:value] || 0
|
117
|
+
noreply = args[:noreply] || false
|
118
|
+
raise ArgumentError.new(':key cannot be nil') unless key
|
119
|
+
raise ArgumentError.new(':value cannot be nil') unless value
|
120
|
+
cmd_str = "#{cmd} #{key} #{value}"
|
121
|
+
cmd_str << " noreply" if noreply
|
122
|
+
send_to_peer "#{cmd_str}\r\n"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
}
|
126
|
+
|
127
|
+
# Send a stats request to the memcached server. +args+ is a
|
128
|
+
# Array that specifies the specific stats command as defined
|
129
|
+
# in the memcache protocol.
|
130
|
+
def stats(*args)
|
131
|
+
book_it(:stats)
|
132
|
+
cmd_str = "stats #{args.join(' ')}"
|
133
|
+
send_to_peer "#{cmd_str}\r\n"
|
134
|
+
end
|
135
|
+
|
136
|
+
# Wish these could be properly rdoc'd...
|
137
|
+
%w(flush_all version quit).each { |cmd|
|
138
|
+
class_eval do
|
139
|
+
define_method "#{cmd}" do
|
140
|
+
book_it(cmd.to_sym)
|
141
|
+
send_to_peer "#{cmd}\r\n"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
}
|
145
|
+
|
146
|
+
# Wish these could be properly rdoc'd...
|
147
|
+
%w(stored end).each { |cmd|
|
148
|
+
class_eval do
|
149
|
+
define_method "#{cmd}" do
|
150
|
+
book_it(cmd.to_sym)
|
151
|
+
send_to_peer "#{cmd.upcase}\r\n"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
}
|
155
|
+
|
156
|
+
# Send a value message to the remote endpoint (client); this
|
157
|
+
# is a server-sent message. +args+ is a
|
158
|
+
# Hash with a required :key member, and optional :flash and
|
159
|
+
# :cas_uniq members. Hash must also include a :data
|
160
|
+
# member (the payload to be sent).
|
161
|
+
def value(args)
|
162
|
+
book_it(:value)
|
163
|
+
key = args[:key]
|
164
|
+
flags = args[:flags] || 0
|
165
|
+
cas_uniq = args[:cas_uniq]
|
166
|
+
data = args[:data]
|
167
|
+
raise ArgumentError.new(':data cannot be nil') unless data
|
168
|
+
raise ArgumentError.new(':key cannot be nil') unless key
|
169
|
+
cmd_str = "VALUE #{key} #{flags} #{data.length}"
|
170
|
+
cmd_str << " #{cas_uniq}" if cas_uniq
|
171
|
+
send_to_peer "#{cmd_str}\r\n#{data}\r\n"
|
172
|
+
end
|
173
|
+
|
174
|
+
# Send a memcache protocol message contained in the :data
|
175
|
+
# argument to the remote endpoint.
|
176
|
+
def send_to_peer(data)
|
177
|
+
@msgs_out ||= 0
|
178
|
+
@msgs_out += 1
|
179
|
+
send_data(data)
|
180
|
+
end
|
181
|
+
end # Sender
|
182
|
+
end # Memcache
|
183
|
+
end # Protocols
|
184
|
+
end # EventMachine
|
@@ -0,0 +1,118 @@
|
|
1
|
+
#############################################################################
|
2
|
+
#
|
3
|
+
# Author:: Colin Steele (colin@colinsteele.org)
|
4
|
+
# Homepage::
|
5
|
+
#
|
6
|
+
#----------------------------------------------------------------------------
|
7
|
+
#
|
8
|
+
# Copyright (C) 2008 by Colin Steele. All Rights Reserved.
|
9
|
+
# colin@colinsteele.org
|
10
|
+
#
|
11
|
+
# This program is free software; you can redistribute it and/or modify
|
12
|
+
# it under the terms of either: 1) the GNU General Public License
|
13
|
+
# as published by the Free Software Foundation; either version 2 of the
|
14
|
+
# License, or (at your option) any later version; or 2) Ruby's License.
|
15
|
+
#
|
16
|
+
# See the file COPYING for complete licensing information.
|
17
|
+
#
|
18
|
+
#---------------------------------------------------------------------------
|
19
|
+
#
|
20
|
+
#
|
21
|
+
#############################################################################
|
22
|
+
|
23
|
+
require 'rubygems'
|
24
|
+
require 'eventmachine'
|
25
|
+
|
26
|
+
module EventMachine
|
27
|
+
module Protocols
|
28
|
+
module Memcache
|
29
|
+
class Server
|
30
|
+
|
31
|
+
DEFAULT_PORT = 22122
|
32
|
+
DEFAULT_HOST = 'localhost'
|
33
|
+
|
34
|
+
attr_accessor :connections
|
35
|
+
attr :host, true
|
36
|
+
attr :port, true
|
37
|
+
|
38
|
+
# Create a new memcache server listener, on the specified host
|
39
|
+
# and port.
|
40
|
+
def initialize(host=nil, port=nil)
|
41
|
+
@host = host || DEFAULT_HOST
|
42
|
+
@port = port || DEFAULT_PORT
|
43
|
+
@connections = []
|
44
|
+
end
|
45
|
+
|
46
|
+
# Start listening for client connections.
|
47
|
+
def start
|
48
|
+
begin
|
49
|
+
@signature = EventMachine.start_server(@host,
|
50
|
+
@port.to_i,
|
51
|
+
Connectable,
|
52
|
+
self) { |conn|
|
53
|
+
conn.set_handler(self)
|
54
|
+
|
55
|
+
# Usually called from EM; we fake it out.
|
56
|
+
conn.connection_completed
|
57
|
+
}
|
58
|
+
rescue => e
|
59
|
+
return false
|
60
|
+
else
|
61
|
+
return true
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Stop listening for client connections, and close any open
|
66
|
+
# clients.
|
67
|
+
def stop
|
68
|
+
EventMachine.stop_server(@signature)
|
69
|
+
unless wait_for_connections
|
70
|
+
# Still some running
|
71
|
+
EventMachine.add_periodic_timer(1) {
|
72
|
+
wait_for_connections
|
73
|
+
}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
######################################################################
|
79
|
+
# Callbacks from the connection
|
80
|
+
######################################################################
|
81
|
+
|
82
|
+
def open(conn)
|
83
|
+
@connections << conn
|
84
|
+
end
|
85
|
+
|
86
|
+
def close(conn)
|
87
|
+
@connections.delete(conn)
|
88
|
+
end
|
89
|
+
|
90
|
+
def set(conn, data, args)
|
91
|
+
# we don't do nuffing
|
92
|
+
end
|
93
|
+
alias_method :replace, :set
|
94
|
+
alias_method :prepend, :set
|
95
|
+
alias_method :append, :set
|
96
|
+
alias_method :add, :set
|
97
|
+
|
98
|
+
def get(conn, data, args)
|
99
|
+
# we don't do nuffing
|
100
|
+
end
|
101
|
+
|
102
|
+
protected
|
103
|
+
|
104
|
+
def wait_for_connections
|
105
|
+
if @connections.empty?
|
106
|
+
true
|
107
|
+
else
|
108
|
+
@connections.each { |c|
|
109
|
+
c.close_connection_after_writing
|
110
|
+
}
|
111
|
+
false
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end # Server
|
116
|
+
end # Memcache
|
117
|
+
end # module Protocols
|
118
|
+
end # module EventMachine
|
@@ -0,0 +1,106 @@
|
|
1
|
+
#############################################################################
|
2
|
+
#
|
3
|
+
# Author:: Colin Steele (colin@colinsteele.org)
|
4
|
+
# Homepage::
|
5
|
+
#
|
6
|
+
#----------------------------------------------------------------------------
|
7
|
+
#
|
8
|
+
# Copyright (C) 2008 by Colin Steele. All Rights Reserved.
|
9
|
+
# colin@colinsteele.org
|
10
|
+
#
|
11
|
+
# This program is free software; you can redistribute it and/or modify
|
12
|
+
# it under the terms of either: 1) the GNU General Public License
|
13
|
+
# as published by the Free Software Foundation; either version 2 of the
|
14
|
+
# License, or (at your option) any later version; or 2) Ruby's License.
|
15
|
+
#
|
16
|
+
# See the file COPYING for complete licensing information.
|
17
|
+
#
|
18
|
+
#---------------------------------------------------------------------------
|
19
|
+
#
|
20
|
+
#
|
21
|
+
#############################################################################
|
22
|
+
|
23
|
+
require 'rubygems'
|
24
|
+
require 'eventmachine'
|
25
|
+
|
26
|
+
module EventMachine
|
27
|
+
module Protocols
|
28
|
+
module Memcache
|
29
|
+
|
30
|
+
# TenaciousMC is a "tenacious" memcache client connection. When
|
31
|
+
# the underlying connection is lost, it attempts to reacquire it
|
32
|
+
# automatically.
|
33
|
+
|
34
|
+
class TenaciousMC < Client
|
35
|
+
|
36
|
+
# Create a new "tenacious" connection, which will attempt to
|
37
|
+
# reacquire a connection to the server if it is lost.
|
38
|
+
#
|
39
|
+
# Note that when you're using EventMachine, you won't be
|
40
|
+
# calling TenaciousMC::new yourself - EM does it for you when a
|
41
|
+
# connection is established.
|
42
|
+
#
|
43
|
+
# First argument as per EventMachine::Protocols::Memcache::Client.
|
44
|
+
# Ie., callback hash, module or handler object.
|
45
|
+
#
|
46
|
+
# Second argument is hash of options governing TenaciousMC's
|
47
|
+
# behavior:
|
48
|
+
#
|
49
|
+
# { :host => 'somewhere.com', :port=>12345, :interval => 5 }
|
50
|
+
#
|
51
|
+
# If the options hash includes a :eager key, and its value is
|
52
|
+
# true, then when the connection opens, TenaciousMC will
|
53
|
+
# immediately send a 'get' to the server to fetch data, using
|
54
|
+
# the value of the :key option as the key fetched.
|
55
|
+
#
|
56
|
+
# So a typical invocation:
|
57
|
+
#
|
58
|
+
# EventMachine::run {
|
59
|
+
# EventMachine::connect('over.there',
|
60
|
+
# 12345,
|
61
|
+
# EventMachine::Protocols::Memcache::TenaciousMC,
|
62
|
+
# MyHandlerModule,
|
63
|
+
# {:host => 'over.there', :port => 12345})
|
64
|
+
# }
|
65
|
+
def initialize(*args)
|
66
|
+
@handle_arg = args[0]
|
67
|
+
options = args[1]
|
68
|
+
@host = options[:host] || 'localhost'
|
69
|
+
@port = options[:port]
|
70
|
+
raise "No port option is set" unless @port
|
71
|
+
@interval = options[:interval] || 5
|
72
|
+
@eager = options[:eager] || false
|
73
|
+
@key = options[:key] || nil
|
74
|
+
super(*args)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Called from EM::Connection after the connection is established.
|
78
|
+
def connection_completed
|
79
|
+
super
|
80
|
+
if @eager && @key
|
81
|
+
self.get(:key => @key)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Called from EM::Connection when the connection is dead.
|
86
|
+
def unbind
|
87
|
+
super
|
88
|
+
if @interval > 0
|
89
|
+
EventMachine::add_timer(@interval) do
|
90
|
+
EventMachine::connect(@host,
|
91
|
+
@port,
|
92
|
+
self.class,
|
93
|
+
@handle_arg,
|
94
|
+
{
|
95
|
+
:host => @host,
|
96
|
+
:port => @port,
|
97
|
+
:interval => @interval
|
98
|
+
})
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end # TenaciousMC
|
104
|
+
end # Memcache
|
105
|
+
end # module Protocols
|
106
|
+
end # module EventMachine
|