golang 为什么能做到高并发

goroutine是go并行的关键,goroutine说到底就是携程,但是他比线程更小,几十个goroutine可能体现在底层就是五六个线程,Go语言内部帮你实现了这些goroutine之间的内存共享。执行goroutine只需极少的栈内存(大概是4~5KB),当然会根据相应的数据伸缩。也正因为如此,可同时运行成千上万个并发任务。goroutine比thread更易用、更高效、更轻便。

一些高并发的处理方案基本都是使用协程,openresty也是利用lua语言的协程做到了高并发的处理能力,PHP的高性能框架Swoole目前也在使用PHP的协程。
协程更轻量,占用内存更小,这是它能做到高并发的前提。

go web开发中怎么做到高并发的能力

学习go的HTTP代码。先创建一个简单的web服务。

package main

import (
"fmt"
"log"
"net/http"
) func response(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello world!") //这个写入到w的是输出到客户端的
} func main() {
http.HandleFunc("/", response)
err := http.ListenAndServe(":9000", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}

然后启动

这样简单的一个WEB服务就搭建起来。接下来我们一步一步理解这个Web服务是怎么运行的,怎么做到高并发的。
我们顺着http.HandleFunc("/", response)方法顺着代码一直往上看。

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux type ServeMux struct {
mu sync.RWMutex//读写锁。并发处理需要的锁
m map[string]muxEntry//路由规则map。一个规则一个muxEntry
hosts bool //规则中是否带有host信息
}
一个路由规则字符串,对应一个handler处理方法。
type muxEntry struct {
h Handler
pattern string
}

上面是DefaultServeMux的定义和说明。我们看到ServeMux结构体,里面有个读写锁,处理并发使用。muxEntry结构体,里面有handler处理方法和路由字符串。
接下来我们看下,http.HandleFunc函数,也就是DefaultServeMux.HandleFunc做了什么事。我们先看mux.Handle第二个参数HandlerFunc(handler)

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
mux.Handle(pattern, HandlerFunc(handler))
}
type Handler interface {
ServeHTTP(ResponseWriter, *Request) // 路由实现器
}
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}

我们看到,我们传递的自定义的response方法被强制转化成了HandlerFunc类型,所以我们传递的response方法就默认实现了ServeHTTP方法的。

我们接着看mux.Handle第一个参数。

func (mux *ServeMux) Handle(pattern string, handler Handler) {
mux.mu.Lock()
defer mux.mu.Unlock() if pattern == "" {
panic("http: invalid pattern")
}
if handler == nil {
panic("http: nil handler")
}
if _, exist := mux.m[pattern]; exist {
panic("http: multiple registrations for " + pattern)
} if mux.m == nil {
mux.m = make(map[string]muxEntry)
}
mux.m[pattern] = muxEntry{h: handler, pattern: pattern} if pattern[] != '/' {
mux.hosts = true
}
}

将路由字符串和处理的handler函数存储到ServeMux.m 的map表里面,map里面的muxEntry结构体,上面介绍了,一个路由对应一个handler处理方法。
接下来我们看看,http.ListenAndServe(":9000", nil)做了什么

func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
} func (srv *Server) ListenAndServe() error {
addr := srv.Addr
if addr == "" {
addr = ":http"
}
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}

net.Listen("tcp", addr),就是使用端口addr用TCP协议搭建了一个服务。tcpKeepAliveListener就是监控addr这个端口。
接下来就是关键代码,HTTP的处理过程

func (srv *Server) Serve(l net.Listener) error {
defer l.Close()
if fn := testHookServerServe; fn != nil {
fn(srv, l)
}
var tempDelay time.Duration // how long to sleep on accept failure if err := srv.setupHTTP2_Serve(); err != nil {
return err
} srv.trackListener(l, true)
defer srv.trackListener(l, false) baseCtx := context.Background() // base is always background, per Issue 16220
ctx := context.WithValue(baseCtx, ServerContextKey, srv)
for {
rw, e := l.Accept()
if e != nil {
select {
case <-srv.getDoneChan():
return ErrServerClosed
default:
}
if ne, ok := e.(net.Error); ok && ne.Temporary() {
if tempDelay == {
tempDelay = * time.Millisecond
} else {
tempDelay *=
}
if max := * time.Second; tempDelay > max {
tempDelay = max
}
srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
time.Sleep(tempDelay)
continue
}
return e
}
tempDelay =
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(ctx)
}
}

for里面l.Accept()接受TCP的连接请求,c := srv.newConn(rw)创建一个Conn,Conn里面保存了该次请求的信息(srv,rw)。启动goroutine,把请求的参数传递给c.serve,让goroutine去执行。
这个就是GO高并发最关键的点。每一个请求都是一个单独的goroutine去执行。
那么前面设置的路由是在哪里匹配的?是在c.serverde的c.readRequest(ctx)里面分析出URI METHOD等,执行serverHandler{c.server}.ServeHTTP(w, w.req)做的。看下代码

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler
if handler == nil {
handler = DefaultServeMux
}
if req.RequestURI == "*" && req.Method == "OPTIONS" {
handler = globalOptionsHandler{}
}
handler.ServeHTTP(rw, req)
}

handler为空,就我们刚开始项目中的ListenAndServe第二个参数。我们是nil,所以就走DefaultServeMux,我们知道开始路由我们就设置的是DefaultServeMux,所以在DefaultServeMux里面我一定可以找到请求的路由对应的handler,然后执行ServeHTTP。前边已经介绍过,我们的reponse方法为什么具有ServeHTTP的功能。流程大概就是这样的。

我们看下流程图

结语

我们基本已经学习忘了GO 的HTTP的整个工作原理,了解到了它为什么在WEB开发中可以做到高并发,这些也只是GO的冰山一角,还有Redis MySQL的连接池。要熟悉这门语言还是多写多看,才能掌握好它。灵活熟练的使用。

原文地址:www.cnblogs.com/feixiangmanon/p/10504081.html

golang高并发的更多相关文章

  1. golang高并发的理解

    前言 GO语言在WEB开发领域中的使用越来越广泛,Hired 发布的<2019 软件工程师状态>报告中指出,具有 Go 经验的候选人是迄今为止最具吸引力的.平均每位求职者会收到9 份面试邀 ...

  2. Golang适合高并发场景的原因分析

    http://blog.csdn.NET/ghj1976/article/details/27996095 典型的两个现实案例: 我们先看两个用Go做消息推送的案例实际处理能力. 360消息推送的数据 ...

  3. [golang]Golang实现高并发的调度模型---MPG模式

    Golang实现高并发的调度模型---MPG模式 传统的并发形式:多线程共享内存,这也是Java.C#或者C++等语言中的多线程开发的常规方法,其实golang语言也支持这种传统模式,另外一种是Go语 ...

  4. Golang语言快速上手到综合实战高并发聊天室

    需要的联系我:QQ:1844912514 Go是Google开发的一种编译型,可并行化,并具有垃圾回收功能的编程语言.2015,Go迎来了全迸发的一年.时隔一年,回头再看,Go已跻身主流编程语言行列. ...

  5. Surfer 高并发双核无头浏览器 (Golang语言)

    Surfer   A high level concurrency downloader. surfer是一款Go语言编写的高并发爬虫下载器,拥有surf与phantom两种下载内核. 支持固定Use ...

  6. 【GoLang】并发小结

    006.并发 1 概念 1.1 goroutine是Go并行设计的核心,goroutine的本质是轻量级线程 1.2 golang的runtime实现了对轻量级线程即goroutine的智能调度管理 ...

  7. Microsoft Orleans构建高并发、分布式的大型应用程序框架

    Microsoft Orleans 在.net用简单方法构建高并发.分布式的大型应用程序框架. 原文:http://dotnet.github.io/orleans/ 在线文档:http://dotn ...

  8. go---weichart个人对Golang中并发理解

    个人觉得goroutine是Go并行设计的核心,goroutine是协程,但比线程占用更少.golang对并发的处理采用了协程的技术.golang的goroutine就是协程的实现. 十几个gorou ...

  9. 为一个支持GPRS的硬件设备搭建一台高并发服务器用什么开发比较容易?

    高并发服务器开发,硬件socket发送数据至服务器,服务器对数据进行判断,需要实现心跳以保持长连接. 同时还要接收另外一台服务器的消支付成功消息,接收到消息后控制硬件执行操作. 查了一些资料,java ...

随机推荐

  1. K8S知识点总结

    一.K8S介绍: Kubernetes(k8s)是Google开源的容器集群管理系统.在Docker技术的基础上,为容器化的应用提供部署运行.资源调度.服务发现和动态伸缩等一系列完整功能,提高了大规模 ...

  2. 在vue中让某个组件重新渲染的笨方法

    在vue中,推崇的是数据驱动也就是数据更新进而使组件得以重新渲染:在某些情况下,我们想要在数据不改变的情况下,重新渲染组件:我遇到的一个情况是:同一个页面,两个tab页分别为tab1和tab2,公用了 ...

  3. 前端web worker实践与总结

    参考链接:https://www.jianshu.com/p/97f6144dfddf

  4. addRoutes进行权限控制

    用addRoutes实现动态路由:https://www.jianshu.com/p/0bea4a1b0350 详解基于vue,vue-router, vuex以及addRoutes进行权限控制:ht ...

  5. Go语言实例化结构体——为结构体分配内存并初始化

    转自: http://c.biancheng.net/view/66.html 结构体的定义只是一种内存布局的描述,只有当结构体实例化时,才会真正地分配内存.因此必须在定义结构体并实例化后才能使用结构 ...

  6. python中的with语句

    https://www.ibm.com/developerworks/cn/opensource/os-cn-pythonwith/index.html

  7. nginx.conf and dockerfile带颜色

    wget http://www.vim.org/scripts/download_script.php?src_id=14376 -O nginx.vim mv nginx.vim /usr/shar ...

  8. maraidb忘记数据密码

    一.概述 服务器上安装了maraidb 数据库,但是很久未使用过它,需要使用时,忘记了密码, 此时可以给它重新设置密码. 二.操作 修改密码 修改 /etc/my.cnf,修改下图红色区域位置,修改成 ...

  9. 解决FileInputStream 读取文件中文乱码问题(转)

    当Java中使用 FileInputStream 读取txt等文档时,中文会产生乱码,解决方法如下: try { fis = new FileInputStream(file); InputStrea ...

  10. golang(7):文件读写 & json & 错误处理

    终端读写 操作终端相关文件句柄常量 os.Stdin // 标准输入 os.Stdout // 标准输出 (输出到终端) os.Stderr // 标准错误输出 (输出到终端) fmt 常见用法 fm ...