钱文翔的博客


  • 首页

  • 分类

  • 归档

  • 标签

  • 搜索
close
钱文翔的博客

go-cache源码阅读笔记

发表于 2018-12-28 | 分类于 Go学习小记 |

自己经常使用 map 加上互斥锁来处理缓存, 很久之前就在 github 上发现 go-cache 这个项目,今天重新看到它,发现它已经 2000+star 了。 扫了一下代码,发现代码量很少,就 clone 下来看看。

go-cache 实际代码就两个文件–cache.go 和 shared.go,其中 shared.go 是作者打算在大缓存数据下使用 hash 算法提升缓存的读写速度的,实际上也未对外 export,创建 sharedCache 的函数名也是小写的。

cache 很简单,只有 map[string]Item 和锁。其中 Item 的结构为

1
2
3
4
type Item struct {
Object interface{}
Expiration int64
}

Object 存储数据,Expiration 存储数据过期时间。创建 cache 的时候会创建一个清理器来清理过期的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func newCacheWithJanitor(de time.Duration, ci time.Duration, m map[string]Item) *Cache {
c := newCache(de, m)
// This trick ensures that the janitor goroutine (which--granted it
// was enabled--is running DeleteExpired on c forever) does not keep
// the returned C object from being garbage collected. When it is
// garbage collected, the finalizer stops the janitor goroutine, after
// which c can be collected.
C := &Cache{c}
if ci > 0 {
runJanitor(c, ci)
runtime.SetFinalizer(C, stopJanitor)
}
return C
}

注意 runtime.SetFinalizer()这个函数,之前没用过。这是是在  程序垃圾回收(garbage collection)时执行指定函数。

shared.go 是创建一个 shardedCache, 包含多个 cache,通过 hash 算法(djb33)选择 cache 存取数据.

1
2
3
4
5
6
type shardedCache struct {
seed uint32
m uint32
cs []*cache
janitor *shardedJanitor
}

sharedCache 没有使用 go 的”hash/fnv”中的 hash.Hash 函数,使用的是自己写的 hash 算法,并称自己的 hash 算法 djb33 比 fnv 的快 5 倍 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// djb2 with better shuffling. 5x faster than FNV with the hash.Hash overhead.
func djb33(seed uint32, k string) uint32 {
var (
l = uint32(len(k))
d = 5381 + seed + l
i = uint32(0)
)
// Why is all this 5x faster than a for loop?
if l >= 4 {
for i < l-4 {
d = (d * 33) ^ uint32(k[i])
d = (d * 33) ^ uint32(k[i+1])
d = (d * 33) ^ uint32(k[i+2])
d = (d * 33) ^ uint32(k[i+3])
i += 4
}
}
switch l - i {
case 1:
case 2:
d = (d * 33) ^ uint32(k[i])
case 3:
d = (d * 33) ^ uint32(k[i])
d = (d * 33) ^ uint32(k[i+1])
case 4:
d = (d * 33) ^ uint32(k[i])
d = (d * 33) ^ uint32(k[i+1])
d = (d * 33) ^ uint32(k[i+2])
}
return d ^ (d >> 16)
}

注意,创建 seed 代码

1
2
3
4
5
6
7
8
9
10

max := big.NewInt(0).SetUint64(uint64(math.MaxUint32))
rnd, err := rand.Int(rand.Reader, max)
var seed uint32
if err != nil {
os.Stderr.Write([]byte("WARNING: go-cache's newShardedCache failed to read from the system CSPRNG (/dev/urandom or equivalent.) Your system's security may be compromised. Continuing with an insecure seed.\n"))
seed = insecurerand.Uint32()
} else {
seed = uint32(rnd.Uint64())
}

引入的包是”crypto/rand” 和 insecurerand “math/rand”。 记得以后使用”crypto/rand” 来  构造随机数,安全些。

钱文翔的博客

使用gRPC交互

发表于 2017-09-20 | 分类于 杂记 |

在.protoc中定义gRPC接口:

1
2
3
4
5
6
7
syntax = "proto3";
package example;
service FormatData {
rpc DoFormat(Data) returns (Data){}
}
message Data {
string text = 1;

使用protoc将.protoc转化为你想要的语言的文件,方便调动 。
编译器protoc的下载地址:
https://github.com/google/protobuf/releases/tag/v3.4.1

Windows的版本:
https://github.com/google/protobuf/releases/download/v3.4.0/protoc-3.4.0-win32.zip

使用protoc将.proto文件转为对应语言的文件。如果你想要python的协议文件的话,则将参数设置为–python_out:
protoc -I=./ –python_out=./ ./rpc.proto

若是Golang的话,则为:
protoc -I=./ –go_out=./ ./rpc.proto

使用Python的话,可以:

1
2
3
4
pip install grpcio
pip install protobuf
pip install grpcio-tools
python -m grpc_tools.protoc -I../../protos --python_out=. --grpc_python_out=. ../../protos/helloworld.proto

proto3和proto2语法不一样,基本上是关键字变少了。

例子

钱文翔的博客

(翻译)For Range Semantics

发表于 2017-08-07 | 分类于 Go学习小记 |

原文是《For Range Semantics》

序章

为了更好的理解这篇文章中展现的内容,你应该先阅读以下文章:

四篇文章的索引:

  1. Language Mechanics On Stacks And Pointers

  2. Language Mechanics On Escape Analysis

  3. Language Mechanics On Memory Profiling

  4. Design Philosophy On Data And Semantics

值的概念和指针的语义在 Go 中无处不在。正如之前的文章中说的那样,语义一致性对于完整性和可读性至关重要。它让开发人员,随着代码库的增长,保持一个强大的代码库的心理模型。它可以尽可能减少错误,副作用和未知的表现。

引言

这这篇文章中,我将会探索在 Go 中 for range 语法块同时提供值和指针两种语义形式。我会教你语义的使用并展示给你这个语义如何衍生至更深层次。然后,我会使用一个简单的例子,来展示使用这些语义会出现的错误。

阅读全文 »
钱文翔的博客

(翻译)go-styleguide

发表于 2017-08-02 | 分类于 Go学习小记 |

原文在这里: https://github.com/bahlo/go-styleguide/blob/master/README.md

Go Styleguide Go的风格指南

This serves as a supplement to
Effective Go, based on years of
experience and inspiration/ideas from conference talks.

这篇文章作为Effective Go的补充,这是根据多年的经验和来自会议讨论的灵感/想法来写的。

阅读全文 »
钱文翔的博客

Gugo!我自己写的简易静态博客生成器

发表于 2017-07-26 | 分类于 杂记 |

我自己使用Go写了一个简易的静态博客生成器。可以将使用markdown写的博客文章转换成html博客页面。写这篇文章的时候,已经实现了博客主页文章列表,博客文章展示,tag列表和分类列表。

我的github项目–gugo

缘起

在看完并翻译writing%20a%20Static%20Blog%20Generator%20in%20Go/)了这篇文章之后,我就有了自己写一个静态博客的想法。

在此之前,我的博客使用的是hexo,看了不少hexo的document来学习使用hexo。为了使我的博客更加赏心悦目,我一直致力于更换hexo的theme,并乐此不疲。

偶然间从github的trending上看到hugo,第一反应是–“哎哟,不错哟”。我对其这么有好感的主要原因是hugo是用Go写的。然后我就去官方网站看hugo的–啧啧,Hugo的官网真是丑到爆!我直接下拉看themes,嗯,真丑。(我写这篇博客的时候,hugo提供的theme样例已经很多了,看起来还很不错)

所以,我虽然觉得hugo不错,但因为官方提供的theme样例丑的原因,而并没有使用hugo生成我的静态博客。虽然hugo只是一个静态博客生成器,和网站的theme并无太大关系,但是我一个theme的美观足以促使我使用这个项目。hugo没有代替hexo在我心中的地位,但是她也给了我很深的印象。

当看到《Writing a Static Blog Generator in Go》这篇文章的时候,我就表现出了极大的兴趣。这篇博客写的非常好,我迫不及待的翻译了这篇文章。让我受益匪浅。

阅读全文 »
钱文翔的博客

(翻译)writing a Static Blog Generator in Go

发表于 2017-07-04 | 分类于 Go学习小记 |

翻译原文:https://zupzup.org/static-blog-generator-go/?utm_source=golangweekly&utm_medium=emai

Writing a Static Blog Generator in Go 使用Go写一个静态博客生成器

A static-site-generator is a tool which, given some input (e.g. markdown) generates a fully static website using HTML, CSS and JavaScript.

一个静态网站生成器是一个使用HTML, CSS and JavaScript,将给予的输入文件(例如markdown)生成完整的静态网站

Why is this cool? Well, for one it’s a lot easier to host a static site and it’s also (usually) quite a bit faster and resource-friendly. Static sites aren’t the best choice for all use-cases, but for mostly non-interactive websites such as blogs they are great.

为什么这很Cool?是的,对于一个人来说,主持一个静态的网站要容易得多,而且(通常)也会更快,而且资源更友好。静态网站不是对所有情况都是最好的选择,但是对于大多数非交互网站比如博客,是非常好的。

In this post, I will describe the static blog generator I wrote in Go, which powers this blog.

在本文中,我将描述我使用Go编写的静态博客生成器,它为这个博客提供了强大的功能。

阅读全文 »

钱文翔的博客

(翻译)取消多个Goroutines

发表于 2017-06-28 | 分类于 Go学习小记 |

原文 https://chilts.org/2017/06/12/cancelling-multiple-goroutines

Cancelling Multiple Goroutines 取消多协程

When Go was first released, there was a way to do some things in concurrency. As time has gone on, various things have changed. The Context package for one thing. :)

在Go刚开始发行的时候,只有一种并发的方法。随着时间流逝,很多事情都发生了变化。Context包就是其中之一。

This article doesn’t go into all of the ways of doing concurrency but will focus on one problem and take you through a few different solutions so you can see how things have evolved.

这篇文章不会探究所有并发的方法,但是会研究一个问题,并带你熟悉一些不同的解决方法,让你了解方法是如何推论出来的。

The Problem

The problem I’d like to address here is being able to cancel multiple goroutines. There are many blog posts out there (I curate @CuratedGo, please follow) which show how to cancel just one goroutine, but my use-case was slightly more complicated. The rest of this article summarises my progress through getting this to work.

我在这里想提出的问题是取消对协程。有许多blog写了如何取消一个协程,但是我的用例是更加复杂的。下面的文章总结了我实现的过程。

The way we’re going decide when to quit is by listening for a C-c keypress. Of course at that point, we want to make sure we tidy up things nicely at that point. For example, if we’re currently streaming tweets from Twitter, we’d rather we told them we’re finished than just drop the connection.

我们要决定何时退出的方式是监听C-c按键。当然,我们确保我们在这一点上很好地处理事情。例如,如果我们目前正在推送Twitter的推文,我们要告诉他们我们已经完成,而不是放弃了解。

Let’s get started.
让我们开始吧

阅读全文 »

钱文翔的博客

lec01学习笔记

发表于 2017-06-28 | 分类于 6.824课程 |

分为: 存储,通讯和计算
关键点:实现,性能,容错和一致性。一致性和性能不可兼得
split-brain的意思是A true split brain means multiple systems are online and have accessed an exclusive resource simultaneously

MapReduce: 定义Map和Reduce函数,输入数组形式的数据。

扩展:不互相等待,不共享数据。可并发执行。可单纯的配置更多的计算机来获得更多的吞吐量
性能:Hard to build a network than can run 1000x faster than a single computer.So they cared about minimizing movement of data over the network.一般会被网络限制。尽量减少数据在网络上传输。
容错:当Map和Reduce失败时,重新运行。他们仅仅是函数而已–他们不修改输入的内容,不用保存状态,不共享内存,没有Map-Map和Reduce-Reduce的交互。所以重新执行会得到相同的输出。纯函数(pure function)的这个需求是相对于其他并行编程方案的主要限制,也是MR简单的原因。

MapReduce由Mater和Worker组成。Master给workers分配工作,决定worker运行map方法还是reduce方法。当worker执行错误,直接重启worker就好。
master的调度程序

阅读全文 »

钱文翔的博客

使用网页打开本地程序

发表于 2017-06-21 | 分类于 杂记 |

浏览器中使用 window.open()打开 URL。当 URL 为特定的前缀的时候(URI Scheme),就会调用注册表中对应的指令。

写入注册表

参考 https://msdn.microsoft.com/en-us/library/aa767914(VS.85).aspx
其中的关键是在注册表中写入如下的注册表项

1
2
3
4
5
6
7
8
9
10
HKEY_CLASSES_ROOT
alert
(Default) = "URL:Alert Protocol"
URL Protocol = ""
DefaultIcon
(Default) = "alert.exe,1"
shell
open
command
(Default) = "C:\Program Files\Alert\alert.exe" "%1

使用 go 操作注册表.path 为程序的地址。uri 为调用程序的 URI Scheme。
使用 js 调用 window.open(“uri://“)就可以打开程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
func regKey(path string,uri string) error {
vconsoleKey, ok, err := registry.CreateKey(registry.CLASSES_ROOT, uri, registry.CREATE_SUB_KEY|registry.SET_VALUE)
if err != nil {
logger.Printf("CreateKey console err :%v\n", err)
return err
}
if ok {
return err
}
defer vconsoleKey.Close()
vconsoleKey.SetStringValue("URL Protocol", "")
vconsoleKey.SetStringValue("DefaultIcon", path)
shellKey, _, err := registry.CreateKey(vconsoleKey, `shell`, registry.CREATE_SUB_KEY)
if err != nil {
logger.Printf("CreateKey shell err :%v\n", err)
return err
}
openKey, _, err := registry.CreateKey(shellKey, `open`, registry.CREATE_SUB_KEY)
if err != nil {
logger.Printf("CreateKey open err :%v\n", err)
return err
}
commandKey, _, err := registry.CreateKey(openKey, `command`, registry.CREATE_SUB_KEY|registry.SET_VALUE)
if err != nil {
logger.Printf("CreateKey err :%v\n", err)
return err
}
commandKey.SetStringValue("", path)

return nil
}

获取管理员权限

在注册表中写入注册表项需要管理员权限。

  • go get github.com/akavel/rsrc
  • 把 nac.manifest 文件拷贝到当前 windows 项目根目录
  • rsrc -manifest nac.manifest -o nac.syso
  • go build

nac.mainfest 的内容为:

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="requireAdministrator"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
钱文翔的博客

(翻译)BitTorrent-Protocol协议规范

发表于 2016-12-26 | 分类于 DHT |

本文翻译原文:http://www.bittorrent.org/beps/bep_0003.html
BitTorrent是一个分发文件的协议。它通过URL识别内容,旨在与网络无缝集成。它和一般的HTTP相比的优势在于,当同一个文件的多个下载同时发生时,下载者相互上传,使得文件源可以支持非常大量下载,而其负载增加很小。

BitTorrent文件分发有这些部分组成:

  • 一个普通的web服务器
  • 静态“元信息(metainfo)”文件
  • BitTorrent跟踪器(tracker)
  • “原始”下载器
  • 用户web浏览器端
  • 用户下载器端

理想情况下多端用户现在同一个文件

要开始服务,主机将执行以下步骤

  1. 开启一个跟踪器(tracker)
  2. 开启一个普通的web服务器,例如Apache
  3. 在他们的Web服务器上,将扩展名.torrent与mimetype应用程序/ x-bittorrent相关联。
  4. 使用提供的完整的文件和跟踪器URL,生成一个元数据(metainfo)的(.torrent) 文件
  5. 将元信息(.torrent)文件放在web服务器上
  6. 链接来自其它网页的(.torrent)元数据文件
    阅读全文 »
12
钱文翔

钱文翔

自理,自励,自立

17 日志
5 分类
22 标签
© 2018 钱文翔
由 Hexo 强力驱动
主题 - NexT.Mist