Golang百万级高并发实例

前言

感谢Handling 1 Million Requests per Minute with Go这篇文章给予的巨大启发。

基础

我们使用Go语言,基本上是因为他原生支持的高并发:Goroutine 和 Channel;
Go 的并发属于 CSP 并发模型的一种实现;
CSP 并发模型的核心概念是:“不要通过共享内存来通信,而应该通过通信来共享内存”。

简单用法

我一开始学习Go语言的时候,遇到大访问量的时候,会先创建一个带缓冲的channel,然后起一个Go协程来逐个读取channel中的数据并处理。
说他是并发是因为他没有占用主线程,而是另起了一个协程独自运行。但是这没有实现请求之间的并发。
特别注意:Go语言中的map不是并发安全的,要想实现并发安全,需要自己实现(如加锁),或者使用sync.Map。

package main
import (
    "fmt"
    "runtime"
    "time"
)
func main(){
//这里我们假设数据是int类型,缓存格式设为100
dataChan:=make(chan int,100)
go func(){
  for{
      select{
        case data:=<-dataChan:
                fmt.Println("data:",data)
                time.Sleep(1 * time.Second)//这里延迟是模拟处理数据的耗时
        }
    }
}()

//填充数据
for i:=0;i<100;i++{
  dataChan<-i
}

//这里循环打印查看协程个数
for {
        fmt.Println("runtime.NumGoroutine() :", runtime.NumGoroutine())
        time.Sleep(2 * time.Second)
    }
}

这里打印出来的协程个数时2,为什么? 因为main方法独占一个主协程,我们又起了一个协程,所以是两个。

实现百万级的并发

首先我们要抽象出几个概念:

Job:
    type Job interface {
        Do()
    }
 // 一个数据接口,所有的数据都要实现该接口,才能被传递进来
 //实现Job接口的一个数据实例,需要实现一个Do()方法,对数据的处理就在这个Do()方法中。

Job通道:
    这里有两个Job通道:
    1、WorkerPool的Job channel,用于调用者把具体的数据写入到这里,WorkerPool读取。
    2、Worker的Job channel,当WorkerPool读取到Job,并拿到可用的Worker的时候,会将Job实例写入该Worker的Job channel,用来直接执行Do()方法。

Worker:
    type Worker struct {
        JobQueue    chan Job   //Worker的Job通道
    }
    //每一个被初始化的worker都会在后期单独占用一个协程
    //初始化的时候会先把自己的JobQueue传递到Worker通道中,
    //然后阻塞读取自己的JobQueue,读到一个Job就执行Job对象的Do()方法。

工作池(WorkerPool):
        type WorkerPool struct {
            workerlen   int //WorkerPool中同时 存在Worker的个数
            JobQueue    chan Job // WorkerPool的Job通道
            WorkerQueue chan chan Job
        }
    //初始化时会按照传入的num,启动num个后台协程,然后循环读取Job通道里面的数据,
    //读到一个数据时,再获取一个可用的Worker,并将Job对象传递到该Worker的chan通道

整个过程中 每个Worker都会被运行在一个协程中,在整个WorkerPool中就会有num可空闲的Worker,当来一条数据的时候,就会在工作池中去一个空闲的Worker去执行该Job,当工作池中没有可用的worker时,就会阻塞等待一个空闲的worker。

这是一个粗糙最简单的版本,只是为了演示效果,具体使用需要根据实际情况加一些特殊的处理。

当数据无限多的时候func (wp *WorkerPool) Run() 会无限创建协程,这里需要做一些处理,这里是为了让所有的请求不等待,并且体现一下最大峰值时的协程数。具体因项目而异。

代码地址:https://github.com/wangzhen0625/gonote/tree/master/7goroutune
main.go

package main

import (
    "fmt"
    "runtime"
    "time"
)

type Score struct {
    Num int
}

func (s *Score) Do() {
    fmt.Println("num:", s.Num)
    time.Sleep(1 * 1 * time.Second)
}

func main() {
    num := 100 * 100 * 20
    // debug.SetMaxThreads(num + 1000) //设置最大线程数
    // 注册工作池,传入任务
    // 参数1 worker并发个数
    p := NewWorkerPool(num)
    p.Run()
    datanum := 100 * 100 * 100 * 100
    go func() {
        for i := 1; i <= datanum; i++ {
            sc := &Score{Num: i}
            p.JobQueue <- sc
        }
    }()

    for {
        fmt.Println("runtime.NumGoroutine() :", runtime.NumGoroutine())
        time.Sleep(2 * time.Second)
    }

}

job.go

package main

type Job interface {
    Do()
}

worker.go

package main

type Worker struct {
    JobQueue chan Job
}

func NewWorker() Worker {
    return Worker{JobQueue: make(chan Job)}
}
func (w Worker) Run(wq chan chan Job) {
    go func() {
        for {
            wq <- w.JobQueue
            select {
            case job := <-w.JobQueue:
                job.Do()
            }
        }
    }()
}

workerpool.go

package main

import "fmt"

type WorkerPool struct {
    workerlen   int
    JobQueue    chan Job
    WorkerQueue chan chan Job
}

func NewWorkerPool(workerlen int) *WorkerPool {
    return &WorkerPool{
        workerlen:   workerlen,
        JobQueue:    make(chan Job),
        WorkerQueue: make(chan chan Job, workerlen),
    }
}
func (wp *WorkerPool) Run() {
    fmt.Println("初始化worker")
    //初始化worker
    for i := 0; i < wp.workerlen; i++ {
        worker := NewWorker()
        worker.Run(wp.WorkerQueue)
    }
    // 循环获取可用的worker,往worker中写job
    go func() {
        for {
            select {
            case job := <-wp.JobQueue:
                worker := <-wp.WorkerQueue
                worker <- job
            }
        }
    }()
}

原文地址:http://blog.51cto.com/11140372/2342942

时间: 2024-09-29 23:52:27

Golang百万级高并发实例的相关文章

Golang亿级高并发实例(代码可直接使用)

可以直接拿去当成一个库来用 举例 package main import "your/path/to/.../Concurrence" //定义一个实现Job接口的数据 type Score struct { Num int } //定义对数据的处理 func (s *Score) Do() { fmt.Println("num:", s.Num) time.Sleep(1 * 1 * time.Second) } func main() { num := 100

百万级高并发WebRTC流媒体服务器设计与开发

第1章 课程导学与准备工作本章主要介绍为何要学习WebRTC流媒体服务器开发,以及本门课能为我们带来哪些收获.之后会为大家介绍本课程内容具体安排,最后给出如何学好这门课程的一些学习建议.希望大家都能通过这门课程,学有所成,学有所归. 第2章 C++语言基础回顾[已掌握,可略过]为了便于大家更好的学习流媒体服务器的开发,本章将带大家对WebRTC服务器开发中用到的C++基础知识进行回顾梳理,如类的定义与使用,继承,多态,名存空间等相关知识. 第3章 服务器基础编程本章将带你学习最基础的服务器开发,

百万级高并发WebRTC流媒体服务器设计与开发教程云

百万级高并发WebRTC流媒体服务器设计与开发 资源获取链接:点击获取完整教程 百万级高并发WebRTC流媒体服务器设计与开发 5G时代音视频为王,随着实时音视频应用的爆发,来自Google 的WebRTC成为了人们关注的焦点,但很多人却不知道如何使用WebRTC实现多人实时互动,本课就将围绕与浏览器互通.级联.可扩展等6大痛点手把手带你学习大负载.高并发.高性能 WebRTC 流媒体服务器的设计与开发,揭秘万人互动直播背后的深层奥秘,打造可负载百万用户量的企业级的流媒体服务器 播答题的核心需求

百万级高并发MongoDB集群性能数十倍提升优化实践(上篇)

1. 背景 线上某集群峰值TPS超过100万/秒左右(主要为写流量,读流量很低),峰值tps几乎已经到达集群上限,同时平均时延也超过100ms,随着读写流量的进一步增加,时延抖动严重影响业务可用性.该集群采用mongodb天然的分片模式架构,数据均衡的分布于各个分片中,添加片键启用分片功能后实现完美的负载均衡.集群每个节点流量监控如下图所示: 从上图可以看出集群流量比较大,峰值已经突破120万/秒,其中delete过期删除的流量不算在总流量里面(delete由主触发删除,但是主上面不会显示,只会

JAVA架构师大型分布式高并发电商项目实战,性能优化,集群,亿级高并发,web安全,缓存架构实战

现任58到家技术委员会主席,高级技术总监,负责企业,支付,营销.客户关系等多个后端业务部门.本质,技术人一枚.互联网架构技术专家,"架构师之路"公众号作者.曾任百度高级工程师,58同城高级架构师,58同城技术委员会主席,58同城C2C技术部负责人. 内容介绍 1.大数据量时,数据库架构设计原则 2.数据库水平切分架构设计方向 3.用户中心,帖子中心,好友中心,订单中心水平切分架构实践 下面是58沈剑老师的演讲实录 大家好,我是58沈剑,架构师之路的小编,后端程序员一枚,平时比较喜欢写写

MySQL面试必考知识点:揭秘亿级高并发数据库调优与最佳实践法则

做业务,要懂基本的SQL语句: 做性能优化,要懂索引,懂引擎: 做分库分表,要懂主从,懂读写分离... 数据库的使用,是开发人员的基本功,对它掌握越清晰越深入,你能做的事情就越多. 今天我们用10分钟,重点梳理一遍以下几方面: 数据库知识点汇总: 数据库事务特性和隔离级别: 详解关系型数据库.索引与锁机制: 数据库调优与最佳实践: 面试考察点及加分项. 知识点汇总 一.数据库的不同类型 1.常用的关系型数据库 Oracle:功能强大,主要缺点就是贵 MySQL:互联网行业中最流行的数据库,这不仅

[转]10分钟梳理MySQL知识点:揭秘亿级高并发数据库调优与最佳实践法则

转:https://mp.weixin.qq.com/s/RYIiHAHHStIMftQT6lQSgA 做业务,要懂基本的SQL语句: 做性能优化,要懂索引,懂引擎: 做分库分表,要懂主从,懂读写分离... 数据库的使用,是开发人员的基本功,对它掌握越清晰越深入,你能做的事情就越多. 今天我们用10分钟,重点梳理一遍以下几方面: 数据库知识点汇总: 数据库事务特性和隔离级别: 详解关系型数据库.索引与锁机制: 数据库调优与最佳实践: 面试考察点及加分项. 一.数据库的不同类型 1.常用的关系型数

redis+php+mysql处理高并发实例

一.实验环境ubuntu.php.apache或nginx.mysql二.利用Redis锁解决高并发问题,需求现在有一个接口可能会出现并发量比较大的情况,这个接口使用php写的,做的功能是接收 用户的GET请求中的name字段,然后将这个字段存到mysql中,现在先将数据放到redis的队列中,然后让redis定时将这些数据转移到mysql中.二.实现步骤1.新建数据库test及 数据表test,建表语句如下 CREATE TABLE `test` ( `name` varchar(255) D

千万级高并发负载均衡软件HAPROXY

一.HAProxy 基于硬件的负载均衡设备:f5,big-ip等 基于软件的负载均衡产品:haproxy,lvs,nginx等 在软件的负载均衡产品中,分为基于系统的软负载实现和基于第三方软件的软负载实现,LVS是基于系统实现的一种软负载.HA proxy是基于第三方应用实现的软负载均衡 1.haproxy简介 haproxy是一个开源的,高性能的,基于tcp第四层和http第七层应用的负载均衡软件 优点:可靠性和稳定性非常好 最高可以同时维护40000-50000个并发连接.单位时间内处理最大