I need some help. I have this code below and the logs tell me that I'm not passing the durable parameter, but as you can see I'm passing. I have tried everything and there is always the same mistake.
To mount this code I used these links:
https://github.com/sk2/ANK-NG/blob/master/rabbitmq/rabbit_websocket.py
https://pika.readthedocs.io/en/0.10.0/examples/tornado_consumer.html
websocket.py:
import pika
import tornado
import tornado.websocket as websocket
from tornado.options import options, define, parse_command_line
import tornado.httpserver
import tornado.ioloop
import tornado.wsgi
from pika.adapters.tornado_connection import TornadoConnection
from witbot import recebeInput
import logging
LOG_FORMAT = ('%(levelname) -10s %(asctime)s %(name) -30s %(funcName) '
'-35s %(lineno) -5d: %(message)s')
LOGGER = logging.getLogger(__name__)
class MyWebSocketHandler(websocket.WebSocketHandler):
def allow_draft76(self):
# for iOS 5.0 Safari
return True
def check_origin(self, origin):
return True
def open(self, *args, **kwargs):
self.application.pc.add_event_listener(self)
print ("WebSocket opened")
def on_close(self):
print ("WebSocket closed")
self.application.pc.remove_event_listener(self)
def on_message(self, message):
print (message)
self.application.pc.send_message(message)
class PikaClient(object):
def __init__(self, io_loop):
print ('PikaClient: __init__')
self.io_loop = io_loop
self.connected = False
self.connecting = False
self.connection = None
self.channel = None
self.event_listeners = set([])
def connect(self):
logging.basicConfig(level=logging.DEBUG)
if self.connecting:
print ('PikaClient: Already connecting to RabbitMQ')
return
print ('PikaClient: Connecting to RabbitMQ')
self.connecting = True
port = 5672
cred = pika.PlainCredentials(username='guest', password='guest')
param = pika.ConnectionParameters(host='192.168.99.100',port=port,credentials=cred,heartbeat_interval=5)
print(param)
self.connection = TornadoConnection(param,
on_open_callback=self.on_connected)
print(self.connection)
self.connection.add_on_close_callback(self.on_closed)
def on_connected(self, connection):
print ('PikaClient: connected to RabbitMQ')
self.connected = True
self.connection = connection
self.connection.channel(self.on_channel_open)
def on_channel_open(self, channel):
print ('PikaClient: Channel open, Declaring exchange')
self.channel = channel
self.setup_exchange('amq.topic')
def setup_exchange(self, exchange_name):
print('Declaring exchange %s', exchange_name)
self.channel.exchange_declare(self.on_exchange_declareok, exchange=exchange_name,exchange_type='topic',durable=True)
def on_exchange_declareok(self, unused_frame):
print('Exchange declared')
self.setup_queue('bot.commands')
def setup_queue(self, queue_name):
print('Declaring queue %s', queue_name)
self.channel.queue_declare(self.on_queue_declareok, queue_name)
def on_queue_declareok(self, method_frame):
print ('Binding %s to %s with %s', 'amq.topic', 'bot.commands', 'bot.commands')
self.channel.queue_bind(self.on_bindok, 'bot.commands',
'amq.topic', 'bot.commands')
def on_bindok(self, unused_frame):
print('Queue bound')
self.start_consuming()
def start_consuming(self):
print('Issuing consumer related RPC commands')
self._consumer_tag = self.channel.basic_consume(self.on_message,'bot.commands')
def on_closed(self, connection):
print ('PikaClient: rabbit connection closed')
self.io_loop.stop()
def on_message(self, channel, method, header, body):
print ('PikaClient: message received: %s' % body)
self.notify_listeners(body)
def send_message(self, body):
self.channel.basic_publish(exchange='topic',
routing_key='bot.commands',
body=body)
def notify_listeners(self, body):
bot = recebeInput.decisoes(recebeInput.devolveEntidade(recebeInput.trataJson(body)))
for listener in self.event_listeners:
listener.write_message(bot)
print ('PikaClient: notified %s' % repr(listener))
def add_event_listener(self, listener):
print ("added listener")
self.event_listeners.add(listener)
print ('PikaClient: listener %s added' % repr(listener))
def remove_event_listener(self, listener):
try:
self.event_listeners.remove(listener)
print ('PikaClient: listener %s removed' % repr(listener))
except KeyError:
pass
def main():
parse_command_line()
aplication = tornado.web.Application([
(r'/ws', MyWebSocketHandler)
])
# server = tornado.httpserver.HTTPServer(aplication)
io_loop = tornado.ioloop.IOLoop.instance()
# PikaClient is our rabbitmq consumer
pc = PikaClient(io_loop)
aplication.pc = pc
aplication.pc.connect()
aplication.listen(8081)
io_loop.start()
if __name__ == '__main__':
main()
Error log:
websocket_1 | PikaClient: connected to RabbitMQ
websocket_1 | PikaClient: Channel open, Declaring exchange
websocket_1 | Declaring exchange %s amq.topic
websocket_1 | Exchange declared
websocket_1 | Declaring queue %s bot.commands
rabbit1_1 | 2018-07-17 15:48:18.792 [info] <0.6936.0> connection
<0.6936.0> (172.18.0.1:37556 -> 172.18.0.6:5672): user 'guest' authenticated and granted access to vhost '/'
websocket_1 | [W 180717 15:48:18 channel:1034] Received remote Channel.Close (406): "PRECONDITION_FAILED - inequivalent arg 'durable' for queue 'bot.commands' in vhost '/': received 'false' but current is 'true'" on <Channel number=1 OPEN conn=<TornadoConnection OPEN socket=('172.18.0.2', 47986)->('192.168.99.100', 5672) params=<ConnectionParameters host=192.168.99.100 port=5672 virtual_host=/ ssl=False>>>
websocket_1 | [I 180717 15:48:24 web:2162] 101 GET /ws (192.168.99.1) 1.29ms
websocket_1 | added listener
websocket_1 | PikaClient: listener <__main__.MyWebSocketHandler object at 0x7f483b277cf8> added
websocket_1 | WebSocket opened
Perhaps the queue already exists and is durable so when your library attempts to subscribe to it and sees a durable it throws an error because it is expecting a non-durable queue
For me, the queue exists. I deleted it by rabbitmqadmin delete queue name=name_of_queue and run program again
As you are using nodeJs amqp lib and you faced with the same issue, try to specify options when specifying queue :
connection.queue('your_queue_name',{'durable':true}, function (q) {
// Catch all messages
q.bind('#');
// Receive messages
q.subscribe(function (message) {
// Print messages to stdout
console.log(message);
});
});
Related
I used the Gemini websocket code for a BTC feed but a callback error keeps popping up. However, it occasionally does run the code correctly. I have used 'price' and 'quantity' to test the code before I use the actual variables I want.
Error from running my gemini websocket code
Here is the code:
import websocket, ssl, json
import _thread as thread
websocket._logging._logger.level = -99
bestbid = {}
bestask = {}
top_of_book = 0
def on_message(self, message):
global bestbid, bestask
bestbid = json.loads(message)
bestask = json.loads(message)
print("Message received!")
print("{} {}".format(bestbid["price"], bestask ["quantity"]))
def on_error(self, error):
print(error)
def on_close(self):
print("Connection closed!")
def on_open(self):
print("Connection opened!")
def run(*args):
ws.send(logon_msg)
thread.start_new_thread(run, ())
if __name__ == "__main__":
logon_msg = '{"type": "subscribe","subscriptions":[{"name":"l2","symbols":["BTCUSD"]}]}'
websocket.enableTrace(True)
ws = websocket.WebSocketApp("wss://api.gemini.com/v2/marketdata/",
on_message = on_message,
on_error = on_error,
on_close = on_close,
on_open = on_open)
ws.on_open = on_open
ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
You should check that bestbid (bestbuy) JSON object has fields "price" and quantity". Errors most probably are caused by heartbeat or some diagnostic messages.
Hi I created async websocket client which I can receive and send messages asynchronously.
Here is the clint class that I use.
import websockets
import asyncio
from models import Heartbeat
from model_helper import ModelHelper
from json_helper import JSONHelper
from os.path import dirname, abspath
import logging
import time
waiting_time = 0.5
class WebSocketClient():
def __init__(self, websocket_queue, configs):
self.Heartbeat = Heartbeat().message
self.websocket_queue = websocket_queue
self.configs = configs
def get_connection_string(self):
server_url = self.configs.URL + ":" + str(self.configs.Port) + self.configs.Root
return server_url
async def connect(self):
try:
server_url = self.get_connection_string()
self.connection = await websockets.client.connect(server_url)
if self.connection.open:
print("Connection stablished. Client correcly connected")
# Send greeting
await self.connection.send(self.Heartbeat)
return self.connection
else:
print("Can not connect")
except ConnectionRefusedError as err:
print("Connection Error: {}".format(err))
async def send_message_to_socket(self, connection):
while True:
message = self.websocket_queue.get_send_queue()
try:
if message is not None:
message_ = ModelHelper.to_outgoing_request_model(message)
await connection.send(message_)
else:
await asyncio.sleep(waiting_time)
except websockets.exceptions.ConnectionClosed:
print('Connection with server closed')
self.connect()
async def receive_message_from_socket(self, connection):
while True:
try:
message = await connection.recv()
obj = JSONHelper.toObject(message)
print("Received object from websocket: {}".format(obj))
#If a websocket entry has SendMessage in its Action property
#Consider it as an sms content to be sent.
if(obj.Action == "SendMessage"):
self.websocket_queue.add_received_queue(obj)
except websockets.exceptions.ConnectionClosed:
print('Connection with server closed1')
async def send_heartbeat_to_socket(self, connection):
while True:
#print("Heartbeat loop\n")
try:
await connection.send(self.Heartbeat)
await asyncio.sleep(3)
except websockets.exceptions.ConnectionClosed:
print('Connection with server closed2')
And this is the code where I listen for websocket messages:
def listen_websocket_routine(sms_queue, websocket_queue):
while True:
time.sleep(waiting_time)
#Check Websocket queue for new messages
message = websocket_queue.get_received_queue()
# If Incoming websocket JSON has SendMessage string in its Action attribute transform it to request_model
if message is not None and send_sms.Action == "SendMessage":
# Transform it to outgoing_sms_model
reset = ModelHelper.to_outgoing_sms_model(message.Data)
# Add To send_queue of sms
sms_queue.add_send_queue(reset)
finally How I initiate them with asyncio and threading.
client = WebSocketClient(websocket_queue, configs)
loop = asyncio.get_event_loop()
connection = loop.run_until_complete(client.connect())
task1 = asyncio.ensure_future(client.receive_message_from_socket(connection))
task2 = asyncio.ensure_future(client.send_heartbeat_to_socket(connection))
task3 = asyncio.ensure_future(client.send_message_to_socket(connection))
listen_websocket_thread = threading.Thread(target=listen_websocket_routine, args=(sms_queue, websocket_queue))
listen_websocket_thread.start()
loop.run_forever()
So my question is, whenever connetion breaks, I need to re establish the connection. But I am not sure where should I do that. Should it be before everytime I try to send message or receive or should I do that in more general approach?
Since update 10.0 you could do it with an async iterator, like this(example from the official doc):
async for websocket in websockets.connect(...):
try:
...
except websockets.ConnectionClosed:
continue
My python program work so slow, because make socket reconnect for any request. I want make one connect and send request, with out reconnect
My functions in file send_by_socket.py, some other function and class call send_to_socket for send logmessage. Now it work, but very slow. Reason - make new connect for any message. I want single connection or poll for use it without reconnect. How make it, possibel have good example with sourcecode?
import asyncio
import websockets
from logging import StreamHandler
import json
async def async_send(message):
async with websockets.connect('wss://****.com/chat') as web_socket:
await web_socket.send(message)
class WebSocketHandler(StreamHandler):
def __init__(self):
StreamHandler.__init__(self)
def emit(self, record):
msg = json.dumps({'log': {'message': record.message, 'date': record.asctime, 'level': record.levelname}})
try:
asyncio.get_event_loop().run_until_complete(async_send(msg))
except ConnectionRefusedError:
pass
def send_to_socket(msg_dict):
msg = json.dumps(msg_dict)
try:
asyncio.get_event_loop().run_until_complete(async_send(msg))
except ConnectionRefusedError:
pass
Now program spend about 1 - 1.2 sec for request. I try
con = websockets.connect('wss://****.com/chat')
con.send('some thing')
but have error AttributeError: 'Connect' object has no attribute 'send'
python
import asyncio
import websockets
from logging import StreamHandler
import json
import time
def singleton(cls):
instances = {}
def getinstance():
if cls not in instances:
instances[cls] = cls()
return instances[cls]
return getinstance
#singleton
class SendToWebSocket:
"""
Send message in web-socket, use one connection for sending.
Try make new connection, if old is lost.
"""
__ws = None
__url = "wss://***.com/chat"
def __init__(self):
self.retryTime = 0
self.retryRepeat = 30
self.__create_connect()
#asyncio.coroutine
def __create_connect(self):
if (time.time() - self.retryTime) > self.retryRepeat:
try:
self.__ws = yield from websockets.connect(self.__url)
self.retryTime = 0
except ConnectionRefusedError:
self.retryTime = time.time()
def send(self, message):
t = type(message)
if t is dict:
msg = json.dumps(message)
elif t is str:
msg = message
else:
raise ValueError("Message must be str or dict. Received %s" % type(t))
if self.__ws is not None:
try:
asyncio.get_event_loop().run_until_complete(self.__async_send(msg))
# print('Send normal')
except ConnectionRefusedError:
# print("Can't send")
# try recreate connect
self.__create_connect()
else:
asyncio.get_event_loop().run_until_complete(self.__create_connect())
async def __async_send(self, message):
await self.__ws.send(message)
class WebSocketHandler(StreamHandler):
"""Custom handler for logging library"""
def __init__(self):
StreamHandler.__init__(self)
self.web_socket = SendToWebSocket()
def emit(self, record):
msg = json.dumps({'log': {'message': record.message, 'date': record.asctime, 'level': record.levelname}})
try:
self.web_socket.send(msg)
except ConnectionRefusedError:
pass
I want to send data through websockets as soon as a client is connected.
The Data is at an other place then the Websocket Handler. How can i get the data to the client ?
The server should hold the loop and the Handler. In the connector i connect to a tcp socket to get the data out of some hardware. I expect to have not more then 6 Websockets open once a time. The Data comes as a stream out of the TCP socket.
server.py
import os
from tornado import web, websocket
import asyncio
import connector
class StaticFileHandler(web.RequestHandler):
def set_default_headers(self):
self.set_header("Access-Control-Allow-Origin", "*")
def get(self):
self.render('index.html')
class WSHandler(websocket.WebSocketHandler):
def open(self):
print('new connection')
self.write_message("connected")
def on_message(self, message):
print('message received %s' % message)
self.write_message("pong")
def on_close(self):
print('connection closed')
public_root = 'web_src'
handlers = [
(r'/', StaticFileHandler),
(r'/ws', WSHandler),
]
settings = dict(
template_path = os.path.join(os.path.dirname(__file__), public_root),
static_path = os.path.join(os.path.dirname(__file__), public_root),
debug = True
)
app = web.Application(handlers, **settings)
sensorIP = "xxx.xxx.xxx.xxx"
if __name__ == "__main__":
app.listen(8888)
asyncio.ensure_future(connector.main_task(sensorIP))
asyncio.get_event_loop().run_forever()
connector.py
import yaml
import asyncio
class RAMReceiver:
def __init__(self, reader):
self.reader = reader
self.remote_data = None
self.initParams = None
async def work(self):
i = 0
while True:
data = await self.reader.readuntil(b"\0")
self.remote_data = yaml.load(data[:-1].decode("utf-8",
"backslashreplace"))
# here i want to emit some data
# send self.remote_data to websockets
if i == 0:
i += 1
self.initParams = self.remote_data
# here i want to emit some data after open event is
# triggered
# send self.initParams as soon as a client has connected
async def main_task(host):
tasks = []
(ram_reader,) = await asyncio.gather(asyncio.open_connection(host,
51000))
receiver = RAMReceiver(ram_reader[0])
tasks.append(receiver.work())
while True:
await asyncio.gather(*tasks)
You can use Tornado's add_callback function to call a method on your websocket handler to send the messages.
Here's an example:
1. Create an additional method on your websocket handler which will receive a message from connector.py and will send to connected clients:
# server.py
class WSHandler(websocket.WebSocketHandler):
# make it a classmethod so that
# it can be accessed directly
# from class without `self`
#classmethod
async def send_data(cls, data):
# write your code for sending data to client
2. Pass the currently running IOLoop and WSHandler.send_data to your connector.py:
# server.py
from tornado import ioloop
...
if __name__ == "__main__":
...
io_loop = ioloop.IOLoop.current() # current IOLoop
callback = WSHandler.send_data
# pass io_loop and callback to main_task
asyncio.ensure_future(connector.main_task(sensorIP, io_loop, callback))
...
3. Then modify main_task function in connector.py to receive io_loop and callback. Then pass io_loop and callback to RAMReceiver.
4. Finally, use io_loop.add_callback to call WSHandler.send_data:
class RAMReceiver:
def __init__(self, reader, io_loop, callback):
...
self.io_loop = io_loop
self.callback = callback
async def work(self):
...
data = "Some data"
self.io_loop.add_callback(self.callback, data)
...
The HandlerWebsockets does work fine and is just replying with what has been send at the moment throught messageToSockets(msg). However both tries to send messages to the websocket from the coroutine of the web application are not working. Looks like everything is blocked by these attempts...
class webApplication(tornado.web.Application):
def __init__(self):
handlers = [
(r'/', HandlerIndexPage),
(r'/websocket', HandlerWebSocket, dict(msg='start')),
]
settings = {
'template_path': 'templates'
}
tornado.web.Application.__init__(self, handlers, **settings)
#gen.coroutine
def generateMessageToSockets(self):
while True:
msg = str(randint(0, 100))
print ('new messageToCon: ', msg)
yield [con.write_message(msg) for con in HandlerWebSocket.connections]
yield gen.sleep(1.0)
if __name__ == '__main__':
ws_app = webApplication()
server = tornado.httpserver.HTTPServer(ws_app)
port = 9090
print('Listening on port:' + str(port))
server.listen(port)
IOLoop.current().spawn_callback(webApplication.generateMessageToSockets)
IOLoop.current().set_blocking_log_threshold(0.5)
IOLoop.instance().start()
Here the WebSockets Handler
class HandlerWebSocket(tornado.websocket.WebSocketHandler):
connections = set()
def initialize(self, msg):
print('HWS:' + msg)
def messageToSockets(self, msg):
print ('return message: ', msg)
[con.write_message(msg) for con in self.connections]
def open(self):
self.connections.add(self)
print ('new connection was opened')
pass
def on_message(self, message):
print ('from WebSocket: ', message)
self.messageToSockets(message)
def on_close(self):
self.connections.remove(self)
print ('connection closed')
pass
I am a bit lost in the examples, questions here, documentation etc. So any hint how to properly start a continous calling websocket routine is greatly appreciated
generateMessageToSockets will loop endlessly, generating messages as fast as it can without waiting for those messages to be sent. Since it starts first and never yields, the HTTPServer will never actually be able to accept a connection.
If you really want to send messages as fast as you can, the minimal solution without blocking would be
yield [con.write_message(msg) for con in HandlerWebSocket.connections]
yield gen.moment
But it would probably be better to use gen.sleep to send messages at regular intervals, instead of "as fast as possible".
unfortunately all the gen.routines tries didn't work for me. Moved back to threads
def generateMessageToSockets():
while True:
msg = str(randint(0, 100))
print ('new messageToCon: ', msg)
[con.write_message(msg) for con in HandlerWebSocket.connections]
sleep(1.0)
class WebApplication(tornado.web.Application):
def __init__(self):
handlers = [
(r'/', HandlerIndexPage),
(r'/websocket', HandlerWebSocket, dict(msg='start')),
]
settings = {
'template_path': 'templates'
}
tornado.web.Application.__init__(self, handlers, **settings)
if __name__ == '__main__':
tGenarate = threading.Thread(target=generateMessageToSockets)
tGenarate.start()
ws_app = WebApplication()
server = tornado.httpserver.HTTPServer(ws_app)
port = 9090
print('Listening on port:' + str(port))
server.listen(port)
ioloop.IOLoop.instance().start()
which works