按照时间线,记录一次lua写数据到kafka中碰到的事故
Q1:所有的数据写到kafka的同一个分区里面
A1:每次写入时,都重新创建了连接,这样数据就只保存到一个分区里面了
function connectKafka(kafkaConfig)
local bp, err = producer:new(kafkaConfig.broker_list)
if not bp then
ngx.log(ngx.INFO, "failed to connect: ", err)
return nil,err
end
return bp
end
-----
local bp = connectKafka(kafkaConfig)
local ok, err = bp:send(topic,nil,content)
-----
查过资料后,可以事先进行分区的计算,在发送数据时指定分区,因此nginx.conf中要加个配置,保存用于运算的数据
lua_shared_dict shared_data 1m;
作用:设置一个共享全局变量表,在所有worker进程间共享
然后加入计算分区的方法:
function getPartitionId()
// kafka分区的个数
local partition_num = 2
local shared_data = ngx.shared.shared_data
local sharedKey = "shared_Key"
local key_val = shared_data:get(sharedKey)
if not key_val or key_val > 100000 then
key_val = 1
shared_data:set(sharedKey,key_val)
end
local partition_id = ""..tonumber(key_val%partition_num)
shared_data:incr(sharedKey,1)
return partition_id
end
-----
local bp = connectKafka(kafkaConfig)
local pId = getPartitionId()
local ok, err = bp:send(topic,pId,content)
-----
Q2:按理说数据就应该在五个分区中轮流写入,但是结果是0号分区一潭死水,数据量根本没有发生变化,但是也没有报错,说明指定0号分区的数据没有丢,只是没往0分区写
A2:lua指定分区的算法可能有问题,后台java程序不指定分区也能在不同的分区间写入数据,干脆不再每次去创建连接,试试看
local bp = nil
function connectKafka(kafkaConfig)
if not bp then
bp, err = producer:new(kafkaConfig.broker_list)
if not bp then
ngx.log(ngx.INFO, "failed to connect: ", err)
end
end
end
-----
connectKafka(kafkaConfig)
local ok, err = bp:send(topic,nil,content)
if not ok then
bp = nil
end
-----
这种办法,重启nginx之后,果然数据被基本均匀的保存到了不同的分区中去了
Q3:很快啊,又出现了新的问题,连接丢失写入失败,尽管做了一旦发生错误就将连接重置的逻辑,但是存三条丢一条的情况在环境中是绝不能接受的!
A3:怀疑是主机的性能不行,每来一条数据就写一次不太好,就把发送改成了异步的
bp, err = producer:new(kafkaConfig.broker_list,{ producer_type = "async" })
默认一次200条或50K
发现数据依然能均匀的写入到不同的分区中,并且sendKafka方法没有再提示写入失败的错误
Q4:又很快啊,丢数据的事情还是发生了,200条中能有几条数据因为写入失败被丢,甚至导致了更严重的事情发生------一条数据都没写入,重启nginx进程又恢复了正常(建议写一个15分钟重启一次nginx的crontab)
A4:看了下kafka插件的producer.lua,发现指定key其实并不是partition_id,而是又进行了计算:
local function default_partitioner(key, num, correlation_id)
local id = key and crc32(key) or correlation_id
ngx.log(ngx.INFO,"id:",id," key:",key," crc32(key):",crc32(key)," cId:",correlation_id," pId:", id%num)
-- partition_id is continuous and start from 0
return id % num
end
根据添加的打印语句,发现一旦指定key,就会取crc32算法改写之后的key作为id,再取id和分区总数的余数作为分区号
producer.lua:48: choose_partition(): id:2212294583 key:1 crc32(key):2212294583 cId:1 pId:1,
producer.lua:48: choose_partition(): id:4108050209 key:0 crc32(key):4108050209 cId:1 pId:1,
producer.lua:48: choose_partition(): id:2212294583 key:1 crc32(key):2212294583 cId:1 pId:1,
这个算法我是看不懂,但是A1中前面传入的key就已经是分区号的啊,还要它算什么哦,直接将上面的方法改掉:
local function default_partitioner(key, num, correlation_id)
-- local id = key and crc32(key) or correlation_id
-- ngx.log(ngx.INFO,"id:",id," key:",key," crc32(key):",crc32(key)," cId:",correlation_id," pId:", id%num)
-- partition_id is continuous and start from 0
-- return id % num
return tonumber(key)
end
重启nginx,环境内目前总算是没有出现新问题了…
其实这里出现了两个问题,一个是指定分区,但是0号分区不写的问题,一个是使用持久的连接,分区能被均匀的写入但是连接不稳,导致数据丢失的问题。
前者改写了插件的代码解决,如果不指定分区号又要改回,否则就直接报错了。后者就无法从代码部分理解了,估计是环境的问题,broker找不到、连接超时、连接关闭等,需要安装最新版本的kafka、申请更大的宽带等办法解决了。