socket.io-redis-emitter unable to connect using a redis cluster - node.js

I need to extend my socket.io server to multiple machines behind a load balancer (i do have sticky connections enabled on my load balancer, but once I have two machines or more running my client servers start throwing errors), it looks like one of the only ways to do it is with redis, my redis server runs in a cluster... in order to connect to redis as a cluster there is some nuance.
The socket.io/redis-emitter docs only give examples using the createClient connection with redis, while the redis library itself provides a cluster mode which I have included details of below
It looks like socket.io/redis-emitter and #socket.io/redis-adapter are both incompatible with using createCkuster in the redis library
I am getting the following error:
Error: Cluster is not connected
at RedisClusterSlots._RedisClusterSlots_getRandomClient (/Users/aronlilland/Documents/dev/controlair/notifications/node_modules/#redis/client/dist/lib/cluster/cluster-slots.js:193:15)
at RedisClusterSlots.getClient (/Users/aronlilland/Documents/dev/controlair/notifications/node_modules/#redis/client/dist/lib/cluster/cluster-slots.js:54:120)
at Commander._RedisCluster_execute (/Users/aronlilland/Documents/dev/controlair/notifications/node_modules/#redis/client/dist/lib/cluster/index.js:102:73)
at Commander.sendCommand (/Users/aronlilland/Documents/dev/controlair/notifications/node_modules/#redis/client/dist/lib/cluster/index.js:65:98)
at Commander.commandsExecutor (/Users/aronlilland/Documents/dev/controlair/notifications/node_modules/#redis/client/dist/lib/cluster/index.js:62:75)
at Commander.BaseClass.<computed> [as publish] (/Users/aronlilland/Documents/dev/controlair/notifications/node_modules/#redis/client/dist/lib/commander.js:8:29)
at BroadcastOperator.emit (/Users/aronlilland/Documents/dev/controlair/notifications/node_modules/#socket.io/redis-emitter/dist/index.js:262:26)
at Socket.<anonymous> (/Users/aronlilland/Documents/dev/controlair/notifications/src/sockets/index.js:61:36)
at Socket.emit (node:events:527:28)
at Socket.emitUntyped (/Users/aronlilland/Documents/dev/controlair/notifications/node_modules/socket.io/dist/typed-events.js:69:22)
// mock example
const { createCluster } = require('redis')
const { Emitter } = require('#socket.io/redis-emitter')
const { Server } = require('socket.io')
const app = express()
const server = require('http').Server(app)
const log = require('../logger')('cache:client')
const rootNodes = [
{ url: `redis://${process.env.REDIS_HOST_1}` },
{ url: `redis://${process.env.REDIS_HOST_2}` },
{ url: `redis://${process.env.REDIS_HOST_3}` }
]
const client = createCluster({ rootNodes })
client.on('error', err => log.error(`Redis Cluster Error ${err}`))
const emitter = new Emitter(client)
// ...
const io = new Server(server)
io.on('connection', socket => {
socket.on('event', event => {
emitter.in('room1').emit('<some event>', event)
})
})
im using node v16.15.0
redis (running as a cluster) is v6.2.6
redis (node library) v4.1.0
socket.io/redis-emitter v4.1.1

Related

TypeError: Cannot read properties of undefined (reading 'apply') NodeJs

I'm relatively new to node and I have read through answers to this error without much success.
I'm building a multiplayer game using socket.io and serving the react app statically using express. The app displays the lobby successifully but the server throws this error as soon as the game starts.
Here is the api
const express = require('express')
const path = require('path')
const api = express.Router()
api.use(express.static(path.join(__dirname, "..", "public")))
module.exports = api
and server.js
const http = require('http')
const io = require('socket.io')
const sockets = require('./sockets')
const apiServer = require('./api')
const httpServer = http.createServer(apiServer)
const socketServer = io(httpServer, {
cors: {
origin: 'http://localhost:3000',
methods: ["GET", "POST"]
}
})
const PORT = 4000
httpServer.listen(4000, () => {
console.log(`Server is running on port: ${PORT}`)
})
sockets.listen(socketServer)
is there something I'm doing wrongly?
This are logs when the server start and connect two users
Server is running on port: 4000
User connected CTmTWswPSNdfhhl2AAAB
{
playerData: {
pid: '53f7bfaf-bbbb-496c-a666-f55a9d574054',
avatar: 11,
name: 'Nyah'
},
players: 1
}
User connected UkfcIX202fDXJ6caAAAD
{
playerData: {
pid: '09b68067-cc0d-460d-b28c-21cde0c54862',
avatar: 9,
name: 'Walter'
},
players: 2
}
Then as soon as game starts:
Game on Guys!!!
/home/aimkeys/Development/projects/card-games/poker-server/node_modules/express/lib/router/index.js:646
return fn.apply(this, arguments);
^
TypeError: Cannot read properties of undefined (reading 'apply')
at Immediate.<anonymous> (/home/aimkeys/Development/projects/card-games/poker-server/node_modules/express/lib/router/index.js:646:15)
at processImmediate (node:internal/timers:468:21)
The app is working correctly when I have started the frontend and the backend at different ports.

Problems with migrating to Redis 4.x in Node

I am trying to migrate my google cloud app engine from Redis 3.x to 4.x. However, it appears that there have been some major changes in Redis 4.x. It appears that the client no longer autoconnect and there have been some chnages to the syntax. Here's what I have run
'use strict';
import {createClient} from 'redis';
// These are just values stored in environment variables.
const REDISHOST = process.env.REDIHOST;
const REDISPORT = process.env.REDIPORT;
const REDISAUTH = process.env.REDISAUTH;
const redisClient.createClient();
redisClient.host = REDISHOST;
redisClient.port = REDISPORT;
redisclient.auth = REDISAUTH;
redisClient.on('error', (err) => console.error(`##### REDIS ERR: ${err}.`));
await redisClient.connect();
I can tell that host, port, and auth is being set in redisClient, but when I connect, it tries to connect to localhost and fails. Any idea what I am missing here?
You need to pass the connection information in the call the createClient():
const redisClient = createClient({
socket: {
host: REDISHOST,
port: REDISPORT
},
password: REDISAUTH
})
There are lots of options for connecting. They are all detailed in the client configuration guide.

Redis Cloud and connect-redis: the client is closed

I currently build a website using Express and want to use redis cloud database to save userID in session. The redisClient is created in redisClient.js and after that i pass it to redisStore in session in app.js. Here is the code:
redisCLient.js
const redis = require("redis");
let redisClient = redis.createClient({
host: process.env.REDIS_HOSTNAME,
port: parseInt(process.env.REDIS_PORT),
password: process.env.REDIS_PASSWORD
});
redisClient.on('error', function(err) {
console.log('*Redis Client Error: ' + err.message);
});
redisClient.on('connect', function(){
console.log('Connected to redis instance');
});
(async () => {
await redisClient.auth(process.env.REDIS_PASSWORD)
.catch(err => {console.log('Redis auth error: ' + err.message)});
await redisClient.connect()
.catch(err => {console.log('Redis connect error: ' + err.message)});
})();
module.exports = redisClient;
app.js
const session = require("express-session");
const redisStore = require('connect-redis')(session);
const redisClient = require('./session-store/redisClient');
...
app.use(cookieParser());
app.use(session({
store: new redisStore({client: redisClient, ttl: 3600 * 24 * 30}),
saveUninitialized: false,
secret: process.env.SESSION_SECRET,
resave: false
}));
The problem is: upon starting the server i got error messages log in console like this:
Redis auth error: The client is closed
*Redis Client Error: connect ECONNREFUSED 127.0.0.1:6379
*Redis Client Error: connect ECONNREFUSED 127.0.0.1:6379
*Redis Client Error: connect ECONNREFUSED 127.0.0.1:6379
*Redis Client Error: connect ECONNREFUSED 127.0.0.1:6379
...
I used this guide to set up redis cloud and assign dotenv variables (host, port and password). I have debugged and the dotenv is working fine and I have host, port and password variables correct.
But the problem still remains. I still get The client is closed and connect ECONNREFUSED 127.0.0.1:6379 error as in console log above. How can i fix this?
was stuck on same issue and found some luck. have used 'ioredis' module instead of redis which worked seamlessly.
const redis = require('ioredis');
const redisClient = redis.createClient({host:'your host address',port:your port,username:'',password:''});
redisClient.on('connect',() => {
console.log('connected to redis successfully!');
})
redisClient.on('error',(error) => {
console.log('Redis connection error :', error);
})
module.exports = redisClient;
When you create a client using redis.createClient, you need to use url instead of host, port.
refer to basic-example
it showing url format
redis[s]://[[username][:password]#][host][:port][/db-number]
in your case, it might be like this
var url = `redis://<YourUsername>:${process.env.REDIS_PASSWORD}#${process.env.REDIS_HOSTNAME}:${parseInt(process.env.REDIS_PORT)}`
redis.createClient({
url: url
});
Using host, port option must be the old version.
Option 1: switch the order of the calls to auth and connect
From the Node Redis client documentation:
When connecting to a Redis server that requires authentication, the AUTH command must be sent as the first command after connecting.
You should therefore switch the order of the calls to redisClient.auth and redisClient.connect.
Option 2: remove the call to auth
However, the documentation for the password property of createClient options states:
If set, client will run Redis auth command on connect.
As you are supplying password to createClient, you could alternatively just remove the explicit call to auth.
You must do await redisClient.connect() before you access the client. Try to move your redisClient.connect() just after you create it.
try this -->
var options = {
client: redis.createClient({
url : process.env.REDIS_URL,
legacyMode: true,
})};
you can make your url as 'redis://host:port'
import { createClient } from "redis";
const redisClient = createClient({
url: "redis://localhost:6379",
});
const start = async () => {
await redisClient.connect();
};
start();
export default redisClient;

How to enable tls for heroku redis node?

I cannot connect to Heroku Redis using TLS. I tried whats given in heroku docs and the answer on this post How to Connect Heroku Redis TLS Node? but nothing seems to work. My current redis options look like:
const Redis = require('ioredis');
const url = process.env.REDIS_URL;
const options = {
tls: {
rejectUnauthorized: false,
},
};
const opts = new Redis(url, options);
Error that I am getting is
[ioredis] Unhandled error event:Error: connect ETIMEDOUT
at TLSSocket.<anonymous
(/app/node_modules/ioredis/built/redis/index.js:310:37)
at Object.onceWrapper (events.js:421:28)
at TLSSocket.emit (events.js:315:20)
.....
Also what is the difference between REDIS_URL and REDIS_TLS_URL? Which one to use?
REDIS_TLS_URL will have an extra 's' in the scheme of the url -> rediss://password#host:port vs. the REDIS_URL (with a single 's' in it's scheme). The other difference is the port. The TLS port is usually +1 of the regular port. On Heroku Redis version 6, you need to use the REDIS_TLS_URL along with tls option you defined above.
const Redis = require('ioredis');
const url = process.env.REDIS_TLS_URL;
const options = {
tls: {
rejectUnauthorized: false,
},
};
const opts = new Redis(url, options);

NodeJS as Websocket client using ws

I need to send a message to a websocket server through request POST. The client is not a browser but a Node server.
I'm new to websocket.
When I run the code below.
var WebSocket = require("ws");
const express = require("express");
var app = express();
const client = new WebSocket(process.env.URL);
client.on("error", handleError);
client.onopen = () => {
client.send("Message From Client");
};
function handleError(error) {
console.log(error);
}
app.get("/echo", async function (req, res) {
client.once("connection", function connection(cli) {
cli.send(msg);
res.send("send");
});
});
app.listen(3333, function () {
console.log("Example app listening on port 3333!");
});
It shows the error
Error: write EPROTO 19524:error:1408F10B:SSL routines:ssl3_get_record:wrong version number:c:\ws\deps\openssl\openssl\ssl\record\ssl3_record.c:332:
at WriteWrap.onWriteComplete [as oncomplete] (internal/stream_base_commons.js:92:16) {
errno: 'EPROTO',
code: 'EPROTO',
syscall: 'write'
}
I'm not sure how to do it with express, but it should be similar to https. Here is how to do it with https.
Basically it has to be the https server that should listen on certain port and also have certs and keys as option passed to it. Then you would pass the https server to websocket server.
When client connects, it'll connect to https and then upgrade to WSS.
Which means your client application can connect to wss://test.mysite.com:1234.
const https = require('https')
const options = {
cert: fs.readFileSync('/etc/ssl/test.mysite.com/cert.pem'),
key: fs.readFileSync('/etc/ssl/test.mysite.com/privkey.pem'),
ca: fs.readFileSync('/etc/ssl/test.mysite.com/chain.pem')
};
const httpsServer = https.createServer(options);
httpsServer.listen(1234, () => console.log('Https Server running on port 1234'));
var ws = new WebSocket.Server({
server: httpsServer
});
ws.on('connection', socket => {
console.log('Conencted')
});

Resources