基于redis的分布式限频库

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 总述 这个库的目标是为了能够简单粗暴的实现分布式限频功能, 类似于ID生成器的用法, client每次从Redis拿回-批数据(在这里只是一个数值)进行消费, 只要没有消费完,就没有达到频率限制。 English README 优势 依赖少,只依赖redis,不需要专门的服务 使用的redis自己的时钟,不需要相应的服务器时钟完全一致 线程(协程)安全 系统开销小,对redis的压力很小 安装 go get github.com/vearne/ratelimit 用法 1. 创建 redis.Client 依赖 “github.com/go-redis/redis” client := redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "xxx", // password set DB: 0, // use default DB }) 2. 创建限频器 limiter, _ := ratelimit.NewRedisRateLimiter(client, "push", 1 * time.Second, 200, 10, ratelimit.TokenBucketAlg, ) 表示允许每秒操作200次 limiter, _ := ratelimit.NewRedisRateLimiter(client, "push", 1 * time.Minute, 200, 10, ratelimit.TokenBucketAlg, ) 表示允许每分钟操作200次 ...

August 24, 2018 · 2 min

制作了jsonlint的镜像站

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 引言 jsonlint.com是我最喜欢的json 验证和编辑网站 但国内的访问这个网站非常不稳定,根据它提供的源码,部署了一个它的镜像站,为了表示对原作者的尊重,保留了原有的广告和推广链接 访问地址 http://jsonlint.vearne.cc 访问地址 Thanks to Douglas Crockford of JSON and JS Lint, and Zach Carter, who built a pure JavaScript implementation. You can download the JSONLint source code on GitHub. 参考资料 zaach/jsonlint circlecell/jsonlint.com 请我喝瓶饮料

August 21, 2018 · 1 min

基于UIC构建单点登录

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 前言 在阅读open-falcon源码时,意外发现它有个UIC(用户中心), 这个项目的代码实现比较简单,唯一问题是界面有点丑 传送门: UIC github地址 公司正好有个前后端分离的项目需要做登录,因此我打算基于UIC来做单点登录 2. 实现 关于单点登录的基本概念请阅读参考资料1自行脑补 2.1 login过程 注①② 也可采用cookie的方案,本方案是将sig存储在浏览器的localstorage中,每次请求后端API时,附带在Header头中 注③ 一般可以采用302跳转的方案,但是UIC项目本身是通过JS实现的Redirect到callback URL 2.2 logout过程 logout过程需要对UIC进行改造 调用UIC的logout函数,需要执行以下动作 remove UIC session 告知其它已经登录的系统的执行logout(需要每个参与系统需要提供接口, 清除session) 参考资料 单点登录原理与简单实现 PS: 仔细想想微信公众号的Oauth也是单点登录的过程 请我喝瓶饮料

August 8, 2018 · 1 min

Lucene索引结构漫谈

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 前言 注意: 本文基于Lucene 3.0.2, 目前Lucene的版本最新已经是7.3.x 虽然Lucene的版本变化较大, 但是索引结构已经构建它的核心思想并没有发生。 Lucene是solr和Elasticsearch的基础,汽车中的引擎,它的每次改版都会引起上层系统的巨大变化。研究它对于提升查询性能,降低存储开销有非常大的帮助。 笔者有三年多的ES使用经验,但是真正踏踏实实探究Lucene和ES也是最近时间的事情。 重要 首先,推荐的是一本书《Lucene实战》这本书的作者有好几个都是Lucene的核心开发人员,因此对Lucene的理解是非常透彻的, 非常推荐。 推荐索引文件的查看工具Luke, 它可以打开Lucene和ES的索引文件,直观的观察它们的内部数据 1. 索引文件列表 Lucene有2种文件格式 1.1 CompoundFile == false -rw-r--r-- 1 zhuwei wheel 1471 6 5 15:44 _2.fdt -rw-r--r-- 1 zhuwei wheel 12 6 5 15:44 _2.fdx -rw-r--r-- 1 zhuwei wheel 66 6 5 15:44 _2.fnm -rw-r--r-- 1 zhuwei wheel 323 6 5 15:44 _2.frq -rw-r--r-- 1 zhuwei wheel 8 6 5 15:44 _2.nrm -rw-r--r-- 1 zhuwei wheel 442 6 5 15:44 _2.prx -rw-r--r-- 1 zhuwei wheel 61 6 5 15:44 _2.tii -rw-r--r-- 1 zhuwei wheel 2611 6 5 15:44 _2.tis -rw-r--r-- 1 zhuwei wheel 9 6 5 15:44 _2.tvd -rw-r--r-- 1 zhuwei wheel 1647 6 5 15:44 _2.tvf -rw-r--r-- 1 zhuwei wheel 20 6 5 15:44 _2.tvx -rw-r--r-- 1 zhuwei wheel 20 6 5 15:44 segments.gen -rw-r--r-- 1 zhuwei wheel 233 6 5 15:44 segments_4 1.2 CompoundFile == true total 33976 -rw-r--r-- 1 zhuwei wheel 2459065 7 31 13:17 _0.cfs -rw-r--r-- 1 zhuwei wheel 13468962 7 31 13:17 _0.cfx -rw-r--r-- 1 zhuwei wheel 1451842 7 31 13:17 _1.cfs -rw-r--r-- 1 zhuwei wheel 20 7 31 13:17 segments.gen -rw-r--r-- 1 zhuwei wheel 442 7 31 13:17 segments_2 组合文件只是将原来放在多个文件中的数据整合到少数的几个文件中,减少了打开的文件描述符的数量,其它并没有大的区别,所以我们重点来看非组合文件。 ...

July 31, 2018 · 3 min

Golang strings中的Index函数(字符串查找)

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 前言 在Golang中strings模块Index原型如下 func Index(s, substr string) int Golang中 substr长度大于64/32(视CPU的情况而定)的情况, 查找采用的rabin-karp算法,它是由Richard M. Karp和 Michael O. Rabin在1987年提出的。(1987年就这么牛了,还让人活不活) 该算法的实际应用是检测抄袭 2. 点评 假定s长度为n, substr长度为m 它的时间复杂度为O(n + m),最坏情况下的时间复杂度为O(n * m)。 KMP算法的时间复杂度也是O(n + m), 可见它的速度并不慢 它的相比于KMP算法的最大优势是,实现简单,易于理解 2.1 以下是暴力算法的伪码 function NaiveSearch(string s[1..n], string pattern[1..m]) for i from 1 to n-m+1 for j from 1 to m if s[i+j-1] ≠ pattern[j] jump to next iteration of outer loop return i return not found 2.2 以下是rabin-karp算法的伪码 function RabinKarp(string s[1..n], string pattern[1..m]) hpattern := hash(pattern[1..m]); for i from 1 to n-m+1 hs := hash(s[i..i+m-1]) if hs = hpattern if s[i..i+m-1] = pattern[1..m] return i return not found 大家可以看出rabin-karp算法其实是暴力算法的改进版, 简单的说: 就是先匹配Pattern的哈希值,如果hash值相同,才实际进行字符串匹配(line 4/5/6) ...

July 30, 2018 · 2 min

chrome定时提醒插件

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1.引言 周末在家,花了点时间,写了一个chrome插件,提醒在繁忙的工作中,注意休息保护眼睛 插件下载地址 1.0.3 github地址 2.使用方法 2.1 打开chrome扩展程序面板 更多工具-> 扩展程序 或者 直接在地址栏输入 chrome://extensions/ 2.2 打开开发者模式 注意 非常重要,否则可能安装不上 2.3 将ireminder.crx拖拽到chrome扩展面板 2.4 设置提醒 这个插件会根据设置的间隔推送提醒 2.5 提醒效果 有兴趣的朋友可以试一下。 请我喝瓶饮料

July 16, 2018 · 1 min

聊聊Elasticsearch的集群状态的管理和维护

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 注意 本文参考的ES 5.0.1的源码 1. 查看集群状态 可以通过API查看集群状态 GET /_cluster/state 大致可以得到如下内容 version 集群状态数字版本号 每次更新version + 1 集群重启version不会置0(只会单调增) state_uuid 是集群状态字符串版本号(UUID) master_node master节点 nodes 该版本中的所有节点信息 routing_table 和 routing_nodes 都是描述不同index上的shard在node上的分布关系 2. 集群状态的维护 ES源码中集群状态的对应类为ClusterState.java The cluster state can be updated only on the master node. All updates are performed by on a single thread and controlled by the {@link ClusterService}. After every update the {@link Discovery#publish} method publishes new version of the cluster state to all other nodes in the cluster. In the Zen Discovery it is handled in the {@link PublishClusterStateAction#publish} method ...

July 12, 2018 · 2 min

压缩率远超JPG和PNG的图片压缩格式-WebP

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 引言 WebP格式,谷歌(google)开发的一种旨在加快图片加载速度的图片格式。图片压缩体积大约只有JPEG的2/3,并能节省大量的服务器宽带资源和数据空间。Facebook Ebay等知名网站已经开始测试并使用WebP格式。 但WebP是一种有损压缩。相较编码JPEG文件,编码同样质量的WebP文件需要占用更多的计算资源。 目前谷歌、阿里巴巴、搜狐都有在采用此方案,另外七牛云支持同步转换,可以直接使用。 注意:某些Android版本不兼容webp格式。 安装webp 编码/解码工具 mac 上安装webp非常简单 brew install webp 让我们看看实际转换的效果, cwebp支持对PNG/JPEG/TIFF 格式的转换 # 编码 cwebp -q 80 8260434328.jpg -o 8260434328_80.webp # 解码 dwebp 8260434328_80.webp -o 8260434328.jpg 原图 大小: 130KB 质量因子:80 大小: 73KB 质量因子:80 大小: 35KB 大家可以看到图片大小减少了75%左右,但感官上,图片的画质几乎没有影响。 注意 如果图片无法正常浏览,请使用chrome浏览器 关于移动端使用WebP,请阅读参考资料1 参考资料 移动端如何使用 WebP 请我喝瓶饮料

July 5, 2018 · 1 min

聊聊Gossip的一个实现

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 引言 Gossip是一种去中心化、容错并保证最终一致性的协议。它的基本思想是通过不断的和集群中的节点gossip交换信息,经过 O(log(n))个回合, gossip协议即可将信息传递到所有的节点。 这里介绍Gossip的一个实现库hashicorp/memberlist,并讲一下需要注意的事项。 2. hashicorp/memberlist memberlist是HashiCorp公司开源的Gossip库,这个库被consul(也是HashiCorp公司开源的)所引用。 它是SWIM的一个扩展实现。 下面的例子test_gossip.go中它被用来做集群节点发现 package main import ( "flag" "fmt" "github.com/hashicorp/memberlist" // "net" "os" "strconv" "time" ) var ( bindPort = flag.Int("port", 8001, "gossip port") ) func main() { flag.Parse() hostname, _ := os.Hostname() config := memberlist.DefaultLocalConfig() config.Name = hostname + "-" + strconv.Itoa(*bindPort) // config := memberlist.DefaultLocalConfig() config.BindPort = *bindPort config.AdvertisePort = *bindPort fmt.Println("config.DisableTcpPings", config.DisableTcpPings) fmt.Println("config.IndirectChecks", config.IndirectChecks) fmt.Println("config.RetransmitMult", config.RetransmitMult) fmt.Println("config.PushPullInterval", config.PushPullInterval) fmt.Println("config.ProbeInterval", config.ProbeInterval) fmt.Println("config.GossipInterval", config.GossipInterval) fmt.Println("config.GossipNodes", config.GossipNodes) fmt.Println("config.BindPort", config.BindPort) list, err := memberlist.Create(config) if err != nil { panic("Failed to create memberlist: " + err.Error()) } // Join an existing cluster by specifying at least one known member. // 配置种子节点, 这里我直接写死了 _, err = list.Join([]string{"127.0.0.1:8001", "127.0.0.1:8002"}) fmt.Println("err", err) if err != nil { panic("Failed to join cluster: " + err.Error()) } // Ask for members of the cluster for { fmt.Println("-------------start--------------") for _, member := range list.Members() { fmt.Printf("Member: %s %s\n", member.Name, member.Addr) } fmt.Println("-------------end--------------") time.Sleep(time.Second * 3) } } 可以直接在单机上进行测试,启动 ...

July 4, 2018 · 3 min

算法题 leecode 525.连续数组

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 题目 给定一个二进制数组, 找到含有相同数量的 0 和 1 的最长连续子数组。 实例1: 输入: [0,1] 输出: 2 说明: [0, 1] 是具有相同数量0和1的最长连续子数组。 实例2 输入: [0,1,0] 输出: 2 说明: [0, 1] (或 [1, 0]) 是具有相同数量0和1的最长连续子数组。 解题思路 注意 数组中只会有0、1 两种数据 相同数量的 0 和 1 的最长连续子数组中肯定包含有相同数量的0和1 这里我们要利用0和1数量相同,互相抵消的特性 如果我们把0都当做-1, 1还是1,沿着数组求和,对于以下数组 [0, 1, 1, 0, 1, 1, 1, 0] 我们可以想到如果2个位置(i, j)之间有相同数量0和1的最长连续数组, 那么这2个位置的sum(i) == sum(j) 这里要注意的是,对于数组初始的sum值是0,我们可以记为sum(-1) = 0 要找到最长连续子数组,只需找到sum值相同,且位置相距最大的情况即可 最终代码 find_max.go func findMaxLength(nums []int) int { // sum -> index // 存最小值 cache := map[int]int{} cache[0] = -1 sum := 0 res := 0 for i := 0; i < len(nums); i++ { if nums[i] == 0 { sum -= 1 } else { sum += 1 } if _, ok := cache[sum]; ok { index := cache[sum] res = max(i-index, res) } else { cache[sum] = i } } return res } func max(a int, b int) int { if a < b { return b } else { return a } } 请我喝瓶饮料

June 13, 2018 · 1 min