]>
Commit | Line | Data |
---|---|---|
1 | ## | |
2 | # This class represents a redis server instance. | |
3 | ||
4 | class Server | |
5 | ||
6 | ## | |
7 | # The amount of time to wait before attempting to re-establish a | |
8 | # connection with a server that is marked dead. | |
9 | ||
10 | RETRY_DELAY = 30.0 | |
11 | ||
12 | ## | |
13 | # The host the redis server is running on. | |
14 | ||
15 | attr_reader :host | |
16 | ||
17 | ## | |
18 | # The port the redis server is listening on. | |
19 | ||
20 | attr_reader :port | |
21 | ||
22 | ## | |
23 | # | |
24 | ||
25 | attr_reader :replica | |
26 | ||
27 | ## | |
28 | # The time of next retry if the connection is dead. | |
29 | ||
30 | attr_reader :retry | |
31 | ||
32 | ## | |
33 | # A text status string describing the state of the server. | |
34 | ||
35 | attr_reader :status | |
36 | ||
37 | ## | |
38 | # Create a new Redis::Server object for the redis instance | |
39 | # listening on the given host and port. | |
40 | ||
41 | def initialize(host, port = DEFAULT_PORT) | |
42 | raise ArgumentError, "No host specified" if host.nil? or host.empty? | |
43 | raise ArgumentError, "No port specified" if port.nil? or port.to_i.zero? | |
44 | ||
45 | @host = host | |
46 | @port = port.to_i | |
47 | ||
48 | @sock = nil | |
49 | @retry = nil | |
50 | @status = 'NOT CONNECTED' | |
51 | @timeout = 1 | |
52 | end | |
53 | ||
54 | ## | |
55 | # Return a string representation of the server object. | |
56 | def inspect | |
57 | "<Redis::Server: %s:%d (%s)>" % [@host, @port, @status] | |
58 | end | |
59 | ||
60 | ## | |
61 | # Try to connect to the redis server targeted by this object. | |
62 | # Returns the connected socket object on success or nil on failure. | |
63 | ||
64 | def socket | |
65 | return @sock if @sock and not @sock.closed? | |
66 | ||
67 | @sock = nil | |
68 | ||
69 | # If the host was dead, don't retry for a while. | |
70 | return if @retry and @retry > Time.now | |
71 | ||
72 | # Attempt to connect if not already connected. | |
73 | begin | |
74 | @sock = connect_to(@host, @port, @timeout) | |
75 | @sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1 | |
76 | @retry = nil | |
77 | @status = 'CONNECTED' | |
78 | rescue Errno::EPIPE, Errno::ECONNREFUSED => e | |
79 | puts "Socket died... socket: #{@sock.inspect}\n" if $debug | |
80 | @sock.close | |
81 | retry | |
82 | rescue SocketError, SystemCallError, IOError => err | |
83 | puts "Unable to open socket: #{err.class.name}, #{err.message}" if $debug | |
84 | mark_dead err | |
85 | end | |
86 | ||
87 | return @sock | |
88 | end | |
89 | ||
90 | def connect_to(host, port, timeout=nil) | |
91 | addrs = Socket.getaddrinfo('localhost', nil) | |
92 | addr = addrs.detect { |ad| ad[0] == 'AF_INET' } | |
93 | sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0) | |
94 | #addr = Socket.getaddrinfo(host, nil) | |
95 | #sock = Socket.new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0) | |
96 | ||
97 | if timeout | |
98 | secs = Integer(timeout) | |
99 | usecs = Integer((timeout - secs) * 1_000_000) | |
100 | optval = [secs, usecs].pack("l_2") | |
101 | sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval | |
102 | sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval | |
103 | end | |
104 | sock.connect(Socket.pack_sockaddr_in('6379', addr[3])) | |
105 | sock | |
106 | end | |
107 | ||
108 | ## | |
109 | # Close the connection to the redis server targeted by this | |
110 | # object. The server is not considered dead. | |
111 | ||
112 | def close | |
113 | @sock.close if @sock && !@sock.closed? | |
114 | @sock = nil | |
115 | @retry = nil | |
116 | @status = "NOT CONNECTED" | |
117 | end | |
118 | ||
119 | ## | |
120 | # Mark the server as dead and close its socket. | |
121 | def mark_dead(error) | |
122 | @sock.close if @sock && !@sock.closed? | |
123 | @sock = nil | |
124 | @retry = Time.now #+ RETRY_DELAY | |
125 | ||
126 | reason = "#{error.class.name}: #{error.message}" | |
127 | @status = sprintf "%s:%s DEAD (%s), will retry at %s", @host, @port, reason, @retry | |
128 | puts @status | |
129 | end | |
130 | ||
131 | end |