现网服务,每次更新一个服务时,另外一个集群所有node 都跟着同时重启一遍,这么调皮,这是闹哪样啊。。

  看系统日志:/var/log/messages

  Oct 30 15:19:41 localhost kernel: beam.smp[21880]: segfault at 7fa300006d4b ip 00007fa300006d4b sp 00007fa3d0d7c788 error 14 in locale-archive[7fa31616f000+5e91000

  beam crash了,好吧开始回忆咱用了哪些c库, 都不应该有问题啊

  嗯,打开core dump,再复现一遍。嗯,在线上复现嗷,设计成完全不影响业务的重启还是很有用的。

  不一会dump粗来了,挂上gdb 很快找到出错堆栈:

#0 0x00007fa300006d4b in ?? ()
#1 0x00007fa3aa83dd96 in quicksort () from /data0/xxx_0.4.4/lib/hash_ring-0.1.6/priv/hash_ring_drv.so
#2 0x00007fa3aa83d026 in hash_ring_remove_node () from /data0/xxx_0.4.4/lib/hash_ring-0.1.6/priv/hash_ring_drv.so
#3 0x00007fa3aa83c295 in hash_ring_drv_output () from /data0/xxx_0.4.4/lib/hash_ring-0.1.6/priv/hash_ring_drv.so
#4 0x00000000004924ef in call_driver_output (c_p=0x7fa3b0ec8f50, flags=2064, prt=0x7fa3d8d40bc0, from=55559696966931,
list=140341277983642, refp=0x0) at beam/io.c:1768

嗯,定位到出问题位置在于c库依赖hash_ring

顺便说下服务间调用一致性hash的实现:

1. 通过gen_server 定时rpc:call 目标nodes 指定服务运行状态,并动态管理hash_ring 中动态节点。

init([Configs]) ->
%hash_ring 需要先启动, link 需要同时重启
link(whereis(hash_ring)),
ets:new(?MODULE, [named_table, protected, set, {read_concurrency, true}]),
ets:new(?ROUND_ROBIN_ETS, [named_table, public, set, {write_concurrency, true}]),
ets:insert(?ROUND_ROBIN_ETS, {inc, 0}),
{ok, Routes} = parse_configs(Configs),
State = apply_routes(Routes, #state{}),
start_check_timer(),
{ok, State}.

  

monitor_route({Svc, Node} = Route) ->
% monitor 无法立即返回是否成功,rpc:call 成功后再monitor
case catch rpc:call(Node, erlang, whereis, [Svc], 3000) of
{'EXIT', Reason} ->
{error, Reason};
undefined ->
{error, svc_undefined};
Pid when is_pid(Pid) ->
Ref = erlang:monitor(process, Route),
{ok, Ref};
Error ->
{error, Error}
end. route_up(Name, Route, Ref, #state{mons=Mons, downs=Downs} = State) ->
case lists:member(Route, get_all_routes(Name)) of
true ->
case lists:member(Route, get_routes(Name)) of
false ->
ok = hash_ring:add_node({route, Name}, term_to_binary(Route)),
add_route(Name, Route);
true ->
ok
end,
State#state{mons=dict:store(Ref, {Name, Route}, Mons),
downs=Downs -- [{Name, Route}]};
_ ->
catch erlang:demonitor(Ref),
lager:info("igonre route_up:~p ~p ~p", [Name, Route, Ref]),
State
end.

  2. 使用自己的gen_call 替代 rpc:call 调用,节省monitor 资源消耗

route(Name, Key) ->
case hash_ring:find_node({route, Name}, neo_util:to_binary(erlang:phash2(Key))) of
{ok, Route} -> {ok, binary_to_term(Route)};
Error -> Error
end. call(Name, Key, Req, Timeout) ->
{ok, Dst} = route(Name, Key),
gen_call(Dst, Req, Timeout). %参考 whatsapp 做法
%使用场景:
%1. Process 需要被长期monitor
%2. Process 为node内常驻服务进程,不是临时进程
%3. 调用前需要先确认Process alive 状态
%优势:
%不需要monitor, 节省两次网络交互
%gen:call 需要monitor -> call -> demonitor dst node monitor 操作消耗资源
%影响:
%process down 瞬间, call 应答都会超时,但此调用返回的是timeout
%
gen_call(Process, Request) ->
gen_call(Process, Request, 5000). gen_call(Process, Request, Timeout) ->
Ref = erlang:make_ref(),
catch erlang:send(Process, {'$gen_call', {self(), Ref}, Request}),
receive
{Ref, Reply} ->
Reply
after Timeout ->
exit({timeout, {?MODULE, call, [Process, Request, Timeout]}})
end.

  问题原因是:https://github.com/chrismoos/hash-ring/blob/master/sort.c#52

  快排算法中STACK_SIZE=1024 移除节点时,实现会对剩余节点做一次排序。节点过多时,数组就越界了。

  解决方案:

  sort.h 快排算法是有问题的,但先不急着优化排序算法。

  其实一致性hash的添加加点和删除节点也能能够做到O(1) 的,为此我提交了一个patch修复该问题。

  方案:

  1. 移除节点,每个虚拟节点保存物理节点的引用,删除时只需要将空位campact

2. 添加节点,只需要计算新加节点并做快排,而不要所有节点再次排序,做一次二路归并即可

    quicksort((void**)adds, ring->numReplicas, item_sort);
+ //ring->numNodes * ring->numReplicas
+ if(ring->items == NULL || ring-> numNodes == ) {
+ ring->items = adds;
+ } else {
+ size_t size_new = sizeof(hash_ring_item_t*) * ring->numNodes * ring->numReplicas;
+ hash_ring_item_t **news = (hash_ring_item_t **)malloc(size_new);
+ hash_ring_item_t **olds = ring->items;
+ if(news == NULL) {
+ return HASH_RING_ERR;
+ }
+ int oldlen = (ring->numNodes - ) * ring->numReplicas;
+ int addlen = ring->numReplicas;
+ int i=, j=, k=;
+ while() {
+ if(i == oldlen && j == addlen) {
+ break;
+ }
+ if(j == addlen) {
+ news[k++] = olds[i++];
+ continue;
+ }
+ if(i == oldlen) {
+ news[k++] = adds[j++];
+ continue;
+ }
+ int ret = item_sort(olds[i], adds[j]);
+ if(ret == ) {
+ news[k++] = olds[i++];
+ news[k++] = adds[j++];
+ } else if (ret < ) {
+ news[k++] = olds[i++];
+ } else {
+ news[k++] = adds[j++];
+ }
+ };
+ free(adds);
+ free(olds);
+ ring->items = news;

  另:

  这个hash 算法其实还是不太可靠的,虚拟节点数字相同怎么办?

  一般来说所有节点添加,删除次序相同即可保证一致性,当然最好能够通过nodename 对相同虚节点做排序。

erlang 虚机crash的更多相关文章

  1. erlang 虚机CPU 占用高排查

    -问题起因 近期线上一组服务中,个别节点服务器CPU使用率很低,只有其他1/4.排除业务不均,曾怀疑是系统top统计错误,从Erlang调度器的利用率调查 找到通过erlang:statistics( ...

  2. 【转载】关闭XenServer中挂起(hang)虚机的方法

    在XenServer中,碰到VM挂起(hang)的情况,也不是那么少见,而VM长时间挂起,那么很影响心情和后续的操作. 一般情况下,为了关闭VM或者重启VM,我们推荐这样的操作顺序: 进入到VM内,使 ...

  3. 远程管理 KVM 虚机 - 每天5分钟玩转 OpenStack(5)

    上一节我们通过 virt-manager 在本地主机上创建并管理 KVM 虚机.其实 virt-manager 也可以管理其他宿主机上的虚机.只需要简单的将宿主机添加进来 填入宿主机的相关信息,确定即 ...

  4. 启动第一个 KVM 虚机 - 每天5分钟玩转 OpenStack(4)

    本节演示如何使用 virt-manager 启动 KVM 虚机. 首先通过命令 virt-manager 启动图形界面 # virt-manager 点上面的图标创建虚机 给虚机命名为 kvm1,这里 ...

  5. Virtualbox虚机无法启动因断电

      The virtual machine 'nn1' has terminated unexpectedly during startup with exit code 1 (0x1). More ...

  6. Neutron 理解(5):Neutron 是如何向 Nova 虚机分配固定IP地址的 (How Neutron Allocates Fixed IPs to Nova Instance)

    学习 Neutron 系列文章: (1)Neutron 所实现的虚拟化网络 (2)Neutron OpenvSwitch + VLAN 虚拟网络 (3)Neutron OpenvSwitch + GR ...

  7. Neutron 理解 (9): OpenStack 是如何实现 Neutron 网络 和 Nova虚机 防火墙的 [How Nova Implements Security Group and How Neutron Implements Virtual Firewall]

    学习 Neutron 系列文章: (1)Neutron 所实现的虚拟化网络 (2)Neutron OpenvSwitch + VLAN 虚拟网络 (3)Neutron OpenvSwitch + GR ...

  8. 云与备份之(1):VMware虚机备份和恢复

    本系列文章会介绍云与备份之间的关系,包括: (1)VMware 虚机备份和恢复 (2)KVM 虚机备份和恢复 (3)云与备份 (4)OpenStack 与备份 (5)公有云与备份 1. 与备份有关的V ...

  9. OpenStack 企业私有云的若干需求(1):Nova 虚机支持 GPU

    本系列会介绍OpenStack 企业私有云的几个需求: 自动扩展(Auto-scaling)支持 多租户和租户隔离 (multi-tenancy and tenancy isolation) 混合云( ...

随机推荐

  1. 使用EXtjs6.2构建web项目

    一.项目简介 众所周知ext是一款非常强大的表格控件,尤其是里边的grid为用户提供了非常多的功能,现在主流的还是用extjs4.0-4.2,但是更高一点的版本更加符合人的审美要求.因此,在今天咱们构 ...

  2. [转] JAVA读取excel数据(插入oracle数据库)

    原文地址:http://blog.csdn.net/zczzsq/article/details/16803349 本实例做的是读取execl(只能读取.xls的execl,即只能读取03版的),如果 ...

  3. iOS开发--OC调用JS篇

    OC调用JS篇 其中相对应的html部分如下: <html> <header> <meta http-equiv="Content-Type" con ...

  4. C#-WebForm-★内置对象简介★Request-获取请求对象、Response相应请求对象、Session全局变量(私有)、Cookie全局变量(私有)、Application全局公共变量、ViewState

    内置对象: 1.Request - 获取请求对象 用法:接收传值 protected void Page_Load(object sender, EventArgs e) { TextBox1.Tex ...

  5. vue-Resource(与后端数据交互)

    单来说,vue-resource就像jQuery里的$.ajax,用来和后端交互数据的.可以放在created或者ready里面运行来获取或者更新数据... vue-resource文档:https: ...

  6. Socket编程实践(2) Socket API 与 简单例程

    在本篇文章中,先介绍一下Socket编程的一些API,然后利用这些API实现一个客户端-服务器模型的一个简单通信例程.该例子中,服务器接收到客户端的信息后,将信息重新发送给客户端. socket()函 ...

  7. Python 【第六章】:Python操作 RabbitMQ、Redis、Memcache、SQLAlchemy

    Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度 ...

  8. 安装Windows10,Ubuntu双系统14.04LTS记录

    参考链接:http://www.jianshu.com/p/2eebd6ad284d(推荐直接看这个链接,我也是看这篇博客装的)然后自己记录一下,防止以后找不到了 本记录是在Windows10 上安装 ...

  9. Mysql多表表关联查询 inner Join left join right join

    Mysql多表表关联查询 inner Join left join right join

  10. java 方法

    方法命名规范要求 类的命名规范:“全部单词的 首字母必须大写”.那么在定义方法的时候也是有命名规范要求的:“第 一个单词的首字母小写,之后每个单词的首字母大写”,那么这就是方法 的命名规范. 递归调用 ...