背景

一个web服务如果能将自身变动和对外服务隔离对服务的稳定性和可用性是友好的,所以出现了graceful的东西,实现随不同,但原理大致相似,我用的一个具体的实现请看github.com/cgCodeLife/…,在测试中发现一个现象,在执行graceful的服务热升级的时候发现golang的client会偶现EOF, read connection reset by peer, connection idle close等现象,所以需要结合我自己的测试代码和现象分析下内部原因,为此有这篇文章以此作为公共讨论的地方希望能有人给出好的建议或指正我的问题,共同学习进步。

结论

web服务虽然能实现graceful的能力,但是并不是理想的,client会偶尔出现连接问题,无关乎并发量

测试环境

golang client
goang版本1.10
http协议 1.1
是否长连接 是/否 都尝试过
并发数 1,30个都尝试过
每个连接发送的次数1, 1000次 其中次数为1次的实验在client端未发现连接问题
请求方式 post十几字节的字符串
golang server
golang版本 1.10
响应数据 自己的进程号,7字节左右

问题分析

golang client代码
package mainimport ("net/http"log "github.com/sirupsen/logrus""io/ioutil""fmt""bytes""sync"
)func main() {var wg sync.WaitGroupvar count intvar rw sync.RWMutex
TEST:for i := 0; i < 30; i++ {wg.Add(1)go func () {defer wg.Done()tr := http.Transport{DisableKeepAlives: false}client := &http.Client{Transport: &tr}for i := 0; i < 1000; i++ {f, err := ioutil.ReadFile("data")if err != nil {fmt.Println("read file err", err)return}fmt.Println(len(f))reader := bytes.NewReader(f)rw.Lock()count += 1index := countrw.Unlock()resp, err := client.Post("http://0.0.0.0:8888", "application/x-www-form-urlencoded", reader)if err != nil {rw.RLock()currentCount := countrw.RUnlock()log.Fatal(err, index, currentCount)}defer resp.Body.Close()data, err := ioutil.ReadAll(resp.Body)if err != nil {log.Fatal(err)}log.Printf("data[%s]", string(data))}}()}wg.Wait()goto TEST
}复制代码

golang server代码

package mainimport (graceful "github.com/cgCodeLife/graceful2""net/http"log "github.com/sirupsen/logrus""io/ioutil""fmt""os""strconv"
)func main() {server := graceful.NewServer()handler := http.HandlerFunc(handle)server.Register("0.0.0.0:8888", handler)err := server.Run()if err != nil {log.Fatal(err)}
}func handle(w http.ResponseWriter, r *http.Request) {defer r.Body.Close()_, err := ioutil.ReadAll(r.Body)if err != nil {fmt.Println("read body error[%s] pid[%d]", err, os.Getpid())}w.Write([]byte(strconv.Itoa(os.Getpid())))
}复制代码

实验部分截图

1个连接请求1次并发是1的情况
1个连接请求1000次并发是1的情况
1个连接请求1次并发是30 (连接资源应该耗尽了,但是没有触发EOF, reset等连接问题)
1个连接请求1000次并发是30
这里简单描述的我用的graceful的原理,它是一个master-worker模式,master常驻,只处理信号和像worker发送terminate信号,worker负责web服务,在收到信号之后会进行shutdown操作,逻辑就这些。
看下shutdown的代码 src/net/http/server.go 2536行开始
// shutdownPollInterval is how often we poll for quiescence
// during Server.Shutdown. This is lower during tests, to
// speed up tests.
// Ideally we could find a solution that doesn't involve polling,
// but which also doesn't have a high runtime cost (and doesn't
// involve any contentious mutexes), but that is left as an
// exercise for the reader.
var shutdownPollInterval = 500 * time.Millisecond// Shutdown gracefully shuts down the server without interrupting any
// active connections. Shutdown works by first closing all open
// listeners, then closing all idle connections, and then waiting
// indefinitely for connections to return to idle and then shut down.
// If the provided context expires before the shutdown is complete,
// Shutdown returns the context's error, otherwise it returns any
// error returned from closing the Server's underlying Listener(s).
//
// When Shutdown is called, Serve, ListenAndServe, and
// ListenAndServeTLS immediately return ErrServerClosed. Make sure the
// program doesn't exit and waits instead for Shutdown to return.
//
// Shutdown does not attempt to close nor wait for hijacked
// connections such as WebSockets. The caller of Shutdown should
// separately notify such long-lived connections of shutdown and wait
// for them to close, if desired. See RegisterOnShutdown for a way to
// register shutdown notification functions.
func (srv *Server) Shutdown(ctx context.Context) error {atomic.AddInt32(&srv.inShutdown, 1)defer atomic.AddInt32(&srv.inShutdown, -1)srv.mu.Lock()lnerr := srv.closeListenersLocked()srv.closeDoneChanLocked()for _, f := range srv.onShutdown {go f()}srv.mu.Unlock()ticker := time.NewTicker(shutdownPollInterval)defer ticker.Stop()for {if srv.closeIdleConns() {return lnerr}select {case <-ctx.Done():return ctx.Err()case <-ticker.C:}}
}复制代码

shutdown主要做两件事情
1.终止listen
2.关闭所有空闲连接
空闲连接怎么来的呢,在request的时候就把conn设置为active,然后在当前请求处理完成之后设置成idle,所以,所以我理解的在一个连接上如果持续多次请求会比较容易出现shutdown扫描的时候这个request虽然被idle了,但是在close的同时收到了客户端发送过来的请求被reset,所以怀疑golang在close的时候是同时关闭socket fd的读写通道的(为此我单独针对shutdown做了一个小实验,juejin.im/post/5d033c…),所以这种情况会出现client连接问题。

验证

1连接持续发送包信息如下:888是服务端端口
很显然,服务端连接还没释放完服务就没了
1连接间隔1秒持续发送信息:
完整的四次挥手

解决方案

其实我理解这个问题如果靠server端达到完全避免的目的是不太合适的,因为发送与否的主动权在client上,所以这种情况需要client端配合处理网络问题。

转载于:https://juejin.im/post/5d0229506fb9a07ef562314c

golang http服务的graceful问题相关推荐

  1. 如何在golang http服务端程序中读取2次Request Body?(转)

    转自知乎:如何在golang http服务端程序中读取2次Request Body? - 知乎 提问: 在golang http服务端程序中,我想在真正处理Request Body之前将Body中的内 ...

  2. 一个人写一个集群:基于GRPC的golang微服务框架iogo(grpc/protobuf/etcd/freetoo/码客 卢益贵)

    一个人写一个集群:基于GRPC的golang微服务框架iogo keyword:iogo,golang,grpc,protobuf,etcd,zookeeper,microservice,distri ...

  3. go开源文件服务器框架,golang微服务框架go-zero系列-4:go-zero文件服务

    golang微服务框架go-zero系列-4:go-zero文件服务 go-zero本身支持文件服务,但是我们需要写相关的handler文件,本文目的在于 不写任何一个和文件相关的handler 如果 ...

  4. Golang 微服务教程

    本节对 gRPC 的使用浅尝辄止,更多可参考:gRPC 中 Client 与 Server 数据交互的 4 种模式 前言 系列概览 <Golang 微服务教程>分为 10 篇,总结微服务开 ...

  5. Golang微服务教程

    转自:https://segmentfault.com/a/1190000015135650?utm_campaign=studygolang.com&utm_medium=studygola ...

  6. Golang微服务开发实践

    github: github.com/yun-mu/Micr- 微服务概念学习:可参考 Nginx 的微服务文章 微服务最佳实践:可参考 微服务最佳实践 demo 简介 服务: consignment ...

  7. golang微服务框架对比_微服务里程碑,Golang与Spring Cloud Alibaba完美结合

    目前微服务架构仍是软件架构中最新的热门话题,虽然Golang是一门新的语言,但Golang的性能比python和java高出不少.既能承受程序使用运行的服务构建的繁重负载,又容易与GitHub集成,管 ...

  8. golang微服务框架对比_Go语言开发的微服务框架,你了解多少?

    Go语言开发的微服务框架 1.项目名称:Istio 项目简介:Istio是由Google.IBM和Lyft开源的微服务管理.保护和监控框架.使用istio可以很简单的创建具有负载均衡.服务间认证.监控 ...

  9. golang微服务框架对比_最强开源微服务框架,全网独家整理

    诞生于 2014 年的"微服务架构",其思想经由 Martin Fowler 阐述后,在近几年持续受到重视,理论与相关实践都不断发展,目前它已经成为了主流软件架构模式. 关于微服务 ...

  10. golang微服务框架go-zero系列-1:适合创业的golang微服务框架go-zero + 金光灿灿的gorm V2实践

    为什么使用go-zero 可以有第三个选择 golang圈子不大,微服务框架框架屈指可数:除了go-micro.go-kit,几乎没有其他选择.go-zero为此提供第三个可能. go-micro 对 ...

最新文章

  1. UITableView 添加长按手势UILongPressGestureRecognizer
  2. (一)Audio子系统之AudioRecord.getMinBufferSize
  3. Springmvc的helloworld实例
  4. Python读取Json字典写入Excel表格的方法
  5. Django之Ajax刷新记住用户名
  6. React Router入门指南
  7. Spark: sortBy和sortByKey函数详解
  8. 6月国产网络游戏审批信息公布 共计86款游戏过审
  9. linux 下运行 jar包 java.lang.ClassNotFoundException: 解决办法
  10. C# 中using的几个用途
  11. Spring Boot 2.0 整合 ES 5 文章内容搜索实战
  12. tracker服务器包含什么信息,tracker服务器
  13. python scipy拟合曲线optimize.curve_fit 50例
  14. centos mysql mariadb_centos7 mysql和mariadb的资料 - 菜鸟头头
  15. 二战企*查*查-企业-数-据爬虫
  16. 计算机网络前三章笔记
  17. win7 文件夹背景。安装使用主题后,变为不透明的经典界面。Win8.1Win10文件夹背景补丁与教程
  18. 机械革命 键盘灯 linux,机械革命x6Ti安装ubuntu(100%成功)
  19. 工具:语雀导出MarkDown文档后图片修复
  20. 分享45个android实例源码,很好很强大.收藏吧!!!

热门文章

  1. 谜底是计算机病毒的谜语,有关于安全的谜语及谜底答案解析|谜底是粽子的谜语...
  2. 【Codewars】Pick peaks
  3. 宋浩概率论与数理统计-第一章-笔记
  4. 概率论与数理统计 基本概念
  5. 英语单词默写本的制作
  6. 百度 android 市场占有率,2019百度 排行榜_2019安卓应用市场排行榜Top10
  7. Python 打新股,我建议你这么来操作!
  8. 从零学习Belief Propagation算法(二)
  9. 小工具--理财计算器
  10. 过年发生的,WinM7推出,MeeGo诞生,iPhone香肠