I made a Ruby web server based on Apache Thrift, but the client (also in ruby, for unit tests) refuses to work and keeps telling me either Thrift::TransportException: Could not connect to 127.0.0.1:8001: Connection refused - connect(2) for 127.0.0.1:8001, or Thrift::TransportException: end of file reached. Tried a bunch of different server implementations and transports, and that doesn't seem to work.
When the server is running, lsof -i :8001 shows
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
ruby 19073 lafickens 9u IPv4 0x878e6fd36b981a71 0t0 TCP *:vcom-tunnel (LISTEN)
So I think the server is functioning.
Here's the server code:
class Server
attr_reader :name
def initialize(name)
#LOGGER = Logger.new $stdout
#name = name
#started = false
#processor = ::Thrift::MultiplexedProcessor.new
#processor.register_processor 'User Service', Thrift::UserService::Processor.new(Handlers::UserServiceHandler.new)
#processor.register_processor 'Sync Service', Thrift::SyncService::Processor.new(Handlers::SyncServiceHandler.new)
end
def start
#transport = ::Thrift::ServerSocket.new(Options.get('port'))
#transport_factory = ::Thrift::BufferedTransportFactory.new
#protocol_factory = ::Thrift::CompactProtocolFactory.new
#server = ::Thrift::ThreadPoolServer.new #processor, #transport, #transport_factory, #protocol_factory
#server_thread = Thread.new {
#server.serve
}
#started = true
#LOGGER.info('Server started successfully')
end
def stop
return if #server_thread.nil?
#server_thread.exit
#transport.close
#started = false
#LOGGER.info('Server stopped successfully')
end
def restart
stop
start
end
def started?
#started
end
end
Client code (actually unit tests)
class TestUserServiceHandler < Test::Unit::TestCase
def setup
#server = Billboard::Server.new 'test handler'
#server.start
#port = Billboard::Options.get 'port'
#transport = ::Thrift::BufferedTransport.new(::Thrift::Socket.new('127.0.0.1', #port))
#binary_protocol = ::Thrift::BinaryProtocol.new #transport
#multiplexed_protocol = ::Thrift::MultiplexedProtocol.new #binary_protocol, 'mprotocol'
#client = Billboard::Thrift::UserService::Client.new #multiplexed_protocol
#transport.open
end
def teardown
#server.stop
#transport.close
end
def test_authenticate
#client.authenticate('test', 'test')
end
# And other tests...
end
Thanks in advance.
Related
I am trying to use haproxy to load balance my websocket rack application.
I publish message in channel rates using redis-cli and this succeeds puts "sent" if ws.send(msg)
The client does receive the 'Welcome! from server' message so I know the initial handshake is done.
But, the client never receives the published message in channel 'rates'.
web_socket.rb
require 'faye/websocket'
module WebSocket
class App
KEEPALIVE_TIME = 15 # in seconds
def initialize(app)
#app = app
#mutex = Mutex.new
#clients = []
# #redis = Redis.new(host: 'rds', port: 6739)
Thread.new do
#redis_sub = Redis.new(host: 'rds', port: 6379)
#redis_sub.subscribe('rates') do |on|
on.message do |channel, msg|
p [msg,#clients.length]
#mutex.synchronize do
#clients.each do |ws|
# ws.ping 'Mic check, one, two' do
p ws
puts "sent" if ws.send(msg)
# end
end
end
end
end
end
end
def call(env)
if Faye::WebSocket.websocket?(env)
# WebSockets logic goes here
ws = Faye::WebSocket.new(env, nil) # {ping: KEEPALIVE_TIME }
ws.on :open do |event|
p [:open, ENV['APPID'], ws.object_id]
ws.ping 'Mic check, one, two' do
# fires when pong is received
puts "Welcome sent" if ws.send('Welcome! from server')
#mutex.synchronize do
#clients << ws
end
p [#clients.length, ' Client Connected']
end
end
ws.on :close do |event|
p [:close, ENV['APPID'], ws.object_id, event.code, event.reason]
#mutex.synchronize do
#clients.delete(ws)
end
p #clients.length
ws = nil
end
ws.on :message do |event|
p [:message, event.data]
# #clients.each {|client| client.send(event.data) }
end
# Return async Rack response
ws.rack_response
else
#app.call(env)
end
end
end
end
My haproxy.cfg
frontend http
bind *:8080
mode http
timeout client 1000s
use_backend all
backend all
mode http
timeout server 1000s
timeout tunnel 1000s
timeout connect 1000s
server s1 app1:8080
server s2 app2:8080
server s3 app3:8080
server s4 app4:8080
Chrome dev tools
Please help me!!!
EDIT:
I have tried Thread running in Middleware is using old version of parent's instance variable but this does not work.
As mentioned earlier. The below code succeeds
puts "sent" if ws.send(msg)
Okay, After a lot of searching and testing.. I found that the issue was with not setting a ping. during websocket initialization in the server.
Change this
ws = Faye::WebSocket.new(env, nil) # {ping: KEEPALIVE_TIME }
to
ws = Faye::WebSocket.new(env, nil, {ping: KEEPALIVE_TIME })
My KEEPALIVE_TIME is 0.5 because I am making a stock application where rates change very quickly. You can keep it per your needs.
I'm get stuck to write a tcp server daemon in Ruby 2.3. The issue is, that my connection is not going further, when postfix is communicating with the ruby tcp server. If i do connect to the ruby tcp server by telnet, everything works fine. My code is as follows:
require 'socket'
require_relative 'postfix_delegation_object'
class Server
attr_reader :binding, :port
def initialize(binding: '127.0.0.1', port: '1988')
puts "Starting server now!"
puts "Listening on tcp://#{binding}:#{port}"
socket = TCPServer.new(binding, port)
while client = socket.accept
Thread.new { handle_connection(client) }
end
end
def handle_connection(client)
hash_values = {}
puts "New client! #{client}"
while line = client.gets
if line.include? "="
key, val = line.split('=')
hash_values[key] = val.to_s.strip
else
pdo = PostfixDelegationObject.new(hash_values)
client.write("action=dunno")
end
end
end
end
I could solve it by my own. I just had to enter twice '\n'
like this:
client.write("action=dunno\n\n")
This one would not work:
client.write("action=dunno")
client.write("\n\n")
I'm playing with celluloid gem. The example works well, but when I press Ctrl-C I get the unexpected message:
^CD, [2015-10-07T09:53:19.784411 #16326] DEBUG -- : Terminating 8 actors...
and after few seconds, I get the error:
E, [2015-10-07T09:53:29.785162 #16326] ERROR -- : Couldn't cleanly terminate all actors in 10 seconds!
/usr/local/rvm/gems/ruby-2.0.0-p353/gems/eventmachine-1.0.7/lib/eventmachine.rb:187:in `run_machine': Interrupt
from /usr/local/rvm/gems/ruby-2.0.0-p353/gems/eventmachine-1.0.7/lib/eventmachine.rb:187:in `run'
Strange that I create only 4 actors, not 8, and my TERM, INT signals handler isn't be called.
#!/usr/bin/env ruby
require './config/environment'
opts = CommandlineOptions.new.to_h
iface = opts[:iface] || '0.0.0.0'
port = opts[:port] || 3000
App.logger.info('Starting communication server')
connections = Connections.new
local_inbox = LocalQueue.new
auth_server = AuthServer.new(connections, local_inbox)
inbox_service = InboxService.new('inbox', iface, port)
inbox_service.async.process_inbox(local_inbox) # <--------
remote_outbox_name = "outbox_#{iface}:#{port}"
outbox_service = OutboxService.new(connections)
outbox_service.async.subscribe(remote_outbox_name) # <--------
conn_server_opts = { host: iface, port: port }
conn_server_opts.merge!(auth_server.callbacks)
conn_server = ConnServer.new(conn_server_opts)
%W(INT TERM).each do |signal|
trap(signal) do
info("Shutting down...")
conn_server.stop
end
end
conn_server.start
Here InboxService is an actor which creates another actor - there are 2 actors, then OutboxService also creates one actor, so I got created 4 actors.
require 'redis'
require 'celluloid/current'
class InboxServiceActor
include Celluloid
def initialize(remote_inbox_name)
#remote_inbox_name = remote_inbox_name
create_redis_connection
end
def publish(full_msg)
#redis.publish(#remote_inbox_name, full_msg)
end
private
def create_redis_connection
#redis = Redis.new
end
end
require 'json'
require 'redis'
require 'celluloid/current'
class OutboxServiceActor
include Celluloid
include HasLoggerMethods
def initialize
create_redis_connection
end
def subscribe(remote_outbox_name, &block)
#redis.subscribe(remote_outbox_name) do |on|
on.message do |_channel, full_msg|
debug("Outbox message received: '#{full_msg}'")
hash = parse_msg(full_msg)
block.call(hash['signature'], hash['msg']) if message_valid?(hash)
end
end
end
private
def create_redis_connection
#redis = Redis.new
end
def parse_msg(full_msg)
JSON.parse(full_msg)
rescue JSON::ParserError
error('Outbox message JSON parse error')
nil
end
def message_valid?(msg)
msg.is_a?(Hash) && msg.key?('signature') && msg.key?('msg') ||
error('Invalid outbox message. Should '\
'contain "signature" and "msg" keys') && false
end
end
[2013-01-29 09:17:50] INFO WEBrick 1.3.1
[2013-01-29 09:17:50] INFO ruby 1.8.7 (2012-10-12) [i386-linux]
[2013-01-29 09:17:50] WARN TCPServer Error: Address already in use - bind(2)
[2013-01-29 09:17:50] INFO WEBrick::HTTPServer#start: pid=4107 port=8080
When I run the file attached below in linux I get the error described. I tried all possible command and strategies online to listen to processes (including rogue) and kill them. I did this in lots of ports. No luck.
As soon as I run the script in Mac OS and it works. Nevertheless I have to mount it on a server and clients have to communicate with it. It happens on every instance of amazon ec2 and on heroku. I have seen this error one too many times and spend many hours trying to fix it. I configured the security group of ec2 instances and still did not work. I am beyond desperate. At this point I have to think that the problem must be WEBrick itself or something in my code.
require 'webrick'
require 'uri'
require 'net/http'
$own_address = 8080
class AuctionInfo
# The representation is a hash mapping item names to [highest_bidder, highest_bid, end_time]
def initialize
#data = {}
end
def new_item(item, endTime)
#data[item] = ["UNKNOWN", 0, endTime]
end
def bid(item, bid, client)
if #data.has_key?(item)
endTime = #data[item][2]
if #data[item][1].to_i < bid.to_i and Time.new.to_i < endTime.to_i
#data[item] = [client, bid, endTime]
end
end
end
def get_status(item)
if #data.has_key?(item)
return #data[item][0]
end
end
def winner(item)
if #data.has_key?(item)
if #data[item][2].to_i + 1 <= Time.new.to_i
return #data[item][0]
else return "UNKNOWN"
end
end
end
def reset
#data = {}
end
def has_item(item)
return #data.has_key?(item)
end
def get_data
return {}.replace(#data)
end
end
class StartAuctionServlet < WEBrick::HTTPServlet::AbstractServlet
def initialize(server, data)
#data = data
end
def do_POST(request, response)
if request.query['name'] and request.query['end_time']
#data.new_item(request.query['name'], request.query['end_time'].to_i)
end
response.status = 200
end
alias_method :do_GET, :do_POST
end
class BidServlet < WEBrick::HTTPServlet::AbstractServlet
def initialize(server, data)
#data = data
end
def do_POST(request, response)
if request.query['name'] and request.query['client'] and request.query['bid']
#data.bid(request.query['name'], request.query['bid'].to_i, request.query['client'])
end
response.status = 200
end
alias_method :do_GET, :do_POST
end
class StatusServlet < WEBrick::HTTPServlet::AbstractServlet
def initialize(server, data)
#data = data
end
def do_GET(request, response)
if request.query['name']
response.body = #data.get_status(request.query['name'])
end
response.status = 200
end
alias_method :do_POST, :do_GET
end
class WinnerServlet < WEBrick::HTTPServlet::AbstractServlet
def initialize(server, data)
#data = data
end
def do_GET(request, response)
if request.query['name']
response.body = #data.winner(request.query['name'])
end
response.status = 200
end
alias_method :do_POST, :do_GET
end
class ResetServlet < WEBrick::HTTPServlet::AbstractServlet
def initialize(server, data)
#data = data
end
def do_POST(request, response)
#data.reset
response.status = 200
end
alias_method :do_GET, :do_POST
end
class RandomServlet < WEBrick::HTTPServlet::AbstractServlet
def initialize(server, data)
#data = data
end
def do_GET(request, response)
response.status = 200
response.body = #data.get_data.to_s
end
alias_method :do_POST, :do_GET
end
data = AuctionInfo.new
server = WEBrick::HTTPServer.new(:Port => $own_address)
server.mount '/start_auction', StartAuctionServlet, data
server.mount '/bid', BidServlet, data
server.mount '/status', StatusServlet, data
server.mount '/winner', WinnerServlet, data
server.mount '/rst', ResetServlet, data
server.mount '/', RandomServlet, data
trap("INT") { server.shutdown }
server.start
Have you checked whether the linux server is running apache, tomcat, trinidad or any other web server? Odds are one of them is already running on port 8080 on the server.
lsof is a useful command. Try lsof | grep 8080 and see whether anything shows up
I am writing a ruby script to be used as Postfix SMTP access policy delegation. The script needs to access a Tokyo Tyrant database. I am using EventMachine to take care of network connections. EventMachine needs a EventMachine::Connection class that is instantiated by EventMachineās processing loop whenever a new connection is created. so for each connection a class is instantiated and destroyed.
I am creating a connection to Tokyo Tyrant from the post_init of the EventMachine::Connection (ie right after connection is setup) and tearing it down after connection is terminated.
My question is if this is the proper way to connect to db? ie making a connection every yime I need it and tearing it down after I am finished? Wouldn't be better to connect to DB once (when program is started) tear it down during program shutdown? If that is so how should I code that ?
My code is:
require 'rubygems'
require 'eventmachine'
require 'rufus/tokyo/tyrant'
class LineCounter < EM::Connection
ActionAllow = "action=dunno\n\n"
def post_init
puts "Received a new connection"
#tokyo = Rufus::Tokyo::Tyrant.new('server', 1978)
#data_received = ""
end
def receive_data data
#data_received << data
#data_received.lines do |line|
key = line.split('=')[0]
value = line.split('=')[1]
#reverse_client_name = value.strip() if key == 'reverse_client_name'
#client_address = value.strip() if key == 'client_address'
#tokyo[#client_address] = #reverse_client_name
end
puts #client_address, #reverse_client_name
send_data ActionAllow
end
def unbind
#tokyo.close
end
end
EventMachine::run {
host,port = "127.0.0.1", 9997
EventMachine::start_server host, port, LineCounter
puts "Now accepting connections on address #{host}, port #{port}..."
EventMachine::add_periodic_timer( 10 ) { $stderr.write "*" }
}
with regards,
raj
Surprising there's no answers to this question.
What you probably need is a connection pool where you can fetch, use, and return connections as they are required.
class ConnectionPool
def initialize(&block)
#pool = [ ]
#generator = block
end
def fetch
#pool.shift or #generator and #generator.call
end
def release(handle)
#pool.push(handle)
end
def use
if (block_given?)
handle = fetch
yield(handle)
release(handle)
end
end
end
# Declare a pool with an appropriate connection generator
tokyo_pool = ConnectionPool.new do
Rufus::Tokyo::Tyrant.new('server', 1978)
end
# Fetch/Release cycle
tokyo = tokyo_pool.fetch
tokyo[#client_address] = #reverse_client_name
tokyo_pool.release(tokyo)
# Simple block-method for use
tokyo_pool.use do |tokyo|
tokyo[#client_address] = #reverse_client_name
end