I am working with server sent events with spring boot in the backend and react-native/expo as client.
To handle server send event on the client I use react-native-event-source. But the problem is only some events are received by the client. Here is the scenario:
event id 0 -> not received
event id 1 -> received
event id 2 -> not received
event id 3 -> received
event id 4 -> not received
event id 5 -> received
...
on the server side we tried deferent hacks and logs show that the server is actually sending each event but the expo client seems to ignore some events (1 event in two).
Here is how we initialize connection to get server sent events:
listen() {
const options = {
headers: {
Authorization: `Bearer ${this.userToken}`,
},
};
let url = `${TEST_URL}${this.user.id}`;
try {
console.log('starting stream ...');
const eventName = `dataSet-created${this.user.id}`;
this.eventSource = new RNEventSource(url, options);
this.eventSource.addEventListener(eventName, event => {
console.log('stream event received', event);
});
this.eventSource.addEventListener('error', e => {
console.log('stream listener error', e);
});
console.log('eventSource stream ...', this.eventSource);
} catch (e) {
console.log('start stream error', e);
}
}
react-native-event-source does not use true EventSource but rather works around by polling. So my guess is your client miss messages because there are multiple in single polling interval (that is 500ms).
Reason for this is react native currently does not support HTTP streams.
Related
I'm working on a chat app, and trying to listen to a socket io event on the client side, I used a useEffect with the socket as a dependency, but for some reason, when I open a new tab and send a message, (the socket changes) but the useEffect does not run.
const socket = io.connect("http://localhost:3001");
useEffect(() => {
console.log("useEffect:", socket.id);
socket.on("receive_message", (data: any) => {
console.log(data);
});
}, [socket]);
My server code:
io.on("connection", (socket) => {
socket.on("send_message", (data) => {
console.log("data: ", socket.id); // this logs
socket.emit("receive_message", data);
});
});
From what I understand, this works by the server listening in on an event called "send_message", this event is emitted from my client side, upon receiving this event, the server emits it's own event to the client side, and this is supposed to be picked up by the client side by running the useEffect every time the socket dependency changes, so why is my useEffect not running despite the dependency socket changing when I create a new tab?
I notice WebSocket extends EventTarget and EventTarget.dispatchEvent() "sends an Event to the object". But it is not clear from that MDN document who the object is.
I had thought maybe that object was the receiver on the WebSocket server side and dispatchEvent() is another way to send a message to the ws server like WebSocket.send()
So I did the following test in the browser console to see if that is the case(using https://github.com/websockets/ws for ws server).
const ws = new WebSocket('ws://localhost:3000');
//instead of using ws.send('hello world'); I use dispatchEvent here
//as I want to know what dispatchEvent does,
//e.g can it send my message to ws server like send() ?
ws.addEventListener('open', (evt) => {
let event = new MessageEvent('message', {
data: "hello world",
lastEventId: '101'
});
ws.dispatchEvent(event);
});
ws.addEventListener('message', (event) => {
console.log('Message from server ', event.data);
});
//Here is my server codes
const WebSocket = require('ws')
const wss = new WebSocket.Server({ port: 3000 })
wss.on('connection', function connection(ws) {
console.log(`A ${ws.url}} connects`)
ws.on('message', (evt) => {
console.log(`on message:received: ${evt}`)
ws.send('test callback')
})
})
But as it turns out the console immediately prints out "Message from server hello world" while ws server didn't get that event.
So what is the use of dispatchEvent() in WebSocket ?
The EventTarget interface is implemented by objects that can receive events, and is an interface that your ws (WebSocket) variable will implement to receive communication events from the low-level browser implementation of that network socket (such as when it successfully opens, and when a message is received).
However, you're not supposed to call it yourself. What you're doing is manually feeding it an event, and causing the WebSocket to behave as if it was an real event that has arrived from the remote server.
Therefore, your code is only useful in testing scenarios where you are emulating a server response.
There is a simple web server that accepts data. Sample code below.
The idea is to track in real time how much data has entered the server and immediately inform the client about this. If you send a small amount of data, then everything works well, but if you send more than X data in size, then the on.data event on the server is triggered with a huge delay. I can see that data is transfering for 5 seconds already but on.data event is not trigerred.
on.data event seems to be triggered only when data is uploaded completely to the server, so that's why it works fine with small data (~2..20Mb), but with big data (50..200Mb) it doesnt work well.
Or maybe it is due to some kind of buffering..?
Do you have any suggestions why on.data triggered with delay and how to fix it?
const app = express();
const port = 3000;
// PUBLIC API
// upload file
app.post('/upload', function (request, response) {
request.on('data', chunk => {
// message appears with delay
console.log('upload on data', chunk.length);
// send message to the client about chunk.length
});
response.send({
message: `Got a POST request ${request.headers['content-length']}`
});
});
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});
TLDR:
The delay that you are experiencing probably is the Queueing from Resource scheduling from the browser.
The Test
I did some tests with express, and then I found that it uses http to handle requests/response, so I used a raw http server listener to test this scenario, which has the same situation.
Backend code
This code, based on sample of Node transaction samples, will create a http server and give log of time on 3 situations:
When a request was received
When the first data event fires
When the end event fires
const http = require('http');
var firstByte = null;
var server = http.createServer((request, response) => {
const { headers, method, url } = request;
let body = [];
request.on('error', (err) => {
}).on('data', (chunk) => {
if (!firstByte) {
firstByte = Date.now();
console.log('received first byte at: ' + Date.now());
}
}).on('end', () => {
console.log('end receive data at: ' + Date.now());
// body = Buffer.concat(body).toString();
// At this point, we have the headers, method, url and body, and can now
// do whatever we need to in order to respond to this request.
if (url === '/') {
response.statusCode = 200;
response.setHeader('Content-Type', 'text/html');
response.write('<h1>Hello World</h1>');
}
firstByte = null;
response.end();
});
console.log('received a request at: ' + Date.now());
});
server.listen(8083);
Frontend code (snnipet from devtools)
This code will fire a upload to /upload which some array data, I filled the array before with random bytes, but then I removed and see that it did not have any affect on my timing log, so yes.. the upload content for now is just an array of 0's.
console.log('building data');
var view = new Uint32Array(new Array(5 * 1024 * 1024));
console.log('start sending at: ' + Date.now());
fetch("/upload", {
body: view,
method: "post"
}).then(async response => {
const text = await response.text();
console.log('got response: ' + text);
});
Now running the backend code and then running the frontend code I get some log.
Log capture (screenshots)
The Backend log and frontend log:
The time differences between backend and frontend:
Results
looking at the screenshoots and I get two differences between the logs:
The first, and most important, is the difference between frontend fetch start and backend request recevied, I got 1613ms which is "close" (1430ms) to Resource Scheduling in network timing tab, I think there are more things happening between the frontend fetch call and the node backend event, so I can't direct compare the times:
log.backendReceivedRequest - log.frontEndStart
1613
The second is the difference between receving data on backend, which I got
578ms, close to Request sent (585ms) in network timing tab:
log.backendReceivedAllData - log.backendReceivedFirstData
578
I also changed the frontend code to send different sizes of data and the network timing tab still matches the log
The thing that remains unknown for me is... Why does Google Chrome is queueing my fetch since I'm not running any more requests and not using the bandwidth of the server/host? I readed the conditions for Queueing but not found the reason, maybe is allocating the resources on disk, but not sure: https://developer.chrome.com/docs/devtools/network/reference/#timing-explanation
References:
https://nodejs.org/es/docs/guides/anatomy-of-an-http-transaction/
https://developer.chrome.com/docs/devtools/network/reference/#timing-explanation
I found a problem. It was in nginx config. Nginx was setup like a reverse proxy. By default proxy request buffering is enabled, so nginx grabs first whole request body and only then forwards it to nodejs, so that's why I saw delay.
https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_request_buffering
I am having a trouble in implementing the ACK in order to get feedback that a message was delivered using MQTT. My conception lies on providing an id among the message sent by the sender so that the receiver sends back an ACK with the same id on a different channel. Now the issue occurring is that I cannot break the listening on event when I receive the ack.
So far my code is
let mqtt = require('async-mqtt')
, cfg = require('./cfg');
let client = mqtt.connect(cfg.server);
client.subscribe('some/other/topic');
client.on('connect', sendWithAck)
let id = 123;
async function sendWithAck() {
try {
await client.publish('some/topic', `Message with id${id}`, () => {
client.on('message', (topic, msg) => {
console.log(`${topic}> ${msg.toString()}`);
//this.stopPropagation(); //doesn't work
})
});
await client.end();
console.log('done');
} catch(e) {
console.log('error', e);
process.exit();
}
}
This approach won't work, because what would happen if the other end never responds (e.g. has crashed). There is no way to know this at a MQTT protocol level, unlike a synchronous protocol like HTTP.
The right approach is to set up the on('message') listener before the call to subscribe and to use a state machine to record the id of sent messages and remove them when the response comes in. This way you can set up a timer to allow for time out of responses and deal with them appropriately as well.
What's the easiest way to make an outgoing http request in node.js while tracking time it takes to: lookup DNS, connect to server, time to first byte, time to completion, etc. ?
I've tried the request and needle modules, but they don't seem to have this functionality. Should I hack together my own solution using plain http module?
request wraps the http module so you can use the 'socket' event emitted by the request object to listen for net.Socket events.
Based on your question you can listen for the'lookup', 'connect', 'data' and 'close' events on the Socket and use console.time() and console.timeEnd() to keep track of the time taken for the operation.
// You can use request from npm
const request = require('request')
// Or the native http/https module
// const {request} = require('https')
// Store the http.ClientRequest object returned
const req = request('https://www.google.com')
// Listen for the 'socket' event and then attach listeners to the socket
req.on('socket', socket => {
console.time('dnsInfo')
socket.once('lookup', (err, address, family, host) => {
console.timeEnd('dnsInfo')
console.log(address, family, host)
})
socket.once('connect', () => {
console.time('requestTime')
console.time('initialData')
console.log('Connected to server')
})
socket.once('data', chunk => console.timeEnd('initialData'))
socket.once('close', () => console.timeEnd('requestTime'))
})
req.end()
Seems like request actually supports an option to measure the timings by adding the time option:
request = require 'request'
opts =
url: 'http://localhost/redirect.php'
time: true
request opts, (e, r, body) ->
console.log r.timingPhases
prints
{ wait: 4.005603000000008,
dns: 0.5604900000000157,
tcp: 0.2195809999999483,
firstByte: 1.0195130000000177,
download: 0.7950860000000262,
total: 6.600273000000016 }