Задать вопрос
Login8
@Login8
Программист, критик, наватор

Почему Redis PUB/SUB передаем сообщения хуже чем http tcp?

Здравствуйте уважаемые знатоки.
Я в своих проектах когда мне нужно обеспечить взаимодействие python и nodejs приложений использую обычный http TCP (UNIX socket не подходил к сожалению в данном случае мне, поэтому использовал TCP), на локальном ip адресе 127.0.0.1. И все работает хорошо. Однако решил взглянуть в сторону Redis, подумал что если оба приложения написаные на разных языках, подключившись к Redis PUB/SUB они будут передавать друг другу сообщения гораздо быстрее чем по TCP. Но кажется я жестоко ошибся.

Итак вот результаты тестов (сам код будет ниже):

---- Redis PUB/SUB ----

10 сообщений python -> nodejs (среднее время передачи сообщений: 0.0018999814987182618)

100 сообщений python -> nodejs (среднее время передачи сообщений: 0.001810011863708496)

500 сообщений python -> nodejs (среднее время передачи сообщений: 0.006044007778167724)

1000 сообщений python -> nodejs (среднее время передачи сообщений: 0.017947996616363527)

2000 сообщений python -> nodejs (тест провален, ошибка в терминале:
libgcc_s.so.1 must be installed for pthread_cancel to work
libgcc_s.so.1 must be installed for pthread_cancel to work
Аварийный останов
)

---- TCP ----

10 сообщений python -> nodejs (среднее время передачи сообщений: 0.0032999515533447266)

100 сообщений python -> nodejs (среднее время передачи сообщений: 0.001379995346069336)

500 сообщений python -> nodejs (среднее время передачи сообщений: 0.0012699985504150391)

1000 сообщений python -> nodejs (среднее время передачи сообщений: 0.0013020012378692626)

2000 сообщений python -> nodejs (среднее время передачи сообщений: 0.0011915005445480348)

5000 сообщений python -> nodejs (среднее время передачи сообщений: 0.0010703979015350342)

10000 сообщений python -> nodejs (среднее время передачи сообщений: 0.001122001028060913)

100000 сообщений python -> nodejs (среднее время передачи сообщений: 0.0010812000036239625)

Вот так. Сколько бы сообщений не отправил по TCP , среднее время передачи 1 - 3 млс.

А теперь код который использовался в тесте. Может я что то делаю не так, почему Redis PUB/SUB хуже передает сообщения чем TCP, может что то другое посоветуете?

Код python для Redis PUB/SUB:

import sys
import time
import redis.asyncio as redis
import asyncio


connect_redis = redis.Redis(host = 'localhost', port = 6379)
count = 10
is_stop = False

if len(sys.argv) > 1:
	count = int(sys.argv[1])

if len(sys.argv) > 2:
	is_stop = True

async def pub_to_redis(connect_redis : object, channel : str, message : str) -> None:

	sub = connect_redis.pubsub(ignore_subscribe_messages = True)
	await sub.subscribe(channel)
	await connect_redis.publish(channel, message)

async def ggg(connect_redis : object, count : int, is_stop : bool) -> None:

	channel = 'test'

	if is_stop:
		await pub_to_redis(connect_redis = connect_redis, channel = channel, message = 'stop')
		return

	for _ in range(count):
		message = str(time.time())[:14]
		await pub_to_redis(connect_redis = connect_redis, channel = channel, message = message)


loop = asyncio.new_event_loop()
tasks = [
	loop.create_task(ggg(connect_redis, count, is_stop)),
]
loop.run_until_complete(asyncio.wait(tasks))


Код js (nodejs) для Redis PUB/SUB:

const redis = require('redis');

(async () => {

	var arr = [];

	const client = redis.createClient();

	const subscriber = client.duplicate();

	await subscriber.connect();

	await subscriber.subscribe('test', (message, channel) => {
		if(message == 'stop'){
			var sum = arr.reduce((a, b) => a + b, 0);
			console.log('average', sum / arr.length);
			console.log('count', arr.length);
			arr = [];
			return;
		}
		arr.push((+(new Date()) / 1000) - +message);
	});

})();


------------------------------------------------------------

Код python для TCP:

import sys
import requests
import time


port = 12333
default_http_headers = {
'Connection':'close',
'Content-Type':'application/json'
}
count = 10
is_stop = False

if len(sys.argv) > 1:
	count = int(sys.argv[1])

if len(sys.argv) > 2:
	is_stop = True


if is_stop:
	data = '{"request":"stop"}'
	requests.post('http://127.0.0.1:'+str(port)+'/db_query', timeout = 30.0, headers = default_http_headers, data = data.encode('utf-8'))
	sys.exit(0)

for _ in range(count):
	data = '{"request":'+str(time.time())[:14]+'}'
	requests.post('http://127.0.0.1:'+str(port)+'/db_query', timeout = 30.0, headers = default_http_headers, data = data.encode('utf-8'))


Код js (nodejs) для TCP:

const express = require('express');
const app = express();

var port = 12333;
var arr = [];

async function db_query(data){
	if(data.request == 'stop'){
		var sum = arr.reduce((a, b) => a + b, 0);
		console.log('average', sum / arr.length);
		console.log('count', arr.length);
		arr = [];
		return;
	}
	arr.push((+(new Date()) / 1000) - data.request);
}

(async () => {

	app.use(express.json());
	app.post('/db_query', async function (req, res) {
		var result = await db_query(req.body);
		res.set('Content-Type', 'application/json');
		res.send(result+'\n');
	});
	app.listen(port, '127.0.0.1', function(){
		console.log('Listening 127.0.0.1:'+port+'...');
	});

})();


Для тестов Redis PUB/SUB

открываем 2 терминала на debian, в первом вставляем:
node subscriber.js

во втором:
python3 publisher.py 100
после выполнения
python3 publisher.py 0 stop

Для тестов TCP

открываем 2 терминала на debian, в первом вставляем:
node subscriber2.js

во втором:
python3 publisher2.py 100
после выполнения
python3 publisher2.py 0 stop

Вот.
Что думаете?
  • Вопрос задан
  • 1273 просмотра
Подписаться 4 Простой 2 комментария
Решения вопроса 1
Eugene-Usachev
@Eugene-Usachev
Я не знаток Redis и по его исходникам не лазил, но все адаптеры, что я смотрел и писал используют TCP, отсюда я делаю вывод, что Redis использует TCP под капотом. Следовательно вы поменяли структуру с
бек сервер - TCP -> бек сервер
на
бек сервер - TCP -> СУБД (не сервер, у неё своих забот хватает) - TCP -> бек сервер.
- TCP -> второй бек сервер.

Теперь про советы:
1 - PUB/SUB у Redis очень производительный, но вы неправильно его используете для этих целей. Я не знаток python и не очень хорошо знаю node, но не вижу у вас ни batching, ни pipelining. Я недавно проводил тесты на Go и пришёл к выводу, что pipelining для PUB/SUB даёт рост скорости более, чем в 4 раза. В документации к Redis очень хорошо описан подход pipeling и большинство адаптеров его так или иначе предлагают, советую провести тесты с ним.
2 - Вы не по назначению используете PUB/SUB. Для обмена между двумя серверами обычно используют gRPC или TCP или голые сокеты. PUB/SUB используется для масштабирования (я не вижу у вас его в коде, но я не знаток python или node).
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы