Can't use Vertx EventBus from official code example on Vertx.io - ruby

I have 2 very simple Vertx verticles written in Ruby:
### sender.rb
require 'vertx/vertx'
require 'date'
vertx = Vertx::Vertx.vertx()
event_bus = vertx.event_bus()
vertx.set_periodic(2000) { |v|
msg = "Ruby NOW " + DateTime.now.strftime("%H:%M:%S")
puts(msg)
event_bus.publish("news.uk.sport", msg)
}
and
### listener.rb
require 'vertx/vertx'
vertx = Vertx::Vertx.vertx()
event_bus = vertx.event_bus()
consumer = event_bus.consumer("news.uk.sport")
consumer.handler() { |message|
puts "I have received a message: #{message.body()}"
}
consumer.completion_handler() { |res_err,res|
if (res_err == nil)
puts "The handler registration has reached all nodes"
else
puts "Registration failed!"
end
}
I have constructed these two verticles exactly from the code examples from vertx.io documentation.
I open 2 terminal sessions.
In 1st terminal I deploy the sender.rb verticle:
$ vertx run sender.rb
in 2nd terminal i deploy listener.rb verticle:
$ vertx run listener.rb
The sender sends and prints messages, but listener, doesn't receive anything (no print output from listener.rb)
I have tried to run vertx with flags
-ha
and
-cluster
but it didn't help. Please help.
UPDATE
Many thanks to Tsegismont
I have re-implemented both the listener.rb and the sender.rb and it worked :)
Now i just have little question purely out of curiosity:
In my sender:
simple_eventbus git:(master) ✗ vertx run sender.rb -cluster
Starting clustering...
No cluster-host specified so using address 192.168.7.179
Thread Thread[vert.x-eventloop-thread-2,5,main] has been blocked for 2071 ms, time limit is 2000
Thread Thread[vert.x-eventloop-thread-2,5,main] has been blocked for 3073 ms, time limit is 2000
You're already on a Vert.x context, are you sure you want to create a new Vertx instance?
Succeeded in deploying verticle
On Sender site: We now have a clustered event bus: #<Vertx::EventBus:0x61deddf7>
The message has been sent: 13:20:13
The message has been sent: 13:20:15
The message has been sent: 13:20:17
The sender.rb is:
require 'vertx/vertx'
require 'date'
options = {
}
Vertx::Vertx.clustered_vertx(options) { |res_err,res|
if (res_err == nil)
vertx = res
event_bus = vertx.event_bus()
puts "On Sender site: We now have a clustered event bus: #{event_bus}"
vertx.set_periodic(2000) { |v|
msg = "Message from Sender: ruby message NOW " + DateTime.now.strftime("%H:%M:%S")
puts "The message has been sent: " + DateTime.now.strftime("%H:%M:%S")
event_bus.publish("news.uk.sport", msg)
}
else
puts "Failed: #{res_err}"
end
}
and my listener.rb output:
simple_eventbus git:(master) ✗ vertx run listener.rb -cluster
Starting clustering...
No cluster-host specified so using address 192.168.7.179
Thread Thread[vert.x-eventloop-thread-2,5,main] has been blocked for 2169 ms, time limit is 2000
Thread Thread[vert.x-eventloop-thread-2,5,main] has been blocked for 3172 ms, time limit is 2000
You're already on a Vert.x context, are you sure you want to create a new Vertx instance?
Succeeded in deploying verticle
On listener side: We now have a clustered event bus: #<Vertx::EventBus:0x44af7bbf>
The handler registration has reached all nodes
I have received a message: Message from Sender: ruby message NOW 13:20:13
I have received a message: Message from Sender: ruby message NOW 13:20:15
I have received a message: Message from Sender: ruby message NOW 13:20:17
The listener.rb code:
require 'vertx/vertx'
options = {
}
Vertx::Vertx.clustered_vertx(options) { |res_err,res|
if (res_err == nil)
vertx = res
event_bus = vertx.event_bus()
puts "On listener side: We now have a clustered event bus: #{event_bus}"
consumer = event_bus.consumer("news.uk.sport")
consumer.handler() { |message|
puts "I have received a message: #{message.body()}"
}
consumer.completion_handler() { |res_err,res|
if (res_err == nil)
puts "The handler registration has reached all nodes"
else
puts "Registration failed!"
end
}
else
puts "Failed: #{res_err}"
end
}
In my sender.rb the eventBus object assigned is:
#<Vertx::EventBus:0x61deddf7>
In my listener.rb it is different:
#<Vertx::EventBus:0x44af7bbf>
Is it just the object instance reference?
Doesn't have to be the same object instance to be shared?
And another question is why does it tell me onDeploy this?
Thread Thread[vert.x-eventloop-thread-2,5,main] has been blocked for 3073 ms, time limit is 2000
You're already on a Vert.x context, are you sure you want to create a new Vertx instance?
I have run the code once: vertx run listener.rb -cluster
Why does it tell me that I am already in Vertx context?

The problem is that your code creates a standalone Vert.x instance. To create a clustered Vert.x instance, follow this example:
require 'vertx/vertx'
options = {
}
Vertx::Vertx.clustered_vertx(options) { |res_err,res|
if (res_err == nil)
vertx = res
eventBus = vertx.event_bus()
puts "We now have a clustered event bus: #{eventBus}"
else
puts "Failed: #{res_err}"
end
}
from the Vert.x Event Bus clustering documentation section.

Related

Voila, jupyter and websockets: how to print?

Although the question might seem simple I can't see to find a viable way or anyway of printing the incoming messages from a threaded websocket.
Basically, I've created a jupyterlab notebook that lets me connect to a local websocket server and echo messages sent from a firecamp websocket connection. When running it on a cell (without the run button and run A.start()) I can see the prints but as soon as I hit the run button after restarting the kernal I can't see incoming messages.
Normally I would expect something like:
Function started
Someone said: test 1
Someone said: test 2
In the prints but nothing seems to apperas when hitting the run button.
The main objective is to be able to run the notebook with voila to upload to heroku but I can´t seem to make the prints work. If anybody has a clue or a better idea, I'm all ears.
Thanks in advance.
PD: Code
import ipywidgets as widgets
from IPython.display import Javascript, display
import websocket
import asyncio
import nest_asyncio
import threading
import websocket
import time
import sys
import trace
import logging
from time import sleep
output_box = widgets.Output()
class KThread(threading.Thread):
"""A subclass of threading.Thread, with a kill() method."""
def __init__(self, *args, **keywords):
threading.Thread.__init__(self, *args, **keywords)
self.killed = False
def start(self):
"""Start the thread."""
self.__run_backup = self.run
self.run = self.__run
threading.Thread.start(self)
def __run(self):
"""Hacked run function, which installs the trace."""
sys.settrace(self.globaltrace)
self.__run_backup()
self.run = self.__run_backup
def globaltrace(self, frame, why, arg):
if why == 'call':
return self.localtrace
else:
return None
def localtrace(self, frame, why, arg):
if self.killed:
if why == 'line':
raise SystemExit()
return self.localtrace
def kill(self):
ws.close()
self.killed = True
def on_message(ws, message):
print(message)
def on_open(ws):
ws.send("Connected Test")
def on_close(ws, close_status_code, close_msg):
print("### closed ###")
def on_error(ws, error):
print(error)
#This illustrates running a function in a separate thread. The thread is killed before the function finishes.
def func():
print('Function started')
ws.run_forever()
ws = websocket.WebSocketApp("ws://localhost:7890", on_open=on_open,on_message = on_message, on_close = on_close,on_error = on_error)
A = KThread(target=func)
websocket.enableTrace(True)
run_button = widgets.Button(
description='Run Button',
disabled=False,
button_style='info', # 'success', 'info', 'warning', 'danger' or ''
tooltip='Run button function',
icon='play'
)
def on_run_button_clicked(b):
with output_box:
A.start()
run_button.on_click(on_run_button_clicked)
display(run_button,output_box)
This is the websocket server:
# Importing the relevant libraries
import websockets
import asyncio
# Server data
PORT = 7890
print("Server listening on Port " + str(PORT))
# A set of connected ws clients
connected = set()
# The main behavior function for this server
async def echo(websocket, path):
print("A client just connected")
# Store a copy of the connected client
print(websocket)
connected.add(websocket)
# Handle incoming messages
try:
async for message in websocket:
print("Received message from client: " + message)
# Send a response to all connected clients except sender
for conn in connected:
if conn != websocket:
await conn.send("Someone said: " + message)
# Handle disconnecting clients
except websockets.exceptions.ConnectionClosed as e:
print("A client just disconnected")
finally:
connected.remove(websocket)
# Start the server
start_server = websockets.serve(echo, "localhost", PORT)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

ruby server - issue doing if then else checks on imcoming data from client

Here is my code (and below it is my issue):
require "socket"
server = TCPServer.open("localhost", 2000)
loop {
thread.start(server.accept) do |nodervcr|
msg = nodervcr.gets
puts(msg)
if msg = "codeword"
puts("codeword!")
else
puts("not codeword")
# Note this part works: it sends the server a message and it displays it
# You would think a simple if then else statement could redirect it according to the imcoming message from the client; which is my issue.
end
}
So I tried:
if msg == code
# then do this
elsif msg == code2
# then do this
# etc
But it's not working.
I've tried replacing msg with nodervcr, still nothing.
The strange part is that it's obviously getting the message, and msg does = what the client sends.. but it acts as if that variable dies immediately. I'm new to ruby. please help thanks.
You have a few problems in your code. But the main problem is that when you use something like Telnet, you probally are giving a enter.
So your sending "codeword" + CRLF (codeword\r\n). Thats not the same as your check: msg == "codeword"
What you should do is strip the input from these chars (strip). It removes the CRLF and it then works
Remember. Variable Assignments are = (single) comparing is double ==
Below the working code:
require "socket"
server = TCPServer.open("127.0.0.1", 2000)
loop {
Thread.start(server.accept) do |nodervcr| # Thread, not thread
msg = nodervcr.gets
# puts(msg)
p msg # use this so see all the invisiable chars
if msg.strip == "codeword" # stip the input
puts("codeword!")
else
puts("not codeword")
end
# Note this part works: it sends the server a message and it displays it
# You would think a simple if then else statement could redirect it according to the imcoming message from the client; which is my issue.
end
}

copas looping issue while connecting the server

I am new to lua programming and trying to implement Lua-websocket in openwrt as a client. here is the library.
Was trying to use client copas library but the issue is the script is getting stopped listening server after executing once (i.e. connecting to server, receiving message, sending message). I want the script to always listen the server without any timeout or script halt.
Below is the script
local copas = require'copas'
local websocket = require'websocket'
local json = require('json')
local client = require 'websocket.client'.new()
local ok,err = client:connect('ws://192.168.1.250:8080')
if not ok then
print('could not connect',err)
end
local ap_mac = { command = 'subscribe', channel = 'test' }
local ok = client:send(json.encode(ap_mac))
if ok then
print('msg sent')
else
print('connection closed')
end
local message,opcode = client:receive()
if message then
print('msg',message,opcode)
else
print('connection closed')
end
local replymessage = { command = 'message', message = 'TEST' }
local ok = client:send(json.encode(replymessage))
if ok then
print('msg sent')
else
print('connection closed')
end
copas.loop()
Here copas.loop() is not working.
On openWrt I had installed lua 5.1
Short answer: you do not use Copas correctly.
In detail: copas.loop does nothing, because you have neither created a Copas server, nor a Copas thread. Check the Copas documentation.
The send and receive actions in your script are performed outside Copas, because they are not within a Copas.addthread (function () ... end). You also create a websocket client that is not a copas one, but a synchronous one (the default). Check the lua-websocket documentation and its examples.
The solution:
local copas = require'copas'
local websocket = require'websocket'
local json = require'cjson'
local function loop (client)
while client.state == "OPEN" do
local message, opcode = client:receive()
... -- handle message
local replymessage = { command = 'message', message = 'TEST' }
local ok, err = client:send(json.encode(replymessage))
... -- check ok, err
end
end
local function init ()
local client = websocket.client.copas ()
local ok,err = client:connect('ws://192.168.1.250:8080')
... -- check ok, err
local ap_mac = { command = 'subscribe', channel = 'test' }
ok, err = client:send(json.encode(ap_mac))
... -- check ok, err
copas.addthread (function ()
loop (client)
end)
end
copas.addthread (init)
copas.loop()
The init function instantiates a client for Copas. It also starts the main loop in a Copas thread, that waits for incoming messages as long as the connection is open.
Before starting the Copas loop, do not forget to add a Copas thread for the init function.

Ruby UNIXServer with Node.js client

I've this Ruby server that uses a Unix Socket:
require 'socket'
server = UNIXServer.new('/tmp/ciccio.sock')
loop do
sock = server.accept
loop do
begin
data = sock.recv(1024)
data = "DO SOMETHING -> #{data}"
sock.write(data)
sock.flush
rescue Errno::EPIPE, Errno::ENOTCONN
break
end
end
end
And I've this client in JavaScript that uses node.js net api:
Net = require('net');
var client = Net.connect({path: '/tmp/ciccio.sock'}, () => {
console.log('write data');
client.write('hello world!');
});
client.on('data', (data) => {
console.log(data.toString());
client.end();
});
client.on('end', () => {
console.log('end');
});
client.on('error', (error) => {
console.log(error.toString());
});
The problem is that at the first iteration of server loop, recv receives the right string and the server replies to the client with the string data. But at the second iteration recv receives empty data and so a infinite loop begins... server continues to receive empty data and recv does not block...
If I use a ruby client like this all works fine...
require 'socket'
sock = UNIXSocket.new('/tmp/ciccio.sock')
loop do
sock.write('hello world!')
data = sock.recv(1024)
puts data
sleep(10)
end
sock.close
Your client closes the connection upon connecting, writing "hello world", and then receiving a response. Now your server is calling recv on a socket that has been closed, which will return an empty string. Since you're looping to read on this socket indefinitely, it will never return control back to the outer loop to call accept for the second client. The ruby version of your client works because it never closes the connection to the server.
Breaking out of your inner loop after receiving an empty string should get you what you want,
require 'socket'
server = UNIXServer.new('/tmp/ciccio.sock')
loop do
sock = server.accept
loop do
begin
data = sock.recv(1024)
break if data.empty?
data = "DO SOMETHING -> #{data}"
sock.write(data)
sock.flush
rescue Errno::EPIPE, Errno::ENOTCONN
break
end
end
end
Another potential problem is that your server can only service a single connection to a single client, as the inner loop blocks any subsequent calls to accept until the current connection ends.

Get just one message from rabbitmq queue and unsubscribe

I am using RabbitMq for communication, and I would like to consume just one message and unsubscribe. How to do it in ruby bunny? My subscribe block is pretty easy:
queue.subscribe(block: true) do |delivery_info, properties, payload|
puts "[consumer] #{q.name} received a message: #{payload}"
end
You probably already figured it out by now, but for anyone else...
According to the documentation, you can just use basic_get. For example,
conn = Bunny.new
conn.start
ch = conn.create_channel
delivery_info, properties, message = ch.basic_get("your_queue_name", :ack => true)
ch.acknowledge(delivery_info.delivery_tag)
Another way could be using #pop method of queue.
conn = Bunny.new
conn.start
ch = conn.create_channel
q = ch.queue("queue_name")
delivery_info, properties, payload = q.pop
You can find more details about it here

Resources