当前位置:首页 > 服务器技术 > nginx

Nginx一致性哈希模块的Lua实现

Nginx一致性哈希模块的Lua重新实现

 

技术背景:

最近在工作中使用了nginx+redis 的架构,redis在后台做分布式存储,每个redis都存放不同的数据,这些数据都是某门户网站通过Hadoop分析出来的用户行为日志,key是uid,value是user profile,每小时更新量在500-800万条记录,而这些记录一旦生成,我需要在5分钟左右的时间完成所有导入过程。

 

首先,我在nginx中使用了第三方模块HttpUpstreamConsistent来做负载均衡策略,针对不同用户(uid)选取不同的backend redis:

               upstream somestream {
      consistent_hash $arg_uid;
      server 
            10.50.1.3:11211;
      server 10.50.1.4:11211;
      server 10.50.1.5:11211;
    }

现在问题来了,由于Hadoop系统处理日志的速度非常快,如果把每条记录都通过Nginx来写入Redis中,这样的速度是无法接受的,而且会影响Nginx对正常请求的服务能力。所以,需要将这些数据以离线的方式导入redis集群中,这样就要重新实现HttpUpstreamConsistent模块了,才能保证读写的哈希策略一致。

 

下面的源码演示了如何将HttpUpstreamConsistent模块翻译成Lua的过程,(使用了CRC32作散列,依赖库的路径已列在Reference中)。

#!/usr/bin/lua

-- chenqi@2014/04/02
--[Reference]
--https://github.com/yaoweibin/ngx_http_consistent_hash
--https://github.com/davidm/lua-digest-crc32lualocal CRC = require(CRC32)

local M = {}

local CONSISTENT_BUCKETS = 1024local VIRTUAL_NODE = 160local HASH_PEERS = {}
local CONTINUUM = {}
local BUCKETS = {}

localfunction hash_fn(key)
    return CRC.crc32(key)
end-- in-place quicksortfunction quicksort(array,compareFunc)  
    quick(array,1,#array,compareFunc)  
endfunction quick(array,left,right,compareFunc)  
    if(left < right ) thenlocal index = partion(array,left,right,compareFunc)  
        quick(array,left,index-1,compareFunc)  
        quick(array,index+1,right,compareFunc)  
    endendfunction partion(array,left,right,compareFunc)  
    local key = array[left] 
    local index = left  
    array[index],array[right] = array[right],array[index]
    local i = left  
    while i< right doif compareFunc( key,array[i]) then  
            array[index],array[i] = array[i],array[index]
            index = index + 1end  
        i = i + 1end  
    array[right],array[index] = array[index],array[right]
    return index;  
end-- binary searchlocalfunction chash_find(point)
    local mid, lo, hi = 1, 1, #CONTINUUM
    while1doif point <= CONTINUUM[lo][2] or point > CONTINUUM[hi][2] thenreturn CONTINUUM[lo]
        end-- test middle point
        mid = lo + math.floor((hi-lo)/2)

        -- perfect matchif point <= CONTINUUM[mid][2] and point > (mid > 1and CONTINUUM[mid-1][2] or0) thenreturn CONTINUUM[mid]
        end-- too low, go upif CONTINUUM[mid][2] < point then
            lo = mid + 1else
            hi = mid - 1endendendlocalfunction chash_init()
    local n = #HASH_PEERS
    if n == 0thenprint("There is no backend servers")
        returnendlocal C = {}
    for i,peer inipairs(HASH_PEERS) dofor k=1, math.floor(VIRTUAL_NODE * peer[1]) dolocal hash_data = peer[2] .. "-" .. (k - 1)
            table.insert(C, {peer[2], hash_fn(hash_data)})
        endend

    quicksort(C, function(a,b) return a[2] > b[2] end)
    CONTINUUM = C

--[[
    for i=1,#C do
        print(CONTINUUM[i][1],CONTINUUM[i][2])
    end
--]]local step = math.floor(0xFFFFFFFF / CONSISTENT_BUCKETS)

    BUCKETS = {}
    for i=1, CONSISTENT_BUCKETS dotable.insert(BUCKETS, i, chash_find(math.floor(step * (i - 1))))
        -- print(BUCKETS[i][1],BUCKETS[i][2])
    endend
M.init = chash_init

localfunction chash_get_upstream_crc32(point)
    return BUCKETS[(point % CONSISTENT_BUCKETS)+1][1]
end
M.get_upstream_crc32 = chash_get_upstream_crc32

localfunction chash_get_upstream(key)
    local point = math.floor(hash_fn(key)) 
    return chash_get_upstream_crc32(point)
end
M.get_upstream = chash_get_upstream

localfunction chash_add_upstream(upstream, weigth)
    weight = weight or1table.insert(HASH_PEERS, {weight, upstream})
end
M.add_upstream = chash_add_upstream

return M

 

API调用方式:

            local redis_login= {
    "10.50.1.3:11211",
    "10.50.1.4:11211",
    "10.50.1.5:11211",
}

for k, backend inipairs(redis_login) do
    chash_login.add_upstream(backend)
end
chash_login.init()

uid="309473941"
chash_login.chash_get_upstream(uid)

返回一个backend地址,将该uid对应的数据写入对应的redis中即可,稍后可以使用Nginx读到。

 

PS:关于redis的mass insertion问题,最高效的方式是批量写入文件(文件格式遵循redis协议),然后使用 redis-cli --pipe 直接导入。

原文:http://www.cnblogs.com/chenny7/p/3640990.html


【说明】本文章由站长整理发布,文章内容不代表本站观点,如文中有侵权行为,请与本站客服联系(QQ:254677821)!