存档

2019年2月 的存档

一致性哈希在golang里面的hash

2019年2月28日 没有评论

一致性哈希也不是什么新东西,我第一次看到应该是2年前看《大型网站技术架构:核心原理与案例分析》的时候,不过除了面试的时候其他时候机会没有遇到过和一致性哈希相关的内容,然后慢慢的就是书的知识,我又还给了书。我有时候觉得有些知识不用代码敲出来,自己好像还是不会一样,所以今天就动手把别人一致性哈希golang的实现搬过来。在把代码复制拷贝一份,认认真真的看了以后,感觉一致性哈希也就这样(当然有空还是要好好补补高数)。

讲一致性哈希的资料很多,随便用google搜索一下,高质量的答案很多。我如果再说感觉意义也不是很大,如果你有幸读到这篇文章,我在结尾也会放一些相关的资料链接。

Golang实现

package main

import (
	"fmt"
	"hash/crc32"
	"sort"
	"strconv"
)

type UInt32Slice []uint32

func (s UInt32Slice) Len() int {
	return len(s)
}

func (s UInt32Slice) Less(i, j int) bool {
	return s[i] < s[j]
}

func (s UInt32Slice) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

type Hash func(data []byte) uint32

type Map struct {
	hash     Hash
	replicas int               // 复制因子
	keys     UInt32Slice       // 已排序的节点哈希切片
	hashMap  map[uint32]string // 节点哈希和KEY的map,键是哈希值,值是节点Key
}

func New(replicas int, fn Hash) *Map {
	m := &Map{
		replicas: replicas,
		hash:     fn,
		hashMap:  make(map[uint32]string),
	}
	// 默认使用CRC32算法
	if m.hash == nil {
		m.hash = crc32.ChecksumIEEE
	}
	return m
}

func (m *Map) IsEmpty() bool {
	return len(m.keys) == 0
}

// Add 方法用来添加缓存节点,参数为节点key,比如使用IP
func (m *Map) Add(keys ...string) {
	for _, key := range keys {
		// 结合复制因子计算所有虚拟节点的hash值,并存入m.keys中,同时在m.hashMap中保存哈希值和key的映射
		for i := 0; i < m.replicas; i++ {
			hash := m.hash([]byte(strconv.Itoa(i) + key))
			m.keys = append(m.keys, hash)
			m.hashMap[hash] = key
		}
	}

	// 对所有虚拟节点的哈希值进行排序,方便之后进行二分查找
	sort.Sort(m.keys)
}

// Get 方法根据给定的对象获取最靠近它的那个节点key
func (m *Map) Get(key string) string {
	if m.IsEmpty() {
		return ""
	}

	hash := m.hash([]byte(key))

	// 通过二分查找获取最优节点,第一个节点hash值大于对象hash值的就是最优节点
	idx := sort.Search(len(m.keys), func(i int) bool { return m.keys[i] >= hash })

	// 如果查找结果大于节点哈希数组的最大索引,表示此时该对象哈希值位于最后一个节点之后,那么放入第一个节点中
	if idx == len(m.keys) {
		idx = 0
	}

	return m.hashMap[m.keys[idx]]
}

func main() {
	unique_hash := New(100, nil)
	unique_hash.Add("127.0.0.1", "192.168.1.2", "196.168.1.3")

	fmt.Println(unique_hash.Get("king"))
	fmt.Println(unique_hash.Get("niu"))
	fmt.Println(unique_hash.Get("fire"))
	fmt.Println(unique_hash.Get("water"))
	fmt.Println(unique_hash.Get("war"))
	fmt.Println(unique_hash.Get("war2"))
	fmt.Println(unique_hash.Get("war1"))
	fmt.Println(unique_hash.Get("war2"))
	fmt.Println(unique_hash.Get("women"))
	fmt.Println(unique_hash.Get("man"))
	fmt.Println(unique_hash.Get("body:1"))
}

然后你会发现当你把虚拟节点设置的越大的时候,key的分布就会越平均,当值很小的时候分布的就很不均匀了。当然这也和我们测试的key的数量有关,我就放了这几个,如果你放的越多,它可能就越平均了。

参考资料:

《使用Go实现一致性哈希》

《一致性哈希算法的理解与实践》

golang冒泡排序的实现

2019年2月28日 没有评论

冒泡排序应该是我们程序员接触的第一个算法了吧。前几天去面试,考了一下冒泡排序,长时间不用,只记得个大概,就是相邻的2个元素比较大小,然后互换,具体的记不清了。然后很紧张,就没写出来。今天趁着有时间就用代码敲了一遍,当做笔记。

冒泡排序原理

  1. 比较相邻的元素。 如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。 在这一点,最后的元素应该会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

Golang实现

package main

import "fmt"

func main() {
	sort_array := [10]int{10,7,25,99,1,8,50,85,26,12}
	array_len := len(sort_array)
	for pos:=array_len-1; pos >= 0; pos--  {
		for i :=0; i < pos;  i++ {
			if sort_array[i] > sort_array[i+1] {
				tmp := sort_array[i+1]
				sort_array[i+1] = sort_array[i]
				sort_array[i] = tmp
			}
		}
	}
	fmt.Println(sort_array)
}

 

分类: golang, 算法 标签:

搭建小型团队Wiki系统-gollum

2019年2月19日 没有评论

这两天在考虑给我们4个人的小团队使用什么好的知识共享软件?第一个进入我的视野就是大名鼎鼎的confluence,无论从哪个角度考虑,confluence都是首选,而且我也装了,不过由于云服务器配置实在是太差,做一个操作就要卡上半天(cpu跑满了)。因此无奈,confluence被我pass掉了!接下来我考虑了一下Doku,这是一个php开发的小型wiki系统,其实它很不错,各方面都不错,而且插件也比较丰富。但是它有自己的一套语法系统,使用插件的话可以支持Markdown,但是相对比较麻烦一些,主要还是内心对它并不是特别喜欢吧,也被我pass掉了。最后进入我的视野的gollum(咕噜),这gollum和指环王中的是同一个词,不知道当初起名时,是不是作者比较喜欢电影中的咕噜。gollum是ruby开发的一套wiki系统,它支持Markdown语法、轻量级、结构清晰,看起来是不错的选择。

Docker安装Gollum

首先这里假设你已经成功安装docker了,如果你还没有安装,可以自行搜索一下资料,还是很简单的,这里就不再叙说了。

Dockerfile文件

FROM ruby
RUN apt-get -y update && apt-get -y install libicu-dev cmake && rm -rf /var/lib/apt/lists/*
RUN gem install github-linguist
RUN gem install gollum-rugged_adapter
RUN gem install gollum
RUN gem install org-ruby  # optional
WORKDIR /wiki
ENTRYPOINT ["gollum", "--port", "80", "--adapter", "rugged"]
EXPOSE 80

Docker-compose文件

version: "2"

services:
  gamemodr_wiki:
    build: ./gollum
    volumes:
      - /data/wiki/gamemodr:/wiki
    expose:
      - 80
    ports:
      - 8888:80

运行”docker-compose up -d “就可以让容器服务运行了,注意挂载的”/data/wiki/gamemod”目录需要是一个git初始化过的目录。

如果需要在线编辑,可以用nginx做一个反向代理,然后加一个http用户认证,当然权限部分就没办法了。如果不需要在线编辑,可以去掉gollum在线编辑功能,然后和类似jenkins集成工具整合,也是不错的选择。

分类: Docker, 随笔 标签: ,