Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mongo副本集超过3节点连接primary失败 #1145

Closed
sabearcher opened this issue Jan 8, 2020 · 11 comments
Closed

mongo副本集超过3节点连接primary失败 #1145

sabearcher opened this issue Jan 8, 2020 · 11 comments

Comments

@sabearcher
Copy link

经线上项目,发现mongo副本集配置有4个可以用节点(1主3从),通过mongo配置 rs属性,在连接报错

[:0000000f] WARNING: NO PRIMARY RETURN 192.168.0.209:3717
[:0000000f] INFO: TRY TO CONNECT 192.168.0.210:3717

然后发现主节点已切换到第3个,但是skynet.mongo重连只发生在第一第二节点

local function mongo_auth(mongoc)
...
					local host, port = __parse_addr(mongoc.__pickserver[1])
					table.remove(mongoc.__pickserver, 1)
					mongoc.host	= host
					mongoc.port	= port
					mongoc.__sock:changehost(host, port)

该方法在尝试时,只切换到第二节点,连接成功则 socket_channel 返回,在mongo_auth内校验rs_data.primary失败后,重新取的节点信息又重回第一节点,在5节点模型里,无法找到主节点,永久失败。

@cloudwu
Copy link
Owner

cloudwu commented Jan 8, 2020

我对 mongo 这里的机制不熟悉,不知道官方的策略是怎样的?

@cloudwu
Copy link
Owner

cloudwu commented Jan 8, 2020

我阅读代码看到有 table.remove(mongoc.__pickserver, 1) ,在 pickserver 组里每尝试一个就会删除一个,并且只在没有 pickserver 时才会取一次,为什么会重复取回尝试过的 server 。

@cloudwu
Copy link
Owner

cloudwu commented Jan 8, 2020

这里可供选择的列表,是由第一台机器 ismater 指令返回值中的 hosts 列表决定的,你看看你的所有机器的列表有没有配置正确。

@sabearcher
Copy link
Author

well,我刚才去检查了一下报错,发现是socketchannel在连接第二个节点时报错,line:459

/root/skynet/lualib/skynet/socketchannel.lua:459: in method 'read'
        /root/skynet/lualib/skynet/db/mongo.lua:69: in function </root/skynet/lualib/skynet/db/mongo.lua:68>

没有等到response,应该是这个节点正好有故障。
然后skynet抛出了,没有去尝试后面的备选节点。

外部我们的项目尝试重连,重连有一次重复刚才的流程,因此出现了只连接1、2号两个节点,而没有发现primary的3号节点

@cloudwu
Copy link
Owner

cloudwu commented Jan 9, 2020

选择 primary host 的流程见这个 patch #525

我猜测这个实现有这样的问题:

__pickserver 列表只根据第一个成功连接的节点上取到,如果第一个节点上不存在你这里的第三节点,即使第二节点中提到,也是不可见的。

  1. 是否应该每次连接到新节点,都将新获取的 hosts 追加到 __pickserver 的末尾。
  2. mongo 的集群方案是否允许这种配置行为(允许每个节点并不配置所有其它节点),这里的问题是否只是一个配置错误?是否有标准指定?

@zxfishhack 你可以看看,我认为可以将 https://github.com/cloudwu/skynet/blob/master/lualib/skynet/db/mongo.lua#L128-L142
改为:

-- determine the primary db using hosts
local pickserver = rawget(mongoc, "__pickserver")
if pickserver == nil then
	pickserver = {}
	rawset(mongoc, "__pickserver", pickserver)
end
for _, v in ipairs(rs_data.hosts) do
	if v ~= rs_data.me then
		table.insert(pickserver, v)
	end
end

if pickserver <= 0 then
	error("CAN NOT DETERMINE THE PRIMARY DB")
end
local pick = table.remove(pickserver, 1)
skynet.error("INFO: TRY TO CONNECT " .. pick)
local host, port = __parse_addr(pick)

@sabearcher 你不妨试试。

@cloudwu
Copy link
Owner

cloudwu commented Jan 9, 2020

不过,这样修改有可能导致在配置错误(没有 primary 时) 变成死循环,因为会不断的添加已经尝试过的节点。如果想避免这种情况,可以在 __pickserver 中再加入一个 .tried table 将尝试过的节点记录下来,不要重复添加。

@xjdrew
Copy link
Contributor

xjdrew commented Jan 9, 2020

有可能存在一段时间,没有primary节点,一般不超过12秒,参考这个文档( https://docs.mongodb.com/manual/core/replica-set-elections/ )。目前代码确实没考虑选举过程中的问题,会造成尝试一轮后放弃?

mongo 的集群方案是否允许这种配置行为(允许每个节点并不配置所有其它节点),这里的问题是否只是一个配置错误?是否有标准指定?

新加入的节点,副本集之间会互相发现的;这里描述的bug有点奇怪,一共4个节点,选举新primary至少要3个节点投票的,不可能两个节点都不知道另外两个节点。是手工强行指定的primary节点么?这行代码触发了么?

error("CAN NOT DETERMINE THE PRIMARY DB")

@sabearcher
Copy link
Author

@xjdrew error("CAN NOT DETERMINE THE PRIMARY DB") 从未出现过。 也没有指定过primary。

根据今天出现的新的报错来看,今天阿里云的mongo出现了禁止连接的bug,在重连log里我看到4个节点都尝试过了(副本集是5节点的,一个Arbit,四个数据),反复重试连接不成功,经历了35分钟后,skynet进程退出了, @cloudwu 是否是太长期的socket error,会导致skynet进程退出?

而且之前的bug经过我回头检查所有旧的log,发现起服也有连接到第3节点成功的情况,真实的报错位置还是在
socketchannel.lua

local function wrapper_socket_function(f)
	return function(self, ...)
		local result = f(self[1], ...)
		if not result then
			error(socket_error) ----这里报错
		else
			return result
		end
	end
end
channel_socket.read = wrapper_socket_function(socket.read)

之后的重连尝试有时不正常,现在推测可能确实如你们所说中途一个故障节点的 rs_data.hosts本身就可能数量不对,能否改成无论 channelsocket.lua 如何表现,mongo.lua 至少一定要把配置的 conf.rs 里全部节点都遍历到?
目前我们项目用的阿里云mongo似乎在连接时容易出现故障

@cloudwu
Copy link
Owner

cloudwu commented Jan 9, 2020

我上个回复中的改法也有问题?

@cloudwu
Copy link
Owner

cloudwu commented Jan 9, 2020

我明白了。socketchannel 的 backup 列表只在连接不上时才会轮流试。对于 auth 阶段的错误则不会。我要想想怎么改。

@cloudwu
Copy link
Owner

cloudwu commented Jan 10, 2020

@sabearcher #1146

我写了个 patch 改进这里的处理方式。因为我这里很难搭建对应的环境,所以麻烦你 review 一下 patch 的代码,并做一定的测试。如发现 patch 中的问题,可以继续在那个 pr 下讨论。

@cloudwu cloudwu closed this as completed May 12, 2020
fanlix pushed a commit to fanlix/skynet that referenced this issue Jul 22, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants