Context
I am getting high memory usage with the vertx circuit breaker. I am just using httpbin.org to get all success responses. For individual requests, it works fine. While running a load test the JVM old gen utilization is spiking up.
Reproducer
I have the main verticle code, pasting it here itself:
public class CleanServer extends AbstractVerticle {
Logger logger = Logger.getLogger(CleanServer.class.getName());
#Override
public void start(Promise<Void> startPromise) throws Exception {
Router router = Router.router(vertx);
CircuitBreakerCache cbc = new CircuitBreakerCache(vertx);
router.route(HttpMethod.GET, "/get").handler(context -> {
List<String> domains = context.queryParam("user");
String domain = domains.get(0);
CircuitBreaker cb = cbc.getCircuitBreaker(domain + context.request().path());
HttpServerResponse serverResponse =
context.response().setChunked(true);
cb.executeWithFallback(promise -> {
WebClientOptions options = new WebClientOptions().setTryUseCompression(true).setTcpNoDelay(true).setTcpCork(true).setReceiveBufferSize(128).setConnectTimeout(400);
WebClient client = WebClient.create(vertx, options);
client.get(80, "httpbin.org", "/status/200")
.timeout(2000)
.send(ar -> {
if (ar.succeeded()) {
HttpResponse<Buffer> response = ar.result();
int statusCode = response.statusCode();
if (statusCode != 200) {
promise.fail(response.statusMessage());
} else {
serverResponse.end("Hello!!");
promise.complete();
}
} else {
promise.fail(ar.cause().getMessage());
}
});
}, v -> {
// Executed when the circuit is opened
logger.log(Level.INFO, domain + " Failed " + cb.state().toString() + " Error: Circuit open");
serverResponse.setStatusCode(200).setStatusMessage("Circuit Open").end("Circuit Open");
return context;
});
});
// Create the HTTP server
vertx.createHttpServer(new HttpServerOptions().setMaxInitialLineLength(10000))
// Handle every request using the router
.requestHandler(router)
// Start listening
.listen(8080)
// Print the port
.onSuccess(server ->
System.out.println(
"HTTP server started on port " + server.actualPort()
)
);
}
}
Circuit breaker options:
CircuitBreakerOptions()
.setMaxFailures(50)
.setTimeout(5000)
.setFallbackOnFailure(true)
.setResetTimeout(10000)));
Steps to reproduce
API used: http://localhost:8080/get?user=abc
When I hit the above API at 50 QPS for 30 minutes. The java heap is getting filled up.
Extra
<vertx.version>4.2.6</vertx.version>
JVM params used:
-XX:+UseG1GC -Xms4g -Xmx4g -XX:InitiatingHeapOccupancyPercent=70 -XX:MaxGCPauseMillis=200 -XX:ParallelGCThreads=20 -XX:ConcGCThreads=5
JVM memory with the load test.
Error:
WARNING: Thread Thread[vert.x-eventloop-thread-3,5,main] has been blocked for 3050 ms, time limit is 2000 ms
I think I am blocking the thread somewhere but not sure where exactly as the code seems pretty simple as given in the documentation.
Related
Hello I'm currently implementing a websocket component to my very basic site. I'm running .NET Core 3.1 HTTP Listener for serving html, I've been stumped by implementing websockets.
I've worked with TCP in C# before and understand the flow of everything but websockets are a new thing to me. Here is the C# code for accepting websockets
[Route("/socket", "GET")]
public static async Task upgrade(HttpListenerContext c)
{
if (!c.Request.IsWebSocketRequest)
{
c.Response.StatusCode = 400;
c.Response.Close();
return;
}
try
{
var sock = (await c.AcceptWebSocketAsync(null)).WebSocket;
byte[] buff = new byte[1024];
var r = await sock.ReceiveAsync(buff, System.Threading.CancellationToken.None);
}
catch (Exception x)
{
Console.WriteLine($"Got exception: {x}");
}
//WebSocketHandler.AddSocket(sock);
}
I've added var r = await sock.ReceiveAsync(buff, System.Threading.CancellationToken.None); to this function because originally I was getting the exception in my WebSocketHandler class, so I moved the code to the one function to test.
Here is the client:
<script>
let socket = new WebSocket("ws://localhost:3000/socket");
socket.onopen = function (event) {
console.log("[ OPENED ] - Opened Websocket!");
};
socket.onclose = function (event) {
console.log("[ CLOSED ] - Socket closed");
};
socket.onerror = function (error) {
console.log("[ ERROR ] - Got websocket error:");
console.error(error);
};
socket.onmessage = function (event) {
// This function will be responsible for handling events
console.log("[ MESSAGE ] - Message received: ");
const content = JSON.parse(event.data);
console.log(content);
};
</script>
Here is the output in the console for the client:
Navigated to http://127.0.0.1:5500/index.html
index.html:556 [ OPENED ] - Opened Websocket!
index.html:569 [ CLOSED ] - Socket closed
And here is the exception from the C# server:
Got exception: System.Net.WebSockets.WebSocketException (997): The remote party closed the WebSocket connection without completing the close handshake.
at System.Net.WebSockets.WebSocketBase.WebSocketOperation.Process(Nullable`1 buffer, CancellationToken cancellationToken)
at System.Net.WebSockets.WebSocketBase.ReceiveAsyncCore(ArraySegment`1 buffer, CancellationToken cancellationToken)
at SwissbotCore.HTTP.Routes.UpgradeWebsocket.upgrade(HttpListenerContext c) in C:\Users\plynch\source\repos\SwissbotCore\SwissbotCore\HTTP\Routes\UpgradeWebsocket.cs:line 29
I can provide the http requests that the client sends if need be but I am completely stumped on this, any help will be greatly appreciated.
You might want to read up on The Close Handshake in Section 1.4 in RFC 6455 and also Close the WebSocket Connection in Section 7.1.1 in RFC 6455.
Essentially, you need to let the WebSocket endpoint know you are going to close the socket, before you terminate the socket.
For your server side, you should probably be catching this exception, as this can also happen in production scenarios when network issues occur.
I'm not sure why, but, if you change the code inside try block to this:
try
{
var sock = (await c.AcceptWebSocketAsync(null)).WebSocket;
byte[] buff = new byte[1024];
await Listen(sock);
}
catch (Exception x)
{
Console.WriteLine($"Got exception: {x}");
}
private async Task Listen(WebSocket sock)
{
return new Task(async () =>
{
while(sock.State == WebSocketState.Open)
{
var r = await sock.ReceiveAsync(buff, System.Threading.CancellationToken.None);
}
});
}
it's gonna work out fine.
I'm working on setting up my first websocket server for a communications app. I can't seem to figure out how websockets are implemented in Java.
I've tried, unsuccessfully, creating an annotation based Endpoint, but I'm not sure where the client info is going to come through. This is basically the gist of my code, without going into mundane details.
I'm trying to make the class MessageHelper deal with the websocket info transfer, I just can't grasp how to actually get the transfer there.
class MainServer implements Runnable {
// VARIABLES
ServerSocket serverSocket = null;
int port;
// CONSTRUCTORS
MainServer(int p) {
this.port = p;
}
// METHODS
public void run() {
openServerSocket();
while(!isStopped()){
try{
clientSocket = serverSocket.accept();
} catch(IOException e) {
// Do something
}
new Thread(new MainThread(clientSocket)).start();
}
}
}
// Other methods below.
public class MainThread {
final Socket socket;
MainThread(Socket s) {
this.socket = s;
}
public void run() {
try{
BufferedReader br = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
String input = br.readLine(), read = br.readLine();
while(!input.isEmpty()) {
read += "\n";
read += input;
input = br.readLine();
}
/**
* Everything works fine, I'm just not sure where to go
* from here. I tried creating MessageHelper into the java
* websocket implementation using annotations but it did not
* accept input from the client after the handshake was
* made. My client would send something but it would just
* give and EOFException.
**/
if(websocketHandshakeRequest(read)) {
MessageHelper messageHelper =
new MessageHelper(this.socket);
} else {
// Do something
}
} catch(Exception e) {
// Do something.
}
}
}
Don't be confused about the name WebSocket. A TCP socket and a WebSocket are entirely different kind of "sockets".
In Java you use a ServerSocket for TCP sockets. TCP is a transport layer protocol used to implement application layer protocols like POP3 and HTTP.
WebSocket is a HTTP/1.1 protocol upgrade commonly used in web servers and web browsers. You cannot use a ServerSocket for the WebSocket protocol, at least not so straight forward as you might think. First, you have to implement the HTTP/1.1 protocol and then the WebSocket protocol on top of that.
In the Java world you can use web servers like Tomcat or Jetty which provide WebSocket implementations and a high level Java API. This API is part of the Jave Enterprise Edition (JEE). See also the Jave EE 7 Tutorial - Chapter 18 Java API for WebSocket.
E.g. Jetty is a lightweight JEE web server which can be embedded in your application or run as a stand-alone server. See Jetty Development Guide - Chapter 26. WebSocket Introduction.
So in a Java web application running in a WebSocket enabled JEE web server like Jetty you can implement a server side WebSocket as follows:
package com.example.websocket;
import org.apache.log4j.Logger;
import javax.websocket.CloseReason;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
#ServerEndpoint("/toUpper")
public class ToUpperWebsocket {
private static final Logger LOGGER = Logger.getLogger(ToUpperWebsocket.class);
#OnOpen
public void onOpen(Session session) {
LOGGER.debug(String.format("WebSocket opened: %s", session.getId()));
}
#OnMessage
public void onMessage(String txt, Session session) throws IOException {
LOGGER.debug(String.format("Message received: %s", txt));
session.getBasicRemote().sendText(txt.toUpperCase());
}
#OnClose
public void onClose(CloseReason reason, Session session) {
LOGGER.debug(String.format("Closing a WebSocket (%s) due to %s", session.getId(), reason.getReasonPhrase()));
}
#OnError
public void onError(Session session, Throwable t) {
LOGGER.error(String.format("Error in WebSocket session %s%n", session == null ? "null" : session.getId()), t);
}
}
You register your class as a WebSocket handler for the specific path with the #ServerEndpoint annotation. Your WebSocket URL is then ws://host:port/context/toUpper or wss://host:port/context/toUpper for HTTPS connections.
Edit:
Here is a very simplistic HTML page to demonstrate the client side connection to the above WebSocket. This page is served by the same webserver as the WebSocket. The webapplication containing the WebSocket is deployed at context "websocket" on localhost port 7777.
<html>
<body>
<h2>WebSocket Test</h2>
<div>
<input type="text" id="input" />
</div>
<div>
<input type="button" id="connectBtn" value="CONNECT" onclick="connect()" />
<input type="button" id="sendBtn" value="SEND" onclick="send()" disable="true" />
</div>
<div id="output">
<h2>Output</h2>
</div>
</body>
<script type="text/javascript">
var webSocket;
var output = document.getElementById("output");
var connectBtn = document.getElementById("connectBtn");
var sendBtn = document.getElementById("sendBtn");
var wsUrl = (location.protocol == "https:" ? "wss://" : "ws://") + location.hostname + (location.port ? ':'+location.port: '') + "/websocket/toUpper";
function connect() {
// open the connection if one does not exist
if (webSocket !== undefined
&& webSocket.readyState !== WebSocket.CLOSED) {
return;
}
updateOutput("Trying to establish a WebSocket connection to <code>" + wsUrl + "</code>");
// Create a websocket
webSocket = new WebSocket(wsUrl);
webSocket.onopen = function(event) {
updateOutput("Connected!");
connectBtn.disabled = true;
sendBtn.disabled = false;
};
webSocket.onmessage = function(event) {
updateOutput(event.data);
};
webSocket.onclose = function(event) {
updateOutput("Connection Closed");
connectBtn.disabled = false;
sendBtn.disabled = true;
};
}
function send() {
var text = document.getElementById("input").value;
webSocket.send(text);
}
function closeSocket() {
webSocket.close();
}
function updateOutput(text) {
output.innerHTML += "<br/>" + text;
}
</script>
</html>
If you're willing to use Java Spring - which I think would be great for your use case, it's pretty easy to setup a websocket server and client connection.
There's an example here - https://spring.io/guides/gs/messaging-stomp-websocket/
Im trying to make a simple application. That is When I write a word at edittext in android app such as "Hi", Then android app send message "Hi" to node.js server and node.js server send message "Hi has sent successflly" to android app. This is just a example, actually my object is android send a data(message) to server, and receive another data(message) from server.
The problem is this. When I write a word at android app and press button, the message transmitted successfully(I can confirm by console at node.js). But I cant send message to android from node.js .. When I press send button, My android app shut down..
What android says is "java.lang.NullPointerException: Attempt to invoke virtual method 'void android.app.Activity.runOnUiThread(java.lang.Runnable)' on a null object reference" ..
Yesterday, this error didn't happened and another error occured. "cannot cast string to JSONObject."
I will show you my code.
Server Side(Node.js)
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var port = 12000;
app.get('/', function(req, res) {
res.sendFile('index.html');
})
io.on('connection', function(socket) {
console.log('Android device has been connected');
socket.on('message', function(data) {
console.log('message from Android : ' + data);
Object.keys(io.sockets.sockets);
Object.keys(io.sockets.sockets).forEach(function (id) {
console.log("ID : ", id );
io.to(id).emit('message', data);
console.log(data + ' has sent successfully');
})
/*if (data != null) {
io.emit('message', {message : data + ' has received successfully'});
}*/
})
socket.on('disconnect', function() {
console.log('Android device has been disconnected');
})
})
http.listen(port, function() {
console.log('Server Start at port number ' + port);
})
Client Side (Android)
private Emitter.Listener handleIncomingMessages = new Emitter.Listener(){
#Override
public void call(final Object... args){
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
JSONObject data = (JSONObject) args[0];
String message;
try {
message = data.getString("text").toString();
Log.i("result", message);
addMessage(message);
} catch (JSONException e) {
Log.e("result", "Error : JSONException");
return;
} catch (ClassCastException e) {
Log.e("result", "Error : ClassCastException");
e.printStackTrace();
return;
}
}
});
}
};
private void sendMessage(){
String message = mInputMessageView.getText().toString().trim();
mInputMessageView.setText("");
addMessage(message);
JSONObject sendText = new JSONObject();
try{
sendText.put("text", message);
socket.emit("message", message);
}catch(JSONException e){
}
}
private void addMessage(String message) {
mMessages.add(new Message.Builder(Message.TYPE_MESSAGE)
.message(message).build());
// mAdapter = new MessageAdapter(mMessages);
mAdapter = new MessageAdapter( mMessages);
mAdapter.notifyItemInserted(0);
scrollToBottom();
}
private void scrollToBottom() {
mMessagesView.scrollToPosition(mAdapter.getItemCount() - 1);
}
I already searched similar problems that other people asked, but It didn't give me solution. Please help me. Thank you for reading long question.
p.s Because Im not English speaker, Im not good at English .. There will be many problems at grammar and writing skills. Thanks for understanding...
Reason this happens is because method getActivity() returns null. This might happen if you run this on a fragment after it is detached from an activity or activity is no longer visible. I would do a normal null check before like:
Activity activity = getActivity();
if(activity != null) {
activity.runOnUiThread(new Runnable() {...}
}
I'm not familiar with socket.emit() method but it might throw network exception since it's running on UI thread and you are not allowed to do that. I recommend using RxJava/RxAndroid if you want to do this on another thread.
If you want to do network operation just use it like this:
Observable
.fromRunnable(new Runnable {
void run() {
// here do your work
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Void>() {
#Override
public void onCompleted() {
// not really needed here
}
#Override
public void onError(Throwable e) {
// handle errors on UI thread
}
#Override
public void onNext(Void void) {
// do something on UI thread after run is done
}
});
Basically what it does it calls method call from Callable you just made on separate thread and when it's over it invokes onNext method if no exception was thrown or onError method if exception was thrown from Subscriber class.
Note that Response class isn't part of the RxJava/RxAndroid API and you can make it if you want. You can make it a simple POJO class or anything else you need it to be. If you don't need to have response you can use Runnable instead of Callable and it will work just fine.
In order for this to work you need to add this dependencies to your modules Gradle file:
dependencies {
compile 'io.reactivex:rxandroid:1.2.1'
compile 'io.reactivex:rxjava:1.1.6'
}
I'm running a spring 3.1.2 backend on a weblogic 12.1.3 server.
In order to accept websocket connections, my configurator as follows:
public class SpringConfigurator extends Configurator {
private static final Logger LOGGER = LoggerFactory.make();
private static final Map<String, Map<Class<?>, String>> cache = new ConcurrentHashMap<String, Map<Class<?>, String>>();
private static final String MAGIC_STR = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
private static final String NO_VALUE = ObjectUtils.identityToString(new Object());
#SuppressWarnings("unchecked")
#Override
public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException {
WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
if (wac == null) {
String message = "Failed to find the root WebApplicationContext. Was ContextLoaderListener not used?";
LOGGER.error(message);
throw new IllegalStateException(message);
}
String beanName = ClassUtils.getShortNameAsProperty(endpointClass);
if (wac.containsBean(beanName)) {
T endpoint = wac.getBean(beanName, endpointClass);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Using #ServerEndpoint singleton " + endpoint);
}
return endpoint;
}
Component annot = AnnotationUtils.findAnnotation(endpointClass, Component.class);
if ((annot != null) && wac.containsBean(annot.value())) {
T endpoint = wac.getBean(annot.value(), endpointClass);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Using #ServerEndpoint singleton " + endpoint);
}
return endpoint;
}
beanName = getBeanNameByType(wac, endpointClass);
if (beanName != null) {
return (T) wac.getBean(beanName);
}
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Creating new #ServerEndpoint instance of type " + endpointClass);
}
return wac.getAutowireCapableBeanFactory().createBean(endpointClass);
}
// modifyHandshake() is called before getEndpointInstance()
#Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
super.modifyHandshake(sec, request, response);
}
private String getBeanNameByType(WebApplicationContext wac, Class<?> endpointClass) {
String wacId = wac.getId();
Map<Class<?>, String> beanNamesByType = cache.get(wacId);
if (beanNamesByType == null) {
beanNamesByType = new ConcurrentHashMap<Class<?>, String>();
cache.put(wacId, beanNamesByType);
}
if (!beanNamesByType.containsKey(endpointClass)) {
String[] names = wac.getBeanNamesForType(endpointClass);
if (names.length == 1) {
beanNamesByType.put(endpointClass, names[0]);
} else {
beanNamesByType.put(endpointClass, NO_VALUE);
if (names.length > 1) {
String message = "Found multiple #ServerEndpoint's of type " + endpointClass + ", names=" + names;
LOGGER.error(message);
throw new IllegalStateException(message);
}
}
}
String beanName = beanNamesByType.get(endpointClass);
return NO_VALUE.equals(beanName) ? null : beanName;
}
}
The problem is when I try to open websocket connection via a javascript client, it correctly generates response headers as I debugged this location:
#Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
super.modifyHandshake(sec, request, response);
}
But in client side it gives following error:
WebSocket connection to 'ws://localhost:7001/websocket' failed: Error during >WebSocket handshake: Invalid status line
In chrome developer tools the response seems as follows:
HTTP/0.9 200 OK
I think somehow http request does not upgrade to websocket connection.
I really appreciate any help regarding this issue.
I encountered exactly this issue today when testing http://showcase.omnifaces.org/push/socket on WebLogic 12.2.1.
Already at the first test attempt of the webapp, WebLogic throws the below exception when making a websocket connection:
java.lang.IllegalStateException: The async-support is disabled on this request: weblogic.servlet.internal.ServletRequest
Impl#6682044b[GET /omnifaces.push/counter?e6845a3a-26ed-4520-9824-63ffd85b24eb HTTP/1.1]
at weblogic.servlet.internal.ServletRequestImpl.startAsync(ServletRequestImpl.java:1949)
at weblogic.servlet.internal.ServletRequestImpl.startAsync(ServletRequestImpl.java:1925)
at javax.servlet.ServletRequestWrapper.startAsync(ServletRequestWrapper.java:432)
at weblogic.websocket.tyrus.TyrusServletFilter.doFilter(TyrusServletFilter.java:234)
...
It turns out that, on contrary to all other servers I tested, WebLogic's own TyrusServletFilter, which is responsible for handling websocket handshake requests, is internally installed after the filters provided via web.xml of the deployed webapp. The webapp shipped with a character encoding filter and a GZIP filter mapped on /*, so they were invoked before the websocket filter. This was strange at first sight, but I thought it is what it is, so I just added <async-supported>true</async-supported> to those webapp-provided filters so that the TyrusServletFilter can do its job.
However, when making a websocket connection, a JavaScript error in the client side occurred when a push message was being sent, exactly the one you faced:
WebSocket connection to 'ws://localhost:7001/omnifaces.push/counter?e6845a3a-26ed-4520-9824-63ffd85b24eb' failed: Error during WebSocket handshake: Invalid status line
It turns out that WebSockets just can't deal with GZIP responses. After disabling the GZIP filter, everything continued to work flawlessly.
The root problem is however that WebLogic should have installed its TyrusServletFilter before all webapp-provided filters. All other Java EE servers I ever have tested do this correctly. Your workaround of immediately dispatching and forwarding all websocket handshake requests to their target URL pattern, as mentioned in your comment on the question, is a good one. The alternative would be to reconfigure the web.xml-provided filters to not match websocket handshake requests anymore, e.g. by using a more specific URL pattern, or mapping to a specific servlet instead.
I am trying to build a WebSocket session using Python 3.4, Django, Autobahn and JS. I have successfully run the websocket server on the python side, but i cannot subscribe or receive any data published by the server
My code is fairly simple:
class TestAppWS(ApplicationSession):
"""
An application component that publishes an event every second.
"""
def onConnect(self):
self.join(u"realm1")
#asyncio.coroutine
def onJoin(self, details):
counter = 0
while True:
self.publish('com.myapp.topic1', counter)
counter += 1
yield from asyncio.sleep(1)
def start_ws():
print("Running")
session_factory = ApplicationSessionFactory()
session_factory.session = TestAppWS
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
# factory = WebSocketServerFactory("ws://localhost:8090", debug=False)
# factory.protocol = MyServerProtocol
server = None
try:
transport_factory = WampWebSocketServerFactory(session_factory, debug_wamp=True)
loop = asyncio.get_event_loop()
coro = loop.create_server(transport_factory, 'localhost', 8090)
server = loop.run_until_complete(coro)
loop.run_forever()
except OSError:
print("WS server already running")
except KeyboardInterrupt:
pass
finally:
if server:
server.close()
loop.close()
start_ws() is run inside a separate Thread object. If I access localhost:8090 on my browser I can see the Autobahn welcome message.
On the frontend I have
var connection = new autobahn.Connection({
url: 'ws://localhost:8090/',
realm: 'realm1'}
);
connection.onopen = function (session) {
var received = 0;
function onevent1(args) {
console.log("Got event:", args[0]);
received += 1;
if (received > 5) {
console.log("Closing ..");
connection.close();
}
}
session.subscribe('com.myapp.topic1', onevent1);
};
connection.open();
It does not seem to work, when I try to connect the frontend I get the following error on the backend side:
Failing WAMP-over-WebSocket transport: code = 1002, reason = 'WAMP Protocol Error (Received <class 'autobahn.wamp.message.Hello'> message, and session is not yet established)'
WAMP-over-WebSocket transport lost: wasClean = False, code = 1006, reason = 'connection was closed uncleanly (I failed the WebSocket connection by dropping the TCP connection)'
TX WAMP HELLO Message (realm = realm1, roles = [<autobahn.wamp.role.RolePublisherFeatures object at 0x04710270>, <autobahn.wamp.role.RoleSubscriberFeatures object at 0x047102B0>, <autobahn.wamp.role.RoleCallerFeatures object at 0x047102D0>, <autobahn.wamp.role.RoleCalleeFeatures object at 0x047102F0>], authmethods = None, authid = None)
RX WAMP HELLO Message (realm = realm1, roles = [<autobahn.wamp.role.RoleSubscriberFeatures object at 0x04710350>, <autobahn.wamp.role.RoleCallerFeatures object at 0x04710330>, <autobahn.wamp.role.RoleCalleeFeatures object at 0x04710390>, <autobahn.wamp.role.RolePublisherFeatures object at 0x04710370>], authmethods = None, authid = None)
Traceback (most recent call last):
File "C:\Python34\lib\site-packages\autobahn\wamp\websocket.py", line 91, in onMessage
self._session.onMessage(msg)
File "C:\Python34\lib\site-packages\autobahn\wamp\protocol.py", line 429, in onMessage
raise ProtocolError("Received {0} message, and session is not yet established".format(msg.__class__))
autobahn.wamp.exception.ProtocolError: Received <class 'autobahn.wamp.message.Hello'> message, and session is not yet established
on the javascript console I see:
Uncaught InvalidAccessError: Failed to execute 'close' on 'WebSocket': The code must be either 1000, or between 3000 and 4999. 1002 is neither.
Any idea? It looks like the session is not started, honestly it is not clear how this session work. Should not the session be initialized once a connection from the client is made?
Your TestAppWs and your browser code are both WAMP application components. Both of these need to connect to a WAMP router. Then they can talk freely to each other (as if there were no router in between .. transparently).
Here is how to run.
Run a WAMP Router.
Using Crossbar.io (but you can use other WAMP routers as well), that's trivial. First install Crossbar.io:
pip install crossbar
Crossbar.io (currently) runs on Python 2, but that's irrelevant as your app components can run on Python 3 or any other WAMP supported language/run-time. Think of Crossbar.io like a black-box, an external infrastructure, like a database system.
Then create and start a Crossbar.io default router:
cd $HOME
mkdir mynode
cd mynode
crossbar init
crossbar start
Run your Python 3 / asyncio component
import asyncio
from autobahn.asyncio.wamp import ApplicationSession
class MyComponent(ApplicationSession):
#asyncio.coroutine
def onJoin(self, details):
print("session ready")
counter = 0
while True:
self.publish('com.myapp.topic1', counter)
counter += 1
yield from asyncio.sleep(1)
if __name__ == '__main__':
from autobahn.asyncio.wamp import ApplicationRunner
runner = ApplicationRunner(url = "ws://localhost:8080/ws", realm = "realm1")
runner.run(MyComponent)
Run your browser component
var connection = new autobahn.Connection({
url: 'ws://localhost:8080/ws',
realm: 'realm1'}
);
connection.onopen = function (session) {
var received = 0;
function onevent1(args) {
console.log("Got event:", args[0]);
received += 1;
if (received > 5) {
console.log("Closing ..");
connection.close();
}
}
session.subscribe('com.myapp.topic1', onevent1);
};
connection.open();