注意:这些发布说明只包括主要的变化。
注意:上述变化不包括为你的非Rails项目设置Zeitwerk。
注意:如果你使用Sidekiq后端,请记住,在升级之前,你需要消耗所有已经在Redis中的消息。
变化(功能、不兼容等)
自动重新加载开发中的代码变更
到现在为止,为了在Karafka进程中看到你的代码变化,你必须重新启动它。这真的很麻烦,因为对于更大、更复杂的Kafka集群来说,重新启动与重新连接和重新平衡可能需要大量的时间。幸运的是,这些时间已经过去了
你所需要做的就是在你的karafka.rb 文件中的App.boot 之前启用这部分代码。
# For non-Rails app with Zeitwerk loader
if Karafka::App.env.development?
Karafka.monitor.subscribe(
Karafka::CodeReloader.new(
APP_LOADER
)
)
end
# Or for Ruby on Rails
if Karafka::App.env.development?
Karafka.monitor.subscribe(
Karafka::CodeReloader.new(
*Rails.application.reloaders
)
)
end
而你的代码修改将在每次消息/消息批处理后被应用。
但请记住,它有一些限制。
- 路由的变化不会被反映出来。这将需要重新连接,并使重新加载变得非常复杂。
- 你在Karafka框架之外但仍在其中运行的任何后台工作,可能不会在重载时被发现。
- 如果你使用跨越多个批次的内存消费者数据缓冲(或单一消息获取模式下的消息),它将无法工作,因为代码重载意味着重新初始化所有的消费者实例。在这种情况下,你最好不要使用重载模式。
同样值得指出的是,如果你有一个代码,在重载阶段应该以任何方式被重新初始化,你可以把它传递给Karafka::CodeReloader 初始化器。
if Karafka::App.env.development?
Karafka.monitor.subscribe(
Karafka::CodeReloader.new(
*Rails.application.reloaders
) { Dry::Events::Publisher.registry.clear }
)
end
解析器现在是路由中的反序列化器,接受整个Karafka::Params::Params对象
解析器作为一个概念,它将负责对违反SRP的数据进行序列化和反序列化(详见这里)。从现在开始,它们是独立的实体,你可以独立使用。在升级时,只需将你在路由中使用的每个主题的解析器重命名为反序列化器。
App.consumer_groups.draw do
consumer_group :batched_group do
batch_fetching true
topic :xml_data do
consumer XmlMessagesConsumer
batch_consuming false
# parser XmlDeserializer.new
deserializer XmlDeserializer.new
end
end
end
并确保,你自己提取消息的payload 。
class XmlDeserializer
# @param params [Karafka::Params::Params] params to de-serialize
# @return [Hash] deserialized xml
# @example:
# XmlDeserializer.new.call('<node>n</node>')
def call(params)
::Hash.from_xml(params.payload)
end
end
Zeitwerk支持Karafka::Loader
注意:如果你在Ruby on Rails上使用Karafka,你可以跳过这一节。
我们并不是最擅长加载东西的人。Zeitwerk才是。这就是为什么我们放弃了我们的自定义加载器,而选择了它。
只要在配置应用之前,在你的karafka.rb文件中加载你的应用代码,你就可以开始使用了。
APP_LOADER = Zeitwerk::Loader.new
%w[
lib
app/consumers
app/responders
app/workers
].each(&APP_LOADER.method(:push_dir))
APP_LOADER.setup
APP_LOADER.eager_load
class App < Karafka::App
# config here...
end
不要忘记eager_load代码,否则一些Karafka组件可能无法按预期工作。
消息的有效载荷现在可以在'payload'键下使用,不需要root合并
这可能是这个版本中最大的变化。
到目前为止,你的数据在收到时是在#params_batch中的每个params实例的根范围内可用。
这意味着,当你发送一条消息时,你可以像下面这样访问它
WaterDrop::SyncProducer.call(
{ login: 'maciek', id: '1' },
topic: 'users'
)
你会像这样访问它。
def consume
params_batch.each do |params|
puts "Hello #{params['login']}!\n"
end
end
Karafka曾经在Karafka::Params::Params对象根范围内直接合并你的数据。这很方便,但不够灵活。在根params范围内有一些元数据细节可能会被覆盖,另外,如果你发送的不是JSON哈希值,比方说一个数组,你会得到一个异常,你必须使用一个自定义解析器来绕过它(见这个FAQ问题)。
由于这个原因,并且为了更好地将你的传入数据与有效载荷的其他部分(标题、元数据信息等)分开,从现在开始,你的所有数据都将在有效载荷参数键下可用。
def consume
params_batch.each do |params|
puts "Hello #{params['payload']['login']}!\n"
# or
puts "Hello #{params.payload['login']}!\n"
end
en
这同样适用于你想访问未解析数据的情况。
def consume
params_batch.to_a.each |params|
puts "Unparsed details: #{params['payload']}"
end
end
元数据支持
在batch_fetching模式下,当从Kafka集群中获取数据时,会收到额外的信息。这些细节可以通过**#metadata**消费者方法获得。
class UsersConsumer < ApplicationConsumer
def consume
puts metadata
#=> { batch_size: 200, topic: 'events', partition: 2 }
end
end
消息头支持
在大多数消息系统(JMS、QPID等)、流媒体系统和大多数传输系统(HTTP、TCP)中,通常都有一个头和有效载荷的概念。
传统上,有效载荷是用于业务对象的,而报头传统上是用于传输路由、过滤等。头信息是最典型的key=value对。
WaterDrop和Karafka现在都支持消息头。
WaterDrop::SyncProducer.call(
{ login: 'maciek', id: '1' },
topic: 'users',
headers: { event: 'created' }
)
# Karafka consumer
def consume
puts params_batch.last.headers #=> { 'event' => 'created' }
end
RSpec帮助器使消费者测试更加容易
到现在为止,为了测试消费者,你必须知道Karafka存储Kafka消息的内部格式。这不再是真的了
我们创建了一个新的库,叫做Karafka-Testing,它将为你提供所有的方法,使你的消费者测试更加容易。
安装
将这个gem添加到你的Gemfile中的测试组中。
group :test do
gem 'karafka-testing'
gem 'rspec'
end
然后在你的spec_helper.rb文件中。
require 'karafka/testing/rspec/helpers'
RSpec.configure do |config|
config.include Karafka::Testing::RSpec::Helpers
end
使用方法
一旦包含在你的RSpec设置中,这个库将为你提供两个方法,你可以在你的规格中使用。
-#karafka_consumer_for- 这个方法将为所需主题创建一个消费者实例。它需要被设置为规格主题。
-#publish_for_karafka- 这个方法将 "发送 "消息到消费者实例。
注意:使用`#publish_for_karafka`方法发送的消息将不会被发送到Kafka。它们将被 "虚拟 "委托给创建的消费者实例,所以你的规范可以在没有Kafka设置的情况下运行。
RSpec.describe InlineBatchConsumer do
# This will create a consumer instance with all the
# settings defined for the given topic
subject(:consumer) do
karafka_consumer_for(:inline_batch_data)
end
let(:nr1_value) { rand }
let(:nr2_value) { rand }
let(:sum) { nr1_value + nr2_value }
before do
# Sends first message to Karafka consumer
publish_for_karafka({ 'number' => nr1_value }.to_json)
# Sends second message to Karafka consumer
publish_for_karafka({ 'number' => nr2_value }.to_json)
allow(Karafka.logger).to receive(:info)
end
it 'expects to log a proper message' do
expect(Karafka.logger)
.to receive(:info).with(
"Sum of 2 elements equals to: #{sum}"
)
consumer.consume
end
end
仪器仪表的统一
我们对默认监听器和Karafka运行时流程执行期间发布的事件名称做了一些小改动。更多细节请见此提交。
Karafka::Instrumentation::Listener现在是Karafka::Instrumentation::StdoutListener。
这个监听器有了重命名,并切换到实例化版本。
Karafka.monitor.subscribe(
# Old
Karafka::Instrumentation::Listener
# New
Karafka::Instrumentation::StdoutListener.new
)
Karafka::Instrumentation::ProctitleListener已被添加。
添加了名为Karafka::Instrumentation::ProctitleListener的新工具。它的目的是为你提供一个带有描述性数值的更漂亮的proc标题。为了使用它,请在你的karafka.rb引导文件中加入以下一行。
Karafka.monitor.subscribe(
Karafka::Instrumentation::ProctitleListener.new
)
mark_as_consumed分为mark_as_consumed和mark_as_consumed!
一个阻塞的**#mark_as_consumed**方法被分成了两个。
- #mark_as_consumed- 用于非阻塞的最终偏移承诺。
- #mark_as_consumed!- 是一个阻塞的偏移量承诺,它将停止处理流程,以确保偏移量已经被存储。
#payloads用于params_batch,从params_batch中只提取对象的有效载荷。
如果你对额外的`#params`元数据不感兴趣,你可以使用`#payloads`方法,只访问Kafka消息反序列化的有效载荷。
class EventsConsumer < ApplicationConsumer
def consume
EventStore.store params_batch.payloads
end
end
单个消费者类支持一个以上的主题
从现在开始,你可以为多个主题使用同一个消费者类。
App.consumer_groups.draw do
consumer_group :default do
topic :users do
consumer UsersConsumer
end
topic :admins do
consumer UsersConsumer
end
end
end
注意:你仍然会在每个主题分区中拥有独立的实例。
关键故障时的延迟重新连接
如果发生关键故障(网络断开或任何类似的情况),Karafka将退缩并等待reconnect_timeout(默认为10s)后再尝试重新连接。这应该可以防止你在出现严重问题时被错误和日志所堵塞。
放弃对Kafka 0.10的支持,转而支持Kafka 0.11的本机。
对Kafka 0.10的支持已被放弃。如果你决定将Kafka 0.10与Karafka 1.3一起使用,可能会发生奇怪的事情,所以只要升级即可。
重组响应者--multiple_usage constrain不再可用
multiple_usage已经被移除。如果你决定向同一个主题发送多条消息而不声明的话,响应者不会引发任何异常。这个功能是个坏主意,在长期运行的分批流中使用响应器时,会产生很多麻烦。
以下代码在Karafka 1.2中会引发Karafka::Errors::InvalidResponderUsageError错误,但在Karafka 1.3中可以继续运行。
class ExampleResponder < ApplicationResponder
topic :regular_topic
def respond(user, profile)
respond_to :regular_topic, user
respond_to :regular_topic, user
end
end
异常名称标准化
所有Karafka内部框架的异常名称现在都以Error后缀结尾。请看这个文件,了解整个异常列表。
默认的fetcher_max_queue_size从100改为10,以降低最大内存使用量
当Karafka在处理时,ruby-kafka会在一个单独的线程中预先缓冲更多数据。如果你有一个大的消费者滞后,这可能会导致你的Karafka进程在前期预缓冲数百或更多的数据。降低队列大小使得Karafka在默认情况下更容易预测。
开始使用Karafka
如果你想尽快开始使用Kafka和Karafka,那么最好的办法就是直接克隆我们的示例仓库。
git clone https://github.com/karafka/example-app ./example_app
然后,捆绑安装所有的依赖项。
cd ./example_app
bundle install