前段时间想使用bee的热加载功能,就想着安装一下。安装的时候遇到了一点点问题,主要原因还是对golang不是很熟悉,非我的主要编程语言,偶尔自己想起来就用用,感觉半吊子都算不上。
先看一下问题。
go install github.com/beego/bee/v2@latest
得到下面的问题提示,有点没看明白问题出在哪里。
go: github.com/beego/bee/v2@latest: go.mod has non-.../v2 module path "github.com/beego/bee" (and .../v2/go.mod does not exist) at revision v2.0.1
然后做了些搜索,解决了问题,应该是没有启用GO111MODULE特性,我在goland ide里面启用了,但是go env里面缺没有设置。
启用GO111MODULE和设置代理
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct
然后在执行上面的安装命令,成功安装
日志无论在开发还是生产当中,都是很重要的部分,帮助我们快速定位和发现问题。logrus是一个可以格式化存储日志的golang库,而且它还兼容标准库中的logger。logrus目前在github的start数量已经快达到10k,可谓是一个非常受欢迎的golang第三方库了。
logrus特性
- 完全兼容golang标准库日志模块:logrus拥有六种日志级别:debug、info、warn、error、fatal和panic,这是golang标准库日志模块的API的超集。如果您的项目使用标准库日志模块,完全可以以最低的代价迁移到logrus上。
- 可扩展的Hook机制:允许使用者通过hook的方式将日志分发到任意地方,如本地文件系统、标准输出、logstash、elasticsearch或者mq等,或者通过hook定义日志内容和格式等。
- 可选的日志输出格式:logrus内置了两种日志格式,JSONFormatter和TextFormatter,如果这两个格式不满足需求,可以自己动手实现接口Formatter,来定义自己的日志格式。
- Field机制:logrus鼓励通过Field机制进行精细化的、结构化的日志记录,而不是通过冗长的消息来记录日志。
- logrus是一个可插拔的、结构化的日志框架。
最简例子
下面是一个最简单的logrus例子:
package main
import (
log "github.com/sirupsen/logrus"
)
func main() {
log.Info("hello log")
}
分隔日志
分隔日志使用了logrus提供的hook机制,然后利用rotatelogs实现这些功能。
package main
import (
"path"
"time"
"github.com/lestrrat-go/file-rotatelogs"
"github.com/pkg/errors"
"github.com/rifflock/lfshook"
log "github.com/sirupsen/logrus"
)
func ConfigLocalFilesystemLogger(logPath string, logFileName string, maxAge time.Duration) {
baseLogPaht := path.Join(logPath, logFileName)
writer, err := rotatelogs.New(
baseLogPaht+".%Y%m%d.log",
rotatelogs.WithLinkName(baseLogPaht), // 生成软链,指向最新日志文件
rotatelogs.WithMaxAge(maxAge), // 文件最大保存时间
rotatelogs.WithRotationTime(time.Hour*24), // 日志切割时间间隔
)
if err != nil {
log.Errorf("config local file system logger error. %+v", errors.WithStack(err))
}
lfHook := lfshook.NewHook(lfshook.WriterMap{
log.DebugLevel: writer, // 为不同级别设置不同的输出目的
log.InfoLevel: writer,
log.WarnLevel: writer,
log.ErrorLevel: writer,
log.FatalLevel: writer,
log.PanicLevel: writer,
}, &log.JSONFormatter{
TimestampFormat:"2006-01-02 15:04:05",
})
log.AddHook(lfHook)
}
func main() {
//log.SetOutput(os.Stdout)
ConfigLocalFilesystemLogger("./log/", "simple", time.Hour*24*60)
log.Info("hello log")
log.WithFields(log.Fields{
"animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
}
参考资料:
《golang日志框架logrus》
一致性哈希也不是什么新东西,我第一次看到应该是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实现一致性哈希》
《一致性哈希算法的理解与实践》
冒泡排序应该是我们程序员接触的第一个算法了吧。前几天去面试,考了一下冒泡排序,长时间不用,只记得个大概,就是相邻的2个元素比较大小,然后互换,具体的记不清了。然后很紧张,就没写出来。今天趁着有时间就用代码敲了一遍,当做笔记。
冒泡排序原理
- 比较相邻的元素。 如果第一个比第二个大,就交换他们两个。
- 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。 在这一点,最后的元素应该会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
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)
}
近期评论