geektutu / blog

极客兔兔的博客,Coding Coding 创建有趣的开源项目。
https://geektutu.com
Apache License 2.0
166 stars 21 forks source link

动手写分布式缓存 - GeeCache第三天 HTTP 服务端 | 极客兔兔 #63

Open geektutu opened 4 years ago

geektutu commented 4 years ago

https://geektutu.com/post/geecache-day3.html

7天用 Go语言/golang 从零实现分布式缓存 GeeCache 教程(7 days implement golang distributed cache from scratch tutorial),动手写分布式缓存,参照 groupcache 的实现。本文介绍了如何使用标准库 http 搭建 HTTP Server,为 GeeCache 单机节点搭建 HTTP 服务,并进行相关的测试。

kkBill commented 4 years ago

博主你好,首先感谢你的这个系列,获益良多。在本节我有个疑问,由于view, err := group.Get(key)获取到的值已经是一个副本了,为什么在这里w.Write(view.ByteSlice())还要再拷贝一遍呢?我感觉直接w.Write(view)就好了呀?

geektutu commented 4 years ago

@kkBill view.b 是 cache 中真实存储的值,不是副本,切片指向的是同一个位置。view.ByteSlice() 获取的是副本,不过这里可以用 w.Write(view.b) 代替,写入 http body 不会影响 cache 的值。

BowenXiao1999 commented 4 years ago

楼主,请问你对import原理是怎么思考的?是不是把目标代码完全copy到当前代码文件?看到代码中的设计是,geecache.go会把新建的group保存到一个全局变量中。由于import了geecache.go,那么这个全局变量也会移到main.go中,所以这种做法是对的。

ZhouShuaiShuai commented 3 years ago

你好博主。我照着你的写的时候出现了bug。后来发现是http.go文件中的ServeHTTP方法里面的切割字符串的问题。parts := strings.SplitN(r.URL.Path[len(p.basePath):], "/", 2)这里应该修改为parts := strings.SplitN(r.URL.Path[len(p.basePath)+1:], "/", 2) 不然的话在分割的时候会把"/_geecache/scores"分割为 ""和"_geecache/scores" 导致程序出错。

geektutu commented 3 years ago

@BowenXiao1999,golang 变量是 package 级别的,import 了 geecache,就可以通过 geecache.变量名 这样的方式访问 geecache 内部的变量。

geektutu commented 3 years ago

@ZhouShuaiShuai 这一块应该是没问题的。r.URL.Path[len(p.basePath):] 已经刚好是 / 的下一个字符,下标从 0 开始的。

const defaultBasePath = "/_geecache/"

func main() {
        p := "/_geecache/scores/abc"
        parts := strings.SplitN(p[len(defaultBasePath):], "/", 2)
        fmt.Println(parts)  # [scores abc]
}
whitebluepants commented 3 years ago

大佬想问一下go mod这方面的知识,在写最后main的时候碰到了一个问题,ide能够智能提示geecache(也就是能算识别出geecache这个文件夹了吧),但是import之后又提示不存在这个包. 弄了好一会才知道要在geecache文件夹下新建一个gomod文件,并且day3-http-server目录(就是main.go的目录)也要新建一个gomod并指明require geecache并replace geecache的地址才解决这个问题。 到现在对go mod这方面还没搞的特别清楚, 求教

whitebluepants commented 3 years ago

像图中,cache.go使用到同目录下的lru文件夹里的代码,但是lru文件夹里并没有创建go mod文件,为什么这时候又可以import呢。

binkesi commented 2 years ago

@whitebluepants 像图中,cache.go使用到同目录下的lru文件夹里的代码,但是lru文件夹里并没有创建go mod文件,为什么这时候又可以import呢。

我猜测你在geecache下的go mod初始化的名字就是geecache吧,所以你这里的import ("geecache/lru")的geecache实际上是module的名字。个人理解go里面的module就是pacakge的管理器,我一般习惯只在项目最外面建一个go.mod文件,起一个和文件夹不同的名字,比如在day4-consistent-hash下面执行go mod init consistent-hash,这里的import就可以写成import ("consistent-hash/geecache/lru"),这里面的consistent-hash是module名,geecache和lru就都是package名,参考:https://go.dev/blog/using-go-modules

99Kies commented 2 years ago
    parts := strings.SplitN(r.URL.Path[len(p.basePath)+1:], "/", 2)
018429 commented 2 years ago
googs1025 commented 2 years ago

您好,我在最外層的go.mod中,声明require geecache v0.0.0 replace geecache => ./geecache 这两句时,都会找不到 请问是啥原因的

husterdjx commented 2 years ago

博主您好,我跟着做了一下分布式缓存和web框架的两个小任务并且全部完成和看懂实现了,我后续应该做的是怎么能内化为自己的知识并且在面试中能够表明我学到了什么呢?有什么进一步学习的指点嘛?

junbin-yang commented 2 years ago

@husterdjx 博主您好,我跟着做了一下分布式缓存和web框架的两个小任务并且全部完成和看懂实现了,我后续应该做的是怎么能内化为自己的知识并且在面试中能够表明我学到了什么呢?有什么进一步学习的指点嘛?

对已经实现的内容进行优化,如web框架,实际对比性能相较gin还是差了很多的。至于缓存的优化。我也才刚开始看。比较明显的是节点之间的通讯http改成使用rpc。

husterdjx commented 2 years ago

@husterdjx 博主您好,我跟着做了一下分布式缓存和web框架的两个小任务并且全部完成和看懂实现了,我后续应该做的是怎么能内化为自己的知识并且在面试中能够表明我学到了什么呢?有什么进一步学习的指点嘛?

对已经实现的内容进行优化,如web框架,实际对比性能相较gin还是差了很多的。至于缓存的优化。我也才刚开始看。比较明显的是节点之间的通讯http改成使用rpc。

好的谢谢!

chillsoul commented 2 years ago

parts := strings.SplitN(r.URL.Path[len(p.basePath):], "/", 2)

这里应该是

parts := strings.Split(r.URL.Path[len(p.basePath):], "/")

吧?不然永远也不会出现被分成2个以上的情况

GenePaal commented 2 years ago

发现bug, ServeHTTP 方法中的 parts := strings.SplitN(r.URL.Path[len(p.basePath):], "/", 2) 这里应该修改为parts := strings.SplitN(r.URL.Path[len(p.basePath)+1:], "/", 2)
基础语法就不解释了, 另外很好奇各路大佬都没遇到这个问题吗? 我应该不是第一个😂

ShiMaRing commented 2 years ago

@GenePaal 发现bug, ServeHTTP 方法中的 parts := strings.SplitN(r.URL.Path[len(p.basePath):], "/", 2) 这里应该修改为parts := strings.SplitN(r.URL.Path[len(p.basePath)+1:], "/", 2)
基础语法就不解释了, 另外很好奇各路大佬都没遇到这个问题吗? 我应该不是第一个😂

我感觉是不用的,因为按照 len(p.basePath) 取值时,已经利用数组下标完成了一个偏移,没必要再加上1了

ShiMaRing commented 2 years ago

@chillsoul parts := strings.SplitN(r.URL.Path[len(p.basePath):], "/", 2)

这里应该是

parts := strings.Split(r.URL.Path[len(p.basePath):], "/")

吧?不然永远也不会出现被分成2个以上的情 @chillsoul parts := strings.SplitN(r.URL.Path[len(p.basePath):], "/", 2)

这里应该是

parts := strings.Split(r.URL.Path[len(p.basePath):], "/")

吧?不然永远也不会出现被分成2个以上的情况

按照文档说法 //n > 0: at most n substrings; the last substring will be the unsplit remainder. 超过n的数据将不会被分割,并直接保存至数组中,个人认为博主这样做的理由是能够让存储路径支持 / 字符

cyb0225 commented 2 years ago

@ShiMaRing

@chillsoul parts := strings.SplitN(r.URL.Path[len(p.basePath):], "/", 2)

这里应该是

parts := strings.Split(r.URL.Path[len(p.basePath):], "/")

吧?不然永远也不会出现被分成2个以上的情 @chillsoul parts := strings.SplitN(r.URL.Path[len(p.basePath):], "/", 2)

这里应该是

parts := strings.Split(r.URL.Path[len(p.basePath):], "/")

吧?不然永远也不会出现被分成2个以上的情况

按照文档说法 //n > 0: at most n substrings; the last substring will be the unsplit remainder. 超过n的数据将不会被分割,并直接保存至数组中,个人认为博主这样做的理由是能够让存储路径支持 / 字符 我也觉得。 为什么需要先prefix判断basepath,不能直接先按split/分割数组,判断数组长度,然后判断basepath吗

weekmy commented 1 year ago

@GenePaal 发现bug, ServeHTTP 方法中的 parts := strings.SplitN(r.URL.Path[len(p.basePath):], "/", 2) 这里应该修改为parts := strings.SplitN(r.URL.Path[len(p.basePath)+1:], "/", 2)
基础语法就不解释了, 另外很好奇各路大佬都没遇到这个问题吗? 我应该不是第一个😂

const defaultBasePath = "/_geecache/"
const defaultBasePath = "/_geecache"
少了结尾的“/”是解析不出来的,就是字符串分割,感觉没必要纠结

@GenePaal 发现bug, ServeHTTP 方法中的 parts := strings.SplitN(r.URL.Path[len(p.basePath):], "/", 2) 这里应该修改为parts := strings.SplitN(r.URL.Path[len(p.basePath)+1:], "/", 2)
基础语法就不解释了, 另外很好奇各路大佬都没遇到这个问题吗? 我应该不是第一个😂

qbmiller commented 1 year ago

@GenePaal 发现bug, ServeHTTP 方法中的 parts := strings.SplitN(r.URL.Path[len(p.basePath):], "/", 2) 这里应该修改为parts := strings.SplitN(r.URL.Path[len(p.basePath)+1:], "/", 2)
基础语法就不解释了, 另外很好奇各路大佬都没遇到这个问题吗? 我应该不是第一个😂

你是不是没运行啊?

turingboot commented 1 year ago

评论区大部分的问题都很nt,麻烦先把go的语法基础搞透彻点

China-zhang-hui commented 1 year ago

在浏览器地址栏输入http://localhost:9999/_geecache/scores/Tom回车 结果是下载了以Tom命名的无后缀命名文件 为什么页面不是显示Tom的成绩,而是下载文件

GuYith commented 1 year ago

@China-zhang-hui 在浏览器地址栏输入http://localhost:9999/_geecache/scores/Tom回车 结果是下载了以Tom命名的无后缀命名文件 为什么页面不是显示Tom的成绩,而是下载文件

可以看看这行代码

w.Header().Set("Content-Type", "application/octet-stream")

application/octet-stream 通常在下载文件场景中使用,它会告知浏览器写入的内容是一个字节流,浏览器处理字节流的默认方式就是下载

10212 commented 10 months ago

@99Kies

  parts := strings.SplitN(r.URL.Path[len(p.basePath)+1:], "/", 2)

这样改过是对的,不然获取的group为空

gocnpan commented 10 months ago

@10212

@99Kies

    parts := strings.SplitN(r.URL.Path[len(p.basePath)+1:], "/", 2)

这样改过是对的,不然获取的group为空

不用改,你要看到

const defaultBasePath = "/_geecache/" // 这里前后都有 '/'
parts := strings.SplitN(r.URL.Path[len(p.basePath):], "/", 2)