Git Product home page Git Product logo

geektutu / 7days-golang Goto Github PK

View Code? Open in Web Editor NEW
14.7K 14.7K 2.4K 1.01 MB

7 days golang programs from scratch (web framework Gee, distributed cache GeeCache, object relational mapping ORM framework GeeORM, rpc framework GeeRPC etc) 7天用Go动手写/从零实现系列

Home Page: https://geektutu.com/post/gee.html

License: MIT License

Go 99.05% CSS 0.02% Makefile 0.32% HTML 0.34% Shell 0.27%
golang learning scratch starter-project

7days-golang's Introduction

7 days golang programs from scratch

CodeSize LICENSE

README 中文版本

7天用Go从零实现系列

7天能写什么呢?类似 gin 的 web 框架?类似 groupcache 的分布式缓存?或者一个简单的 Python 解释器?希望这个仓库能给你答案。

推荐先阅读 Go 语言简明教程,一篇文章了解Go的基本语法、并发编程,依赖管理等内容。

推荐 Go 语言笔试面试题,加深对 Go 语言的理解。

推荐 Go 语言高性能编程(项目地址),写出高性能的 Go 代码。

期待关注我的「知乎专栏」和「微博」,查看最近的文章和动态。

7天用Go从零实现Web框架 - Gee

Gee 是一个模仿 gin 实现的 Web 框架,Go Gin简明教程可以快速入门。

7天用Go从零实现分布式缓存 GeeCache

GeeCache 是一个模仿 groupcache 实现的分布式缓存系统

7天用Go从零实现ORM框架 GeeORM

GeeORM 是一个模仿 gormxorm 的 ORM 框架

gorm 准备推出完全重写的 v2 版本(目前还在开发中),相对 gorm-v1 来说,xorm 的设计更容易理解,所以 geeorm 接口设计上主要参考了 xorm,一些细节实现上参考了 gorm。

7天用Go从零实现RPC框架 GeeRPC

GeeRPC 是一个基于 net/rpc 开发的 RPC 框架 GeeRPC 是基于 Go 语言标准库 net/rpc 实现的,添加了协议交换、服务注册与发现、负载均衡等功能,代码约 1k。

WebAssembly 使用示例

具体的实践过程记录在 Go WebAssembly 简明教程

  • 示例一:Hello World | Code
  • 示例二:注册函数 | Code
  • 示例三:操作 DOM | Code
  • 示例四:回调函数 | Code

What can be accomplished in 7 days? A gin-like web framework? A distributed cache like groupcache? Or a simple Python interpreter? Hope this repo can give you the answer.

Web Framework - Gee

Gee is a gin-like framework

  • Day 1 - http.Handler Interface Basic Code
  • Day 2 - Design a Flexiable Context Code
  • Day 3 - Router with Trie-Tree Algorithm Code
  • Day 4 - Group Control Code
  • Day 5 - Middleware Mechanism Code
  • Day 6 - Embeded Template Support Code
  • Day 7 - Panic Recover & Make it Robust Code

Distributed Cache - GeeCache

GeeCache is a groupcache-like distributed cache

  • Day 1 - LRU (Least Recently Used) Caching Strategy Code
  • Day 2 - Single Machine Concurrent Cache Code
  • Day 3 - Launch a HTTP Server Code
  • Day 4 - Consistent Hash Algorithm Code
  • Day 5 - Communication between Distributed Nodes Code
  • Day 6 - Cache Breakdown & Single Flight | Code
  • Day 7 - Use Protobuf as RPC Data Exchange Type | Code

Object Relational Mapping - GeeORM

GeeORM is a gorm-like and xorm-like object relational mapping library

Xorm's desgin is easier to understand than gorm-v1, so the main designs references xorm and some detailed implementions references gorm-v1.

  • Day 1 - database/sql Basic | Code
  • Day 2 - Object Schame Mapping | Code
  • Day 3 - Insert and Query | Code
  • Day 4 - Chain, Delete and Update | Code
  • Day 5 - Support Hooks | Code
  • Day 6 - Support Transaction | Code
  • Day 7 - Migrate Database | Code

RPC Framework - GeeRPC

GeeRPC is a net/rpc-like RPC framework

Based on golang standard library net/rpc, GeeRPC implements more features. eg, protocol exchange, service registration and discovery, load balance, etc.

  • Day 1 - Server Message Codec | Code
  • Day 2 - Concurrent Client | Code
  • Day 3 - Service Register | Code
  • Day 4 - Timeout Processing | Code
  • Day 5 - Support HTTP Protocol | Code
  • Day 6 - Load Balance | Code
  • Day 7 - Discovery and Registry | Code

Golang WebAssembly Demo

  • Demo 1 - Hello World Code
  • Demo 2 - Register Functions Code
  • Demo 3 - Manipulate DOM Code
  • Demo 4 - Callback Code

7days-golang's People

Contributors

chaunceyjiang avatar geektutu avatar imageslr avatar imgbotapp avatar xutaoa avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

7days-golang's Issues

geerpc第三天服务注册

geerpc第三天服务注册中,有newArgv和newReplyv,有个疑问:为什么newReplyv中需要进一步判断是否为slice或者map,而newArgv中不需要呢?难道是因为Argv一定就不会是slice或者map吗?

为什么cache需要设计成自动回源的?

你好,我最近在看geeCache的实现,有些不明白地方想请教一下

// Get value for a key from cache
func (g *Group) Get(key string) (ByteView, error) {
	if key == "" {
		return ByteView{}, fmt.Errorf("key is required")
	}

	if v, ok := g.mainCache.get(key); ok {
		log.Println("[GeeCache] hit")
		return v, nil
	}

	return g.load(key)
}

比如这段函数的末尾,在cache未命中的情况下,会自己fallback去load缓存

我没有理解这里的逻辑,为什么缓存需要自己去fallbak?难道不应该直接返回未命中么?
如果涉及成这样,那缓存岂不是永远是命中的?

中间件源码有个疑问

func (c *Context) Next() {
c.index++
s := len(c.handlers)
for ; c.index < s; c.index++ {
c.handlers[c.index](c)
}
}

这里的for循环是不是应该改为直接调用下一个Handler

func (c *Context) Next() {
c.index++
s := len(c.handlers)
if c.index < s {
c.handlers[c.index](c)
}
}

rpc多次调用close

func (server *Server) ServeConn(conn io.ReadWriteCloser) {
	defer func() { _ = conn.Close() }()
    // ....

}
func (server *Server) serveCodec(cc codec.Codec) {
	sending := new(sync.Mutex) // make sure to send a complete response
	wg := new(sync.WaitGroup)  // wait until all request are handled
	for {
		//...
	}
	wg.Wait()
	_ = cc.Close() // 这里
}

gobCodec的close 直接调用了conn.close
这里好像多次调用了conn的close?

stackOverflow的讨论

似乎这样会出问题,我想应该有两种解决方案

  1. 把codec的close都实现成幂等的
  2. 把ServeConn的Close去掉

希望解惑 谢谢

gee-cache/day1-lru/ 文档描述问题

day1-lru/geecache/lru/lru.go - github

day1文档:如果键存在,则更新对应节点的值,并将该节点移到队尾。
代码:更新存在的key,元素是移动到列表l的最前

code:
// MoveToFront moves element e to the front of list l.
// If e is not an element of l, the list is not modified.
// The element must not be nil.
func (l *List) MoveToFront(e *Element) {
if e.list != l || l.root.next == e {
return
}
// see comment in List.Remove about initialization of l
l.move(e, &l.root)
}

testing res:
2023-08-29T11:34:23+08:00 INF lru_test.go:103 > nbytes:4 ,maxBytes201 [tag]=TestOnEvicted
ll's element:{k1,v1} c.nbytes:4,elementSize:4

2023-08-29T11:34:23+08:00 INF lru_test.go:103 > nbytes:8 ,maxBytes201 [tag]=TestOnEvicted
ll's element:{k2,v2} c.nbytes:8,elementSize:4
ll's element:{k1,v1} c.nbytes:8,elementSize:4

2023-08-29T11:34:23+08:00 INF lru_test.go:103 > nbytes:12 ,maxBytes201 [tag]=TestOnEvicted
ll's element:{k3,v3} c.nbytes:12,elementSize:4
ll's element:{k2,v2} c.nbytes:12,elementSize:4
ll's element:{k1,v1} c.nbytes:12,elementSize:4

2023-08-29T11:34:23+08:00 INF lru_test.go:103 > nbytes:16 ,maxBytes201 [tag]=TestOnEvicted
ll's element:{k2,v22222} c.nbytes:16,elementSize:8
ll's element:{k3,v3} c.nbytes:16,elementSize:4
ll's element:{k1,v1} c.nbytes:16,elementSize:4

gee-PostForm能获取form-data,但是不能获取raw-data;是否能增加对应的框架层封装

目前我这里只是一个想法,但是没有达到框架层直接使用的效果。

type RowData struct {
	Username string `json:"username"`
	Password string`json:"password"`
}

// PostRow context provide the method to get the POST request row-data by key
func (context *Context) PostRow(key string) string {
	bodyData, _ := ioutil.ReadAll(context.Req.Body)
	if len(bodyData) != 0 {
		var rowData RowData
		context.BodyData = &rowData
		_ = json.Unmarshal(bodyData, &rowData)
	}
	r := reflect.ValueOf(context.BodyData)
	keyValue := reflect.Indirect(r).FieldByName(key).String()
	return keyValue
}```

想请教一下geecache项目中一致性哈希有实际发挥作用吗?

我的理解是:所有节点的地址都是服务启动前就配置好的,服务器也没有持久化功能,实际上是在内存中存储kv,那么假设用户要新增节点或删除节点,就得把所有服务停下后修改配置,重启后内存中又是空的了,所有的kv又要重新分配到节点上,这个过程中一致性哈希好像没有发挥实际的作用。请问我的理解对吗?如果对的话这个问题该怎么解决呢

意见征求,下一个7天实现的项目大家可以推荐下。

已经实现:

  • 第一期:模仿 gin 的 web 框架 gee
  • 第二期:模仿 groupcache 的 分布式缓存 geecache
  • 第三期:模仿 gorm 的 orm 框架 geeorm
  • 第四期:参考 net/rpc 的 rpc 框架 geerpc

实现中:

  • 第五期:模仿 bbolt 的KV数据库 geebolt

最新动态可以关注:知乎 Go语言 或微博 极客兔兔

订阅方式:watch geektutu/blog ,每篇文章都能收到邮件通知,或通过 RSS 订阅。

闲暇之余,可以看一看 Go 语言高性能编程Go 语言笔试面试题

小白求问 缓存day2不加锁的话 会出现什么问题呀

image
就是类似框里的代码会被打乱执行顺序吗,好像其实影响也不是特别大?

因为如果put和get如果外层逻辑上不加控制的话 也不太能保证get读到的值是在put之前还是put之后的吗? 我在想测试用例里加一些体现加锁不加锁表现上区别的是不是更容易理解点

基于gin的blog系统

大佬能出一个用gin+vue制作一个blog的教程吗,非常期待,感觉大佬的文章系统有逻辑,容易学习

geerpc day4服务端超时处理bug一种可能的解决方案

func (server *Server) handleRequest(cc codec.Codec, req *request, sendMutex *sync.Mutex, wg *sync.WaitGroup, timeout time.Duration) {
	defer wg.Done()

	finished := make(chan struct{})
	var timeoutFlag int32

	go func() {
		defer func() {
			close(finished)
		}()
		err := req.svc.call(req.mtype, req.argv, req.replyv)
		// 执行出错
		if err != nil {
			req.h.Error = err.Error()
			server.sendResponse(cc, req.h, invalidRequest, sendMutex)
			return
		}
		// 超时
		if atomic.LoadInt32(&timeoutFlag) == 1 {
			req.h.Error = "server handle timeout"
			server.sendResponse(cc, req.h, invalidRequest, sendMutex)
			return
		}
		server.sendResponse(cc, req.h, req.replyv.Interface(), sendMutex)
	}()

	// 没有超时控制
	if timeout == 0 {
		<-finished
		return
	}

	// 有超时控制
	select {
	case <-time.After(timeout):
		atomic.StoreInt32(&timeoutFlag, 1)
		return
	case <-finished:
		return
	}
}

day4 启动报错 panic: runtime error: invalid memory address or nil pointer dereference

day4 启动报错:

panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x8 pc=0x66dfda]

goroutine 1 [running]:
gee/gee.getNestPrefix(0x0, 0x0, 0x0, 0x8, 0x6a3440)
        E:/go_project/src/gee/gee/gee.go:88 +0x3a
gee/gee.(*RouterGroup).addRoute(0xc0000562c0, 0x711e2d, 0x3, 0x7123d1, 0x6, 0x72
ab98)
        E:/go_project/src/gee/gee/gee.go:63 +0x4b
gee/gee.(*RouterGroup).GET(...)
        E:/go_project/src/gee/gee/gee.go:69
main.main()
        E:/go_project/src/gee/main.go:45 +0x185

在 gee.go 添加以下方法后就可以了:

func (engine *Engine) addRoute(method string, pattern string, handler HandlerFunc) {
	engine.router.addRoute(method, pattern, handler)
}

// GET defines the method to add GET request
func (engine *Engine) GET(pattern string, handler HandlerFunc) {
	engine.addRoute("GET", pattern, handler)
}

// POST defines the method to add POST request
func (engine *Engine) POST(pattern string, handler HandlerFunc) {
	engine.addRoute("POST", pattern, handler)
}

但是在 day5 没有这三个方法也正常运行,请问为什么呢?
还有非常喜欢大佬的教程,就是 day3 的 trie 树目前也不是特别懂

Go 小白问一个环境问题,google 没找到很好的解决方案

gee-web day1 的 base3 run main.go 遇到找不到 package gee 的问题:

373356eec6e8f4fc4b39b2e05fb0905

这是我的 go env:

image

google 了一下,好像要把当前项目的路径加到 gopath 去,但这样好像有点蠢(如果每个导入自己写的 package 都要这么做的话)? 请问有什么优雅的解决方案吗?

中间件那部分的main.go中OnlyforV2方法

func onlyForV2() gwebframe.HandlerFunc {
	return func(c *gwebframe.Context) {
		// Start timer
		t := time.Now()
		// if a server error occurred
		//c.Fail(500, "Internal Server Error")
		// Calculate resolution time
		log.Printf("[%d] %s in %v for group v2", c.StatusCode, c.Req.RequestURI, time.Since(t))
	}
}

加入了c.Fail会导致调用onlyForV2时必然报错

Gee可以写在应届生简历的项目中吗

兔兔,作为菜鸡应届生(通信专业),真诚发问一下,Gee的这个项目(或7days系列的其他项目)适合放在简历的项目中吗?面试官是否会认同这类项目呢?目前Gee的项目我跟着做的差不多了,就是前缀树路由那块还没有彻底搞懂。
转码好艰难...,希望兔兔有时间能回复一下

gee-web项目中 路由注册顺序,导致的Bug

func TestGetRoute(t *testing.T) {
	r := newRouter()
	r.addRoute("GET", "/", nil)
	r.addRoute("GET", "/hello/:id", nil)
	r.addRoute("GET", "/hello/b/c", nil)
	n, ps := r.getRoute("GET", "/hello/21/c")
	fmt.Printf("n: %v\n", n)  // n: node{pattern=/hello/b/c, part=c, isWild=false}
	fmt.Printf("ps: %v\n", ps)//ps: map[]
}

我们期望得到 n == nil, ps == nil , 但实际上却匹配到了静态路由 pattern=/hello/b/c.

由于注册顺序导致的 路由树可以简化为:
[0] root: path: /
[1] path: hello
[2] path: :id pattern : "/hello/:id"
[3] path: c pattern : "hello/b/c"

主要原因为 先注册的 路由为 /hello/:id , 后注册的路由为 /hello/b/c , 在查找时,没有返回 nil, 源代码如下:

func (n *node) matchChild(part string) *node {
	for _, child := range n.children {
        if child.part == part || child.isWild {  // false,  true
            // 在查找 part为 :id 这一层时, 直接返回了child 
			return child
		}
	}
	return nil
}
func (n *node) insert(pattern string, parts []string, height int) {
	if len(parts) == height {
		n.pattern = pattern
		return
	}

    part := parts[height]
    child := n.matchChild(part) // 由于返回的不是 nil , 而是  node.part == ":id" 这个节点。
	if child == nil {
		child = &node{part: part, isWild: part[0] == ':' || part[0] == '*'}
		n.children = append(n.children, child)
	}
    
	child.insert(pattern, parts, height+1)
}

要想达到预期 结果 n == nil, ps == nil , 就只能修改注册顺序。

先注册静态路由, 确保 matchChildren(part string) []*node  方法返回的切片 包含 child.part="b" 的这个节点。
r.addRoute("GET", "/hello/b/c", nil)
r.addRoute("GET", "/hello/:id", nil)

syscall/js报错

额,我用的goland在跑hello-world里的main.go,报了这个错,我搜了一下需要修改GOOS和GOARCH,但是没有搜到怎么修改这两个变量,求解决。
package command-line-arguments
imports syscall/js: build constraints exclude all Go files in D:\Go\src\syscall\js

序列化和反序列化问题

我在写demo时,向一个网络连接中先发送一个json的数据,再发送一个gob的数据。
然后使用json解码器进行解码,json解码器这时会把两个数据都从网络连接中读出来,存在json解码器解码后,造成gob解码器无法获取到对应的数据,作者的rpc框架也是用的json解码器和gob解码器,并且也是先发送json数据,再发送gob数据,在测试中没有发现这个问题,请问是如何避免的?

1

1

gee路由匹配bug

这里的判断太简单了。
假设为/api这个group配置了中间件,由于只使用前缀匹配,访问另一个group 的时候(例如/apixxx,虽然应该没人这样命名吧),这些中间件也会执行。
能否通过判断 前缀匹配的情况下,请求URL的下一个字符是否为 / 解决?

if strings.HasPrefix(request.URL.Path, group.prefix) {
	if len(request.URL.Path) > len(group.prefix) && request.URL.Path[len(group.prefix)] != '/' {
		continue
	}
	middlewares = append(middlewares, group.middlewares...)
}

geecache day4 一致性哈希疑问

尊敬的作者,您好:
在学习您的geecache day4 一致性哈希章节中针对该算法有个疑问,“环上有 peer2,peer4,peer6 三个节点,key11,key2,key27 均映射到 peer2,key23 映射到 peer4。” 这个部分不太理解,当前环上有三个节点的话,key11映射的节点为 11%3=2顺时针映射peer2,key2映射的节点应该 2%3=2顺时针映射peer2,key27映射的节点为27%3=0顺时针映射peer2,key23映射的节点不也应该为23%3=2 peer2 吗?
这个地方一直没搞明白,望您有空点拨下。万分感谢。

gee-rpc day1 通信过程

有点不懂的是:GobType中为什么要使用json做解码编码?
或者说为什么直接默认使用JsonType?
感觉形式上有点不统一。

day1-rpc: 服务端是如何识别Option和后续内容的界限? 不同的Option编码结果的字节数并不相等

文中提到 动手写RPC框架 - GeeRPC第一天 服务端与消息编码

一般来说,涉及协议协商的这部分信息,需要设计固定的字节来传输的。
但是为了实现上更简单,GeeRPC 客户端固定采用 JSON 编码 Option,后续的 header 和 body 的编码方式由 Option 中的 CodeType 指定,服务端首先使用 JSON 解码 Option,然后通过 Option 得 CodeType 解码剩余的内容。

什么叫固定字节来传输呢? 我理解下面的两个结构体,序列化之后的字节数目应该是不等的。

{MagicNumber:3927900 CodecType:application/gob}
{MagicNumber:3927900 CodecType:application/json}

代码验证, 第一个结构体编码后54字节,第二个结构体编码后55字节。
所以我有个疑问哈, 服务端是如何识别Option和后续消息体的边界的呢?

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
)

type Type string

const (
	GobType  Type = "application/gob"
	JsonType Type = "application/json"
)

const MagicNumber = 0x3bef5c

type Option struct {
	MagicNumber int
	CodecType   Type
}

var DefaultOption = &Option{
	MagicNumber: MagicNumber,
	CodecType:   GobType,
}

func main() {
	var buf bytes.Buffer
	// send options
	_ = json.NewEncoder(&buf).Encode(&Option{
		MagicNumber: MagicNumber,
		CodecType:   GobType,
	})
	fmt.Println(buf.Bytes())
	fmt.Println(buf.Len())
	fmt.Println(buf.String())

	buf.Reset()
	_ = json.NewEncoder(&buf).Encode(&Option{
		MagicNumber: MagicNumber,
		CodecType:   JsonType,
	})

	fmt.Println(buf.Bytes())
	fmt.Println(buf.Len())
	fmt.Println(buf.String())
}

// Output
//[123 34 77 97 103 105 99 78 117 109 98 101 114 34 58 51 57 50 55 57 48 48 44 34 67 111 100 101 99 84 121 112 101 34 58 34 97 112 112 108 105 99 97 116 105 111 110 47 103 111 98 34 125 10]
//54
//{"MagicNumber":3927900,"CodecType":"application/gob"}
//
//[123 34 77 97 103 105 99 78 117 109 98 101 114 34 58 51 57 50 55 57 48 48 44 34 67 111 100 101 99 84 121 112 101 34 58 34 97 112 112 108 105 99 97 116 105 111 110 47 106 115 111 110 34 125 10]
//55
//{"MagicNumber":3927900,"CodecType":"application/json"}

http,tcp,rpc

想问下http的rpc和tcp的rpc有啥区别。
看完http rpc,感觉流程是通过向server(给server发一个connect的http请求),得到client(包含了tcp conn),然后就是发rpc数据。
服务端:实现serveHTTP方法,然后就是handleHTTP,进入serveHTTP,先检查http请求是不是connect,然后就到了原来的serveConn,
就感觉这http和tcp的rpc没有啥区别,希望大佬可以解答一下。

geerpc 在超时处理方面的 bug

在 geerpc 的 day4-timeout 中执行 go test -v 有一定概率会出现卡死的现象,具体日志如下:

~/ExcellentOriginSource/7days-golang/gee-rpc/day4-timeout$ go test -v
=== RUN   TestClient_dialTimeout
=== PAUSE TestClient_dialTimeout
=== RUN   TestClient_Call
=== PAUSE TestClient_Call
=== RUN   TestNewService
2020/12/07 12:10:47 rpc server: register Foo.Sum
--- PASS: TestNewService (0.00s)
=== RUN   TestMethodType_Call
2020/12/07 12:10:47 rpc server: register Foo.Sum
--- PASS: TestMethodType_Call (0.00s)
=== CONT  TestClient_dialTimeout
=== CONT  TestClient_Call
2020/12/07 12:10:47 rpc server: register Bar.Timeout
=== RUN   TestClient_dialTimeout/timeout
=== RUN   TestClient_Call/client_timeout
=== RUN   TestClient_dialTimeout/0
=== RUN   TestClient_Call/server_handle_timeout
--- PASS: TestClient_dialTimeout (3.00s)
    --- PASS: TestClient_dialTimeout/timeout (1.00s)
    --- PASS: TestClient_dialTimeout/0 (2.00s)
# 在此处失去响应,需要手动中止程序或等待10m后程序崩溃退出

这种情况下,如果不进行中断,则会在 10 分钟后因创建过多 goroutine 而导致崩溃,出现这种问题的 bug 大概是每跑 5 次出现1 次.
我的代码运行环境为 Ubuntu-16.04 LTS, go 的版本为 version go1.15.4 linux/amd64
通过对问题代码进行定位,我发现出问题的地方是 client.go 的 Call 函数 中的 select 语句

func (client *Client) Call(ctx context.Context, serviceMethod string, args, reply interface{}) error {
	call := client.Go(serviceMethod, args, reply, make(chan *Call, 1))
	select {
	case <-ctx.Done():
		client.removeCall(call.Seq)
		return errors.New("rpc client: call failed: " + ctx.Err().Error())
	case call := <-call.Done:
		return call.Error
       }
}

在 select 语句中会出现 client.done() 未被执行的情况,这种情况下两个 case 都不满足,进而导致测试程序卡死 select 处。这个问题在 day7 处执行 go run main.go 时也有一定概率会出现,结果是导致 main.go 也卡死,陷入无响应状态

类似var _ PeerPicker = (*HTTPPool)(nil)这种设计目的是什么

首先感谢博主能写出这么好的文章,受益匪浅
然后就是在GeeCache中我看到有类似于
var _ PeerPicker = (*HTTPPool)(nil)

var _ PeerGetter = (*httpGetter)(nil)
请问这种设计的意义是什么,我把这一行代码去了也并没有看到报错,有点疑惑,向博主请教

一个疑惑,在第一部分的关于template的那节

请问,为什么func (group *RouterGroup) createStaticHandler里,需要自己启动fileServer.ServeHTTP(c.Writer, c.Req)呢?不是已经把这个handler绑定到了GET上吗?而之前的GET请求却不需要自己来启动ServeHTTP呢?

GeeRPC day1 bug

GeeRPC day1的代码会有概率阻塞在futex或者epoll_wait系统调用上, 不知道怎么修复, 是不是sleep时间不太够?

strace go run main.go

Ubuntu 18.04
Go 1.15.6

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.