Каждый раз приятно удивляюсь, как с каждым следующим релизом Ruby работает всё быстрее и быстрее, особенно с YJIT - среди интерпретируемых языков равных нет - все остальные нервно курят в сторонке.
Особенно порадовало совершенно безболезненное обновление до версии Ruby 4.0.0 - установил Ruby (через rbenv), установил gem-ы (всего лишь bundle install; bundle update) - и все сайты на Ruby on Rails 8.1.x заработали!... только ещё быстрее, чем прежде!
Браво! Вот это язык, вот это система! Очень впечатляет развитие! Но в то же время и стабильность, зрелость языка! Так держать! Ruby, с Юбилеем! :-)
Для начала я бы посоветовал (скромно) автору всё же получше изучить Rust (если это вообще в принципе возможно для питониста (в чём я, честно говоря, сомневаюсь)). Даже в столь простом примере вы умудрились сделать ошибки - сравните хотя бы свой пример с тем же самым, но на странице библиотеки PyO3, о которой вы сами говорите. Я далеко не сторонник "питуха", а скорее даже наоборот, поэтому я думаю, что сама по себе эта идея, мягко выражаясь, не очень - изобретать на Rust очередные костыли для python.
Относительно недавно была издана книга "Programming Rust SECOND EDITION Fast, Safe Systems Development" Jim Blandy, Jason Orendorff, and Leonora F.S. Tindall (2021 год) Если какое-то издательство выпустит перевод её - это будет очень полезным материалом для изучения Rust. Первая редакция этой книги - это лучший материал для изучения Rust на русском языке, который я видел. Я уже написал электронное письмо в издательство ДМК-пресс (они выпустили перевод первой редакции этой книги "Язык программирования Rust" (2018 год)) с просьбой выпустить перевод и второй редакции этой полезной книги. Посмотрим, что получится... Не только же информацией про популярный ныне Питух пытаться кормить всех...
Могу посоветовать автору обратить внимание на язык программирования Ruby (вместо PHP, а уж тем более Python) - связка Ruby + Rust обещает быть очень перспективной. В Ruby включена поддержка связки с кодом Rust на уровне самой системы языка "bundle gem --ext=rust" - с использованием Magnus (rb_sys). Сам по себе Ruby уже сейчас использует Rust (для компиляции с поддержкой YJIT требуется установка Rust). Ruby обладает понятным, логичным и красивым синтаксисом, на Rust можно реализовывать критичные к скорости и безопасности участки кода.
Чтобы устранить путаницу, вот правило нумерации версий Ruby. Да, немного путаница с терминологией, т.к. в номере версии присутствует ещё и номер TEENY:
Номера версий имеют следующее значение:
MAJOR — увеличивается при несовместимом изменении, которое не может быть выпущено в MINOR.
MINOR — увеличивается каждый раз, может быть несовместима с API.
TEENY — исправление безопасности или ошибки, которое сохраняет совместимость API. Может быть увеличено более чем на 10 (например, 2.1.11) и выпускается каждые 2–3 месяца.
PATCH — количество коммитов с последнего выпуска MINOR (при выпуске MINOR сбрасывается на 0).
Да, действительно, есть некоторая путаница - минорная версия - это третья цифра в номере версии, а не вторая. После 3.3.xxx идёт версия 3.4.xxx - и это уже с некоторыми изменениями, дополнениями в языке. А далее внутри этих веток уже меняется минорная часть xxx - это как раз и есть исправления ошибок в самой этой "ветке", версии языка.
Скажем так, если меняется первая цифра, то это вообще очень существенные изменения в самом языке. Поэтому, я как-то привык рассматривать изменения только 2 и 3 цифр, соответственно, и называю их мажорная и минорная. Извиняюсь за некоторую неточность и собственную интерпретацию.
Но сейчас, я полагаю, смысл изменений в версиях Ruby вам должен быть более понятен.
Ещё раз повторюсь. В этих "параллельных" (как вы их называете) версиях языка есть некоторые отличия в самом языке. Например, в Ruby 3.4 был добавлен в стандарт анонимный аргумент it внутри блоков. Ранее были добавлены другие возможности языка. Вся эволюция описана тут: https://rubyreferences.github.io/rubychanges/evolution.html Минорные версии - это исправления ошибок и некоторые улучшения в ветке без изменений в самом языке.
У Ruby много чего и более приятного есть, чего у Python нет и не будет никогда. А Ruby тем временем развивается и улучшается - становится всё лучше, удобнее, мощнее и быстрее. Python выбран математиками за простоту (им ни к чему разбираться ещё и в программировании), а для программиста эта (кажущаяся) простота, зачастую - ущербность.
"Просто нет другого языка, который мог бы сравниться с Perl по возможностям обработки текста" Чушь полная! Пока Perl "отсутствовал" (скажем так) в других языках давно уже были реализованы все возможности, которыми обладал Perl. Регулярные выражения? PHP, Python, в Ruby вообще поддержка регулярок на уровне языка.
Поломался TIOBE, однозначно поломался!... либо кто-то занимается маркетингом с помощью него.
Самое разумное - рассматривать этот рейтинг как "собака лает, караван идёт" - ни жарко, ни холодно от него, но порой весело.
Ну и всё же вдогонку пример с Ractor, но с ruby-kafka вместо rdkafka. Чтоб тебе совсем не скучать! ))) Как видишь, вариантов решения на Ruby может быть множество, но только я понимаю, что это совсем не о тебе - ты ж великий спец только поумничать, а не найти решение. Про зеркало упомянул?... и это вполне объяснимо - когда заканчиваются аргументы, остаётся последний аргумент: "сам дурак!".
Я в курсе про FFI, rb-sys, Magnus и прочие связки Ruby с нативным кодом. Ты очередной раз мне об этом напомнил, и только ещё раз показал свою тупость. Видимо, ты единственный, кто пишет всё ПОЛНОСТЬЮ сам, без использования любых библиотек, верно? ))) Знаешь, твой код, наверное, настолько "чистый", что ни разу не обращается даже ни к одному системному вызову - только твой собственный идеальный мир.
Вызов в Ruby через FFI - это всего лишь обращение к библиотеке работы с Kafaka. Но для тебя это уже не Ruby! Браво! )))))
Ох и повеселил ты меня, конечно! ))) И где ж таких только раздают, как ты? Ты явно отстал от какого-то цирка. )))
P..S. Ах да, про Rails... Sinatra тут только в том смысле, чтобы тебе понятнее было. Вообще никто не мешает отрабатывать этот 'POST' в контроллере RoR.
P.P.S. Можешь продолжать вякать и дальше - это очень весело! Хоть настроение поднимаешь - и то вперёд! ;-) )))
# encoding: utf-8
# frozen_string_literal: true
require 'sinatra'
require 'kafka'
require 'json'
require 'etc'
require 'mutex_m'
set :bind, '0.0.0.0'
set :port, 4567
BROKERS = ['localhost:9092'] # ruby-kafka ожидает массив
TOPIC = 'events'
WORKERS = Etc.nprocessors
MAX_SIZE = 60_000
# Счётчик обработанных запросов
rps_count = 0
rps_mutex = Mutex.new
# Пул Ractor'ов
worker_index = 0
worker_mutex = Mutex.new
workers = Array.new(WORKERS) do | i |
Ractor.new(i, BROKERS, TOPIC) do | wid, brokers, topic |
# Создаём Kafka-клиент и producer внутри Ractor'а
kafka = Kafka.new(
seed_brokers: brokers,
client_id: "collector_worker_#{wid}"
)
# Синхронный продюсер (для полного контроля и ACK=all)
producer = kafka.sync_producer(
acks: :all,
max_retries: 3,
retry_backoff: 100
)
loop do
msg = Ractor.receive
break if msg == :shutdown
begin
# Парсим JSON с символами в ключах
data = JSON.parse(msg, symbolize_names: true)
# Фильтрация только разрешённых ключей
data.slice!(:type, :name, :a, :b)
# Анализируем сообщения
case data
in { type: 'greeting', name: String => name }
puts "[Ractor #{wid}] Greeting for #{name}"
in { type: 'sum', a: Integer => a, b: Integer => b }
puts "[Ractor #{wid}] Sum = #{a + b}"
else
puts "[Ractor #{wid}] Unrecognized event"
end
# Синхронная отправка (для гарантии ACK=all)
# Можно использовать async_producer, но sync проще для контроля
producer.deliver_message(msg, topic: topic)
rescue JSON::ParserError
warn "[Ractor #{wid}] Invalid JSON"
rescue => e
warn "[Ractor #{wid}] Error: #{e.class} #{e.message}"
ensure
# Важно: закрыть продюсер при выходе
producer&.close
end
end
end
end
# RPS-мониторинг
Thread.new do
loop do
sleep 1
count = rps_mutex.synchronize { rps_count.tap { rps_count = 0 } }
puts "[RPS] #{count} events/sec"
end
end
# HTTP endpoint для приёма событий
post '/events' do
body = request.body.read
halt 413, 'Payload too large' if body.bytesize > MAX_SIZE
halt 400, 'Empty payload' if body.empty?
# Считаем RPS
rps_mutex.synchronize { rps_count += 1 }
# Round-robin выбор Ractor'а
worker_idx = worker_mutex.synchronize { (worker_index += 1) % WORKERS }
workers[worker_idx].send(body)
status 202
'Accepted'
end
# Завершение работы
at_exit do
puts "[Main] Shutting down..."
# Остановить Ractor'ы
workers.each { | w | w.send(:shutdown) }
workers.each(&:take)
puts "[Main] All workers stopped."
end
ну и вот ещё вариантик... сиди разбирайся! заодно может хоть чуть-чуть с матчастью ознакомишься, "профессор" хренов! )))
# encoding: utf-8
# frozen_string_literal: true
require 'sinatra'
require 'rdkafka'
require 'json'
require 'etc'
require 'thread'
set :bind, '0.0.0.0'
set :port, 4567
BROKERS = 'localhost:9092'
TOPIC = 'events'
WORKERS = Etc.nprocessors
MAX_SIZE = 60_000
MAX_QUEUE_SIZE = 10_000
# Конфиг Kafka
RDKAFKA_CONFIG = {
'bootstrap.servers' => BROKERS,
'acks' => 'all',
'queue.buffering.max.ms' => '5',
'batch.num.messages' => '10000'
}
# === Первая очередь: для передачи событий в Ractor'ы (backpressure) ===
input_queue = SizedQueue.new(MAX_QUEUE_SIZE)
# === Вторая очередь: для отправки обработанных данных в Kafka ===
events_queue = SizedQueue.new(MAX_QUEUE_SIZE)
# === Главный producer (в main потоке) ===
producer = Rdkafka::Config.new(RDKAFKA_CONFIG).producer
# === Поток отправки в Kafka (из main потока) ===
# Запускаем фоновую отправку из main потока
# Поток отправки сообщений в Kafka (решает проблему Ractor::IsolationError в rdkafka)
# потому как rdkafka какого-то чёрта использует переменные класса, хотя давно
# всем известно, что их использование не рекомендует даже Matz
# поэтому работа с kafka будет производится в главном потоке
Thread.new do
loop do
msg = events_queue.pop
break if msg == :flush
begin
producer.produce_async(topic: TOPIC, payload: msg)
producer.poll(0)
rescue => e
warn "[Kafka Thread] Error: #{e.class} #{e.message}"
end
end
end
# === Пул Ractor'ов: читают из input_queue, обрабатывают, отправляют в events_queue ===
rps_count = 0
rps_mutex = Mutex.new
workers = Array.new(WORKERS) do | i |
Ractor.new(i, input_queue, events_queue) do | wid, input, output |
loop do
msg = input.pop # читаем из очереди ввода
break if msg == :shutdown
begin
data = JSON.parse(msg, symbolize_names: true)
data.slice!(:type, :name, :a, :b)
case data
in { type: 'greeting', name: String => name }
puts "[Ractor #{wid}] Greeting for #{name}"
in { type: 'sum', a: Integer => a, b: Integer => b }
puts "[Ractor #{wid}] Sum = #{a + b}"
else
puts "[Ractor #{wid}] Unrecognized event"
end
output << msg # в очередь для отправки в kafka
rescue JSON::ParserError
warn "[Ractor #{wid}] Invalid JSON"
rescue => e
warn "[Ractor #{wid}] Error: #{e.class} #{e.message}"
end
end
end
end
# === RPS-мониторинг ===
Thread.new do
loop do
sleep 1
count = rps_mutex.synchronize { rps_count.tap { rps_count = 0 } }
puts "[RPS] #{count} events/sec"
end
end
# === HTTP endpoint для приёма событий ===
post '/events' do
body = request.body.read
halt 413, 'Payload too large' if body.bytesize > MAX_SIZE
halt 400, 'Empty payload' if body.empty?
rps_mutex.synchronize { rps_count += 1 }
begin
input_queue.push(body, exception: false)
rescue ThreadError
halt 429, 'Too many requests'
end
status 202
'Accepted'
end
# === Завершение работы ===
at_exit do
puts "[Main] Shutting down..."
# 1. Отправить сигнал завершения всем Ractor'ам
workers.each { | worker | worker.send(:shutdown) }
# 2. Дождаться завершения всех Ractor'ов
workers.each(&:take)
# 3. Отправить сигнал завершения потоку Kafka
events_queue << :flush
# 4. Дождаться отправки всех сообщений
producer&.flush(5000)
puts "[Main] All workers stopped."
end
Слушай, ты, ограниченная умственной способностью сущность, - в твоей голове, похоже, вместо мозга только набор терминов и дешёвых понтов. С чего ты возомнил себя здесь профессором? Оценки он тут расставляет. Я не студент, и уж тем более не собираюсь слушать тебя, «учителя» с слабоумным подходом. Я предложил конкретные решения, в отличие от тебя, который даже не смог разобраться в элементарном коде, а уж о решениях и речи не идет.
Если у вас Kafka падает, то это никак не проблемы кода. Если до тебя не доходит, попробуй хотя бы немного поработать с этим кодом, исправить конфигурацию Kafka - это называется инженерный подход.
Мне глубоко параллельно, что ты думаешь обо мне. Ты уже успел блеснуть своим скудоумием на Хабре - умные люди давно поняли, кто ты на самом деле (если кто-то читает эту тему). А если у тебя в голове только «это невозможно», то это твои проблемы и, кстати, проблемы твоих работодателей, которые так и не получают нужного результата.
Мне немного стала интересна задача, вот и решил попробовать. Вот тебе ещё вариант. Но это уже точно последний. Не думаю, что ты в этом что-то поймёшь, но мне уже на это наплевать.
# encoding: utf-8
# frozen_string_literal: true
require 'sinatra'
require 'rdkafka'
require 'json'
require 'etc'
require 'mutex_m'
set :bind, '0.0.0.0'
set :port, 4567
BROKERS = 'localhost:9092'
TOPIC = 'events'
WORKERS = Etc.nprocessors
MAX_SIZE = 60_000
MAX_QUEUE_SIZE = 10000 # Максимальный размер очереди
# Конфиг Kafka
RDKAFKA_CONFIG = {
'bootstrap.servers' => BROKERS,
'acks' => 'all',
'queue.buffering.max.ms' => '5',
'batch.num.messages' => '10000'
}
# === Очередь для передачи данных из Ractor'ов в главный поток ===
# Используем SizedQueue для ограничения размера очереди
events_queue = SizedQueue.new(MAX_QUEUE_SIZE)
# === Главный producer (в main потоке) ===
producer = Rdkafka::Config.new(RDKAFKA_CONFIG).producer
# Запускаем фоновую отправку из main потока
# Поток отправки сообщений в Kafka (решает проблему Ractor::IsolationError в rdkafka)
# потому как rdkafka какого-то чёрта использует переменные класса, хотя давно
# всем известно, что их использование не рекомендует даже Matz
# поэтому работа с kafka будет производится в главном потоке
Thread.new do
loop do
msg = events_queue.pop # Блокируется, пока не появится сообщение
next if msg == :flush # Сигнал на завершение
begin
producer.produce_async(topic: TOPIC, payload: msg)
producer.poll(0) # Обработка колбэков
rescue => e
warn "[Kafka Thread] Error: #{e.class} #{e.message}"
end
end
end
# === Пул Ractor'ов (только обработка) ===
rps_count = 0
rps_mutex = Mutex.new
worker_index = 0
worker_mutex = Mutex.new
workers = Array.new(WORKERS) do | i |
Ractor.new(i, events_queue) do | wid, queue |
loop do
msg = Ractor.receive
break if msg == :shutdown
begin
# Парсим JSON с символами в ключах
data = JSON.parse(msg, symbolize_names: true)
# Фильтрация только разрешённых ключей
data.slice!(:type, :name, :a, :b)
# Анализируем сообщения
case data
in { type: 'greeting', name: String => name }
puts "[Ractor #{wid}] Greeting for #{name}"
in { type: 'sum', a: Integer => a, b: Integer => b }
puts "[Ractor #{wid}] Sum = #{a + b}"
else
puts "[Ractor #{wid}] Unrecognized event"
end
# Отправляем в очередь (не в Kafka!)
queue << msg
rescue JSON::ParserError
warn "[Ractor #{wid}] Invalid JSON"
rescue => e
warn "[Ractor #{wid}] Error: #{e.class} #{e.message}"
end
end
end
end
# === RPS-мониторинг ===
Thread.new do
loop do
sleep 1
count = rps_mutex.synchronize { rps_count.tap { rps_count = 0 } }
puts "[RPS] #{count} events/sec"
end
end
# === HTTP endpoint для приёма событий ===
post '/events' do
body = request.body.read
halt 413, 'Payload too large' if body.bytesize > MAX_SIZE
halt 400, 'Empty payload' if body.empty?
# Считаем RPS
rps_mutex.synchronize { rps_count += 1 }
# Попытка отправить сообщение в очередь
begin
# Добавляем сообщение в очередь с отклонением при переполнении
events_queue.push(body, exception: false)
rescue ThreadError
halt 429, 'Too many requests'
end
status 202
'Accepted'
end
# === Завершение работы ===
at_exit do
puts "[Main] Shutting down..."
# 1. Остановить Ractor'ы
workers.each { | w | w.send(:shutdown) }
workers.each(&:take)
# 2. Отправить сигнал завершения в поток Kafka
events_queue << :flush
# 3. Дождаться отправки всех сообщений
producer&.flush(5000)
puts "[Main] All workers stopped."
end
Как же сложно иметь дело со всякими "дереволазами", нахватавшимися терминов, а по сути так и оставшимися деревянными по пояс (при чём сверху). Ты сам научись читать и улавливать суть! «ни C, ни Rust тут не нужны» - это было сказано вообще в отношении тех, кто считает, что Ruby с чем-то не справится. Вот тебе даже ни CRUD, а просто POST! Пример на чистом Ruby, но, как я и говорил, с использованием Ractor. Для этого небольшого примера не надо создавать отдельный проект, а уж тем более незачем публиковать его на Git (GitHub). Я хотел тебе прислать на почту, но ты, видимо, предпочитаешь публичное унижение. Очень печально, что идиотизм пробирается в программистские ряды, очень печально!... На будущее - не суди о людях по себе, и уж если человек о чём-то упоминает, то наверное он имеет представление, о чём он говорит. И да, не показывай этот код коллегам, а то, вполне возможно, тебя попрут с твоей работы за твой код, хотя... может он и хорош, не буду утверждать. P.S. И всё же очень интересно, что у вас там за такие коллосы вычислительные, что способны прожевать 10K x 50K ~= 500Mb/s ... или ты это сам всё придумал ради красного словца?
Вот на этом точно всё! Успехов тебе, умник!
# frozen_string_literal: true
require 'sinatra'
require 'rdkafka'
require 'json'
require 'etc'
require 'mutex_m'
set :bind, '0.0.0.0'
set :port, 4567
BROKERS = 'localhost:9092'
TOPIC = 'events'
WORKERS = Etc.nprocessors
MAX_SIZE = 60_000
# Конфиг Kafka
RDKAFKA_CONFIG = {
'bootstrap.servers' => BROKERS,
'acks' => 'all',
'queue.buffering.max.ms' => '5',
'batch.num.messages' => '10000'
}
# Пул Ractor'ов
rps_count = 0
rps_mutex = Mutex.new
worker_index = 0
worker_mutex = Mutex.new
workers = Array.new(WORKERS) do |i|
Ractor.new(i, RDKAFKA_CONFIG, TOPIC) do |wid, config, topic|
producer = Rdkafka::Config.new(config).producer
loop do
msg = Ractor.receive
break if msg == :shutdown # Завершаем работу
begin
# Парсим JSON с символами в ключах
data = JSON.parse(msg, symbolize_names: true)
# Фильтрация только разрешённых ключей
data.slice!(:type, :name, :a, :b)
# Анализируем сообщения
case data
in { type: :greeting, name: String => name }
puts "[Ractor #{wid}] Greeting for #{name}"
in { type: :sum, a: Integer => a, b: Integer => b }
Но это ведь ИИ - очень хороший помощник, если относиться к нему именно как к помощнику, а не как к творцу, то всё очень даже хорошо!
Возможно, именно благодаря ИИ многие программные системы будут приведены в порядок, будут найдены и исправлены ошибки... и Windows после рефакторинга всего кода в итоге превратится... в Linux ! ;-) )))
Забавно, как некоторые уверенно оперируют словами " CPU-bound", “IO-bound” и “GVL”, не понимая, что в реальной системе это совершенно разные вещи. Любой, кто хотя бы читал Баха про архитектуру UNIX, знает, что спящий процесс при ожидании ввода-вывода отдаёт управление планировщику, а в Ruby в этот момент GVL освобождается. Kafka при этом прекрасно жонглирует соединениями, а CPU Ruby даже не успевает заскучать. Поэтому разговоры в духе “тут обязательно нужен Rust или C, иначе всё упрётся” звучат как обсуждение тормозов у велосипеда на парковке: вроде умные слова есть, а контекста — ноль. Советую для начала разобрать, что именно делает планировщик ОС и где на самом деле узкое место, а уже потом жонглировать терминами. Это избавит от неловких ситуаций, когда инженер со стажем слушает лекцию по “матчасти” от человека, который с ней ещё не очень-то знаком. Понахватавшись терминов, ты ещё не становишься автоматически умнее.
Адрес почты напиши. Я тебе вышлю код. Я не собираюсь для тебя оформлять целый проект, ты сам умный - разберёшься что к чему. Нет адреса - разговор окончен. На этом всё.
Сегодня aptitude с debian.org - ооочень медленно (10-30Kb/s) качалось новое ядро.
"Профессионалы" РКН во всей красе - браво ё-маё!
да-да-да, Ruby чуть ли ни с рождения хоронят... всё никак похоронить не могут ;-) )))
https://inet777.ru/ruby-is-dead
Каждый раз приятно удивляюсь, как с каждым следующим релизом Ruby работает всё быстрее и быстрее, особенно с YJIT - среди интерпретируемых языков равных нет - все остальные нервно курят в сторонке.
Особенно порадовало совершенно безболезненное обновление до версии Ruby 4.0.0 - установил Ruby (через rbenv), установил gem-ы (всего лишь bundle install; bundle update) - и все сайты на Ruby on Rails 8.1.x заработали!... только ещё быстрее, чем прежде!
Браво! Вот это язык, вот это система! Очень впечатляет развитие! Но в то же время и стабильность, зрелость языка! Так держать! Ruby, с Юбилеем! :-)
Для начала я бы посоветовал (скромно) автору всё же получше изучить Rust (если это вообще в принципе возможно для питониста (в чём я, честно говоря, сомневаюсь)). Даже в столь простом примере вы умудрились сделать ошибки - сравните хотя бы свой пример с тем же самым, но на странице библиотеки PyO3, о которой вы сами говорите.
Я далеко не сторонник "питуха", а скорее даже наоборот, поэтому я думаю, что сама по себе эта идея, мягко выражаясь, не очень - изобретать на Rust очередные костыли для python.
Относительно недавно была издана книга
"Programming Rust SECOND EDITION Fast, Safe Systems Development"
Jim Blandy, Jason Orendorff, and
Leonora F.S. Tindall
(2021 год)
Если какое-то издательство выпустит перевод её - это будет очень полезным материалом для изучения Rust. Первая редакция этой книги - это лучший материал для изучения Rust на русском языке, который я видел.
Я уже написал электронное письмо в издательство ДМК-пресс (они выпустили перевод первой редакции этой книги "Язык программирования Rust" (2018 год)) с просьбой выпустить перевод и второй редакции этой полезной книги. Посмотрим, что получится... Не только же информацией про популярный ныне Питух пытаться кормить всех...
Могу посоветовать автору обратить внимание на язык программирования Ruby (вместо PHP, а уж тем более Python) - связка Ruby + Rust обещает быть очень перспективной. В Ruby включена поддержка связки с кодом Rust на уровне самой системы языка "bundle gem --ext=rust" - с использованием Magnus (rb_sys).
Сам по себе Ruby уже сейчас использует Rust (для компиляции с поддержкой YJIT требуется установка Rust).
Ruby обладает понятным, логичным и красивым синтаксисом, на Rust можно реализовывать критичные к скорости и безопасности участки кода.
Чтобы устранить путаницу, вот правило нумерации версий Ruby. Да, немного путаница с терминологией, т.к. в номере версии присутствует ещё и номер TEENY:
Номера версий имеют следующее значение:
MAJOR — увеличивается при несовместимом изменении, которое не может быть выпущено в MINOR.
MINOR — увеличивается каждый раз, может быть несовместима с API.
TEENY — исправление безопасности или ошибки, которое сохраняет совместимость API. Может быть увеличено более чем на 10 (например, 2.1.11) и выпускается каждые 2–3 месяца.
PATCH — количество коммитов с последнего выпуска MINOR (при выпуске MINOR сбрасывается на 0).
Да, действительно, есть некоторая путаница - минорная версия - это третья цифра в номере версии, а не вторая. После 3.3.xxx идёт версия 3.4.xxx - и это уже с некоторыми изменениями, дополнениями в языке. А далее внутри этих веток уже меняется минорная часть xxx - это как раз и есть исправления ошибок в самой этой "ветке", версии языка.
Скажем так, если меняется первая цифра, то это вообще очень существенные изменения в самом языке. Поэтому, я как-то привык рассматривать изменения только 2 и 3 цифр, соответственно, и называю их мажорная и минорная. Извиняюсь за некоторую неточность и собственную интерпретацию.
Но сейчас, я полагаю, смысл изменений в версиях Ruby вам должен быть более понятен.
Ещё раз повторюсь. В этих "параллельных" (как вы их называете) версиях языка есть некоторые отличия в самом языке. Например, в Ruby 3.4 был добавлен в стандарт анонимный аргумент it внутри блоков. Ранее были добавлены другие возможности языка. Вся эволюция описана тут: https://rubyreferences.github.io/rubychanges/evolution.html
Минорные версии - это исправления ошибок и некоторые улучшения в ветке без изменений в самом языке.
Ruby постоянно улучшается, в язык добавляются новые возможности.
Изменения в версиях описаны, например, здесь:
https://rubyreferences.github.io/rubychanges/
+1
У Ruby много чего и более приятного есть, чего у Python нет и не будет никогда. А Ruby тем временем развивается и улучшается - становится всё лучше, удобнее, мощнее и быстрее.
Python выбран математиками за простоту (им ни к чему разбираться ещё и в программировании), а для программиста эта (кажущаяся) простота, зачастую - ущербность.
"Просто нет другого языка, который мог бы сравниться с Perl по возможностям обработки текста"
Чушь полная! Пока Perl "отсутствовал" (скажем так) в других языках давно уже были реализованы все возможности, которыми обладал Perl.
Регулярные выражения? PHP, Python, в Ruby вообще поддержка регулярок на уровне языка.
Поломался TIOBE, однозначно поломался!... либо кто-то занимается маркетингом с помощью него.
Самое разумное - рассматривать этот рейтинг как "собака лает, караван идёт" - ни жарко, ни холодно от него, но порой весело.
Ну и всё же вдогонку пример с Ractor, но с ruby-kafka вместо rdkafka. Чтоб тебе совсем не скучать! ))) Как видишь, вариантов решения на Ruby может быть множество, но только я понимаю, что это совсем не о тебе - ты ж великий спец только поумничать, а не найти решение. Про зеркало упомянул?... и это вполне объяснимо - когда заканчиваются аргументы, остаётся последний аргумент: "сам дурак!".
Я в курсе про FFI, rb-sys, Magnus и прочие связки Ruby с нативным кодом. Ты очередной раз мне об этом напомнил, и только ещё раз показал свою тупость. Видимо, ты единственный, кто пишет всё ПОЛНОСТЬЮ сам, без использования любых библиотек, верно? ))) Знаешь, твой код, наверное, настолько "чистый", что ни разу не обращается даже ни к одному системному вызову - только твой собственный идеальный мир.
Вызов в Ruby через FFI - это всего лишь обращение к библиотеке работы с Kafaka. Но для тебя это уже не Ruby! Браво! )))))
Ох и повеселил ты меня, конечно! ))) И где ж таких только раздают, как ты? Ты явно отстал от какого-то цирка. )))
P..S. Ах да, про Rails... Sinatra тут только в том смысле, чтобы тебе понятнее было. Вообще никто не мешает отрабатывать этот 'POST' в контроллере RoR.
P.P.S. Можешь продолжать вякать и дальше - это очень весело! Хоть настроение поднимаешь - и то вперёд! ;-) )))
ну и вот ещё вариантик... сиди разбирайся! заодно может хоть чуть-чуть с матчастью ознакомишься, "профессор" хренов! )))
Слушай, ты, ограниченная умственной способностью сущность, - в твоей голове, похоже, вместо мозга только набор терминов и дешёвых понтов. С чего ты возомнил себя здесь профессором? Оценки он тут расставляет. Я не студент, и уж тем более не собираюсь слушать тебя, «учителя» с слабоумным подходом. Я предложил конкретные решения, в отличие от тебя, который даже не смог разобраться в элементарном коде, а уж о решениях и речи не идет.
Если у вас Kafka падает, то это никак не проблемы кода. Если до тебя не доходит, попробуй хотя бы немного поработать с этим кодом, исправить конфигурацию Kafka - это называется инженерный подход.
Мне глубоко параллельно, что ты думаешь обо мне. Ты уже успел блеснуть своим скудоумием на Хабре - умные люди давно поняли, кто ты на самом деле (если кто-то читает эту тему). А если у тебя в голове только «это невозможно», то это твои проблемы и, кстати, проблемы твоих работодателей, которые так и не получают нужного результата.
Мне немного стала интересна задача, вот и решил попробовать. Вот тебе ещё вариант. Но это уже точно последний. Не думаю, что ты в этом что-то поймёшь, но мне уже на это наплевать.
чуть позже заметил ошибку, правильно так:
case datain { type: 'greeting', name: String => name }puts "[Ractor #{wid}] Greeting for #{name}"in { type: 'sum', a: Integer => a, b: Integer => b }puts "[Ractor #{wid}] Sum = #{a + b}"elseputs "[Ractor #{wid}] Unrecognized event"endКак же сложно иметь дело со всякими "дереволазами", нахватавшимися терминов, а по сути так и оставшимися деревянными по пояс (при чём сверху). Ты сам научись читать и улавливать суть! «ни C, ни Rust тут не нужны» - это было сказано вообще в отношении тех, кто считает, что Ruby с чем-то не справится. Вот тебе даже ни CRUD, а просто POST! Пример на чистом Ruby, но, как я и говорил, с использованием Ractor. Для этого небольшого примера не надо создавать отдельный проект, а уж тем более незачем публиковать его на Git (GitHub). Я хотел тебе прислать на почту, но ты, видимо, предпочитаешь публичное унижение.
Очень печально, что идиотизм пробирается в программистские ряды, очень печально!...
На будущее - не суди о людях по себе, и уж если человек о чём-то упоминает, то наверное он имеет представление, о чём он говорит.
И да, не показывай этот код коллегам, а то, вполне возможно, тебя попрут с твоей работы за твой код, хотя... может он и хорош, не буду утверждать.
P.S. И всё же очень интересно, что у вас там за такие коллосы вычислительные, что способны прожевать 10K x 50K ~= 500Mb/s ... или ты это сам всё придумал ради красного словца?
Вот на этом точно всё! Успехов тебе, умник!
# frozen_string_literal: truerequire 'sinatra'require 'rdkafka'require 'json'require 'etc'require 'mutex_m'set :bind, '0.0.0.0'set :port, 4567BROKERS = 'localhost:9092'TOPIC = 'events'WORKERS = Etc.nprocessorsMAX_SIZE = 60_000# Конфиг KafkaRDKAFKA_CONFIG = {'bootstrap.servers' => BROKERS,'acks' => 'all','queue.buffering.max.ms' => '5','batch.num.messages' => '10000'}# Пул Ractor'овrps_count = 0rps_mutex = Mutex.newworker_index = 0worker_mutex = Mutex.newworkers = Array.new(WORKERS) do |i|Ractor.new(i, RDKAFKA_CONFIG, TOPIC) do |wid, config, topic|producer = Rdkafka::Config.new(config).producerloop domsg = Ractor.receivebreak if msg == :shutdown # Завершаем работуbegin# Парсим JSON с символами в ключахdata = JSON.parse(msg, symbolize_names: true)# Фильтрация только разрешённых ключейdata.slice!(:type, :name, :a, :b)# Анализируем сообщенияcase datain { type: :greeting, name: String => name }puts "[Ractor #{wid}] Greeting for #{name}"in { type: :sum, a: Integer => a, b: Integer => b }puts "[Ractor #{wid}] Sum = #{a + b}"elseputs "[Ractor #{wid}] Unrecognized event"end# Асинхронная отправка в Kafka без блокировкиproducer.produce_async(topic: topic, payload: msg)producer.poll(0) # Обработка колбэковrescue JSON::ParserErrorwarn "[Ractor #{wid}] Invalid JSON"rescue => ewarn "[Ractor #{wid}] Error: #{e.class} #{e.message}"endendendend# Выводим количество обработанных событий в секундуThread.new doloop dosleep 1count = rps_mutex.synchronize { rps_count.tap { rps_count = 0 } }puts "[RPS] #{count} events/sec"endend# HTTP endpoint для приёма событийpost '/events' dobody = request.body.readhalt 413, 'Payload too large' if body.bytesize > MAX_SIZEhalt 400, 'Empty payload' if body.empty?# Считаем количество запросов в секундуrps_mutex.synchronize { rps_count += 1 }# Используем round-robin для выбора работникаworker_idx = worker_mutex.synchronize { (worker_index += 1) % WORKERS }workers[worker_idx].send(body)status 202'Accepted'end# Завершаем работу всех Ractor'ов при выходе из программыat_exit doputs "[Main] Shutting down..."workers.each { |w| w.send(:shutdown) }workers.each(&:take) # ждём завершенияputs "[Main] All workers stopped."endНо это ведь ИИ - очень хороший помощник, если относиться к нему именно как к помощнику, а не как к творцу, то всё очень даже хорошо!
Возможно, именно благодаря ИИ многие программные системы будут приведены в порядок, будут найдены и исправлены ошибки... и Windows после рефакторинга всего кода в итоге превратится... в Linux ! ;-) )))
Забавно, как некоторые уверенно оперируют словами " CPU-bound", “IO-bound” и “GVL”, не понимая, что в реальной системе это совершенно разные вещи. Любой, кто хотя бы читал Баха про архитектуру UNIX, знает, что спящий процесс при ожидании ввода-вывода отдаёт управление планировщику, а в Ruby в этот момент GVL освобождается. Kafka при этом прекрасно жонглирует соединениями, а CPU Ruby даже не успевает заскучать. Поэтому разговоры в духе “тут обязательно нужен Rust или C, иначе всё упрётся” звучат как обсуждение тормозов у велосипеда на парковке: вроде умные слова есть, а контекста — ноль. Советую для начала разобрать, что именно делает планировщик ОС и где на самом деле узкое место, а уже потом жонглировать терминами. Это избавит от неловких ситуаций, когда инженер со стажем слушает лекцию по “матчасти” от человека, который с ней ещё не очень-то знаком. Понахватавшись терминов, ты ещё не становишься автоматически умнее.
Адрес почты напиши. Я тебе вышлю код. Я не собираюсь для тебя оформлять целый проект, ты сам умный - разберёшься что к чему.
Нет адреса - разговор окончен. На этом всё.