go大go 并发框架用什么框架

Scala与Golang的并发实现对比----好问
Scala与Golang的并发实现对比
并发语言俨然是应大规模应用架构的需要而提出,有其现实所需。前后了解了Scala和Golang,深深体会到现代并发语言与旧有的Java、C++等语言在风格及理念上的巨大差异。本文主要针对Scala和Golang这两个我喜爱的并发语言在并发特性上的不同实现,做个比较和阐述,以进一步加深理解。
一. Scala与Golang的并发实现思路
Scala语言并发设计采用Actor模型,借鉴了Erlang的Actor实现,并且在Scala 2.10之后,Scala采用的是Akka Actor模型库。Actor模型主要特征如下:“一切皆是参与者”,且各个actor间是独立的;发送者与已发送消息间解耦,这是Actor模型显著特点,据此实现异步通信;actor是封装状态和行为的对象,通过消息交换进行相互通信,交换的消息存放在接收方的邮箱中;actor可以有父子关系,父actor可以监管子actor,子actor唯一的监管者就是父actor; 一个actor就是一个容器,它包含了状态、行为、一个邮箱(邮箱用来接受消息)、子actor和一个监管策略;
Go语言也能够实现传统的共享内存的通信方式,但Go更提倡“以通信来共享内存,而非以共享内存来通信”。Go的并发通信方式借鉴CSP(Communicating Sequential Process)模型,其主要特征如下:goroutine(协程,Go的轻量级线程)是Go的轻量级线程管理机制,用“go”启动一个goroutine, 如果当前线程阻塞则分配一个空闲线程,如果没有空闲线程,则新建一个线程;通过管道(channel)来存放消息,channel在goroutine之间传递消息;比如通过读取channel里的消息(通俗点说好比一个个“值”),你能够明白某个goroutine里的任务完成以否;Go给channel做了增强,可带缓存。Scala与Go在并发通信模型实现上的主要差异如下:actor是异步的,因为发送者与已发送消息间实现了解耦;而channel则是某种意义上的同步,比如channel的读写是有关系的,期间会依赖对方来决定是否阻塞自己;actor是一个容器,使用actorOf来创建Actor实例时,也就意味着需指定具体Actor实例,即指定哪个actor在执行任务,该actor必然要有“身份”标识,否则怎么指定呢?!而channel通常是匿名的, 任务放进channel之后你不用关心是哪个channel在执行任务;二. 实例说明
我们来看一个例子:对一组连续序列(1-10000)的整数值进行累加,分别观察Scala与Go环境下单线程与多线程效率,一方面了解并发效率的提升;一方面也能够对比Scala与Go并发实现的差异 ── 这才是本文的重点。具体要求如下:
对1 - 10000的整数进行累加,在并发条件下,我们将1 - 10000平均划分为四部分,启动四个线程进行并发计算,之后将四个线程的运行结果相加得出最终的累加统计值。为了更明显地观察到时间上的差异性,在每部分的每次计算过程中,我们添加一个3000000次的空循环:)三. Scala实现
以下先列出Scala Akka Actor并发实现的完整示例代码:
// Akka并发计算实例
import akka.actor.Actor
import akka.actor.Props
import akka.actor.ActorSystem
import akka.routing.RoundRobinPool
// 定义一个case类
sealed trait SumTrait
case class Result(value: Int) extends SumTrait
// 计算用的Actor
class SumActor extends Actor {
val RANGE = 10000
def calculate(start: Int, end: Int, flag : String): Int = {
var cal = 0
for (i &- (start to end)) {
for (j &- 1 to 3000000) {}
println("flag : " + flag + ".")
return cal
def receive = {
case value: Int =&
sender ! Result(calculate((RANGE / 4) * (value - 1) + 1, (RANGE / 4) * value, value.toString))
case _ =& println("未知 in SumActor...")
// 打印结果用的Actor
class PrintActor extends Actor {
def receive = {
case (sum: Int, startTime: Long) =&
println("总数为:" + sum + ";所花时间为:"
+ (System.nanoTime() - startTime)/.0 + "秒。")
case _ =& println("未知 in PrintActor...")
// 主actor,发送计算指令给SumActor,发送打印指令给PrintActor
class MasterActor extends Actor {
var sum = 0
var count = 0
var startTime: Long = 0
// 声明Actor实例,nrOfInstances是pool里所启routee(SumActor)的数量,
// 这里用4个SumActor来同时计算,很Powerful。
val sumActor
= context.actorOf(Props[SumActor]
.withRouter(RoundRobinPool(nrOfInstances = 4)), name = "sumActor")
val printActor = context.actorOf(Props[PrintActor], name = "printActor")
def receive = {
case "calculate..." =&
startTime = System.nanoTime()
for (i &- 1 to 4) sumActor ! i
case Result(value) =&
sum += value
count += 1
if (count == 4) {
printActor ! (sum, startTime)
context.stop(self)
case _ =& println("未知 in MasterActor...")
object Sum {
def main(args: Array[String]): Unit = {
var sum = 0
val system = ActorSystem("MasterActorSystem")
val masterActor = system.actorOf(Props[MasterActor], name = "masterActor")
masterActor ! "calculate..."
Thread.sleep(5000)
system.shutdown()
在这里我们设计了3个Actor实例,如下图所示:在这里,我们一共定义了 三个Actor实例(actor),MasterActor、SumActor和PrintActor,其中,前者是后两者的父亲actor,如前文Scala的Actor模型特征里提到的:“actor可以有父子关系,父actor可以监管子actor,子actor唯一的监管者就是父actor”。
我们的主程序通过向MasterActor发送“calculate...”指令,启动整个计算过程,嗯哼,好戏开始登场了:)
注意以下代码:
val sumActor
= context.actorOf(Props[SumActor]
.withRouter(RoundRobinPool(nrOfInstances = 4)), name = "sumActor")
这里的设置将会在线程池里初始化称为“routee”的子actor(这里是SumActor),数量为4,也就是我们需要4个SumActor实例参与并发计算。这一步很关键。
然后,在接受消息的模式匹配中,通过以下代码启动计算actor:
for (i &- 1 to 4) sumActor ! i
在SumActor中,每个计算线程都会调用calculate方法,该方法将处理分段的整数累加,并返回分段累加值给父actor MasterActor,我们特地通过case类实现MasterActor接受消息中的一个模式匹配功能(case Result(value) =&...),可以发现,模式匹配在Scala并发功能实现中的地位非常重要,并大大提升了开发人员的开发效率。在这里,我们获取了4个并发过程返回的分段累加值,MasterActor会计算最终的累加值。如果4个并发过程全部完成,就调用PrintActor实例打印结果和所花时间。
在整个运算过程中,我们很容易理解发送者与已发送消息间的解耦特征,发送者和接受者各种关心自己要处理的任务即可,比如状态和行为处理、发送的时机与内容、接收消息的时机与内容等。当然,actor确实是一个“容器”,且“五脏俱全”:我们用类来封装,里面也封装了必须的逻辑方法。
Scala Akka的并发实现,给我的感觉是设计才是关键,将各个actor的功能及关联关系表述清楚,剩余的代码实现就非常容易,这正是Scala、Akka的魅力体现,在底层帮我们做了大量工作!
在这里的PrintActor实际上并无太大存在意义,因为它并不实现并发功能。实现它主要是为了演示actor间的消息传递与控制。
再来看看单线程的计算运行模式:
val RANGE = 10000
var cal = 0
val startTime = System.nanoTime()
for (i &- (1 to RANGE)) {
for (j &- 1 to 3000000) {}
val endTime = System.nanoTime()
并发与单线程两种模式的效率在后面一块说,暂且按下不表。四. Go语言实现
仍然先列出Go语言实现的并发功能完整代码:
// Go并发计算实例
package main
type Sum []int
func (s Sum) Calculate(count, start, end int, flag string, ch chan int) {
for i := start; i &= end; i++ {
for j := 1; j &= 3000000; j++ {
s[count] = cal
fmt.Println("flag :", flag, ".")
ch &- count
func (s Sum) LetsGo() {
// runtime.NumCPU()可以获取CPU核数,我的环境为4核,所以这里就简单起见直接设为4了
const NCPU = 4
const RANGE = 10000
var ch = make(chan int)
runtime.GOMAXPROCS(NCPU)
for i := 0; i & NCPU; i++ {
go s.Calculate(i, (RANGE/NCPU)*i+1, (RANGE/NCPU)*(i+1), strconv.Itoa(i+1), ch)
for i := 0; i & NCPU; i++ {
func main() {
var s Sum = make([]int, 4, 4)
var sum int = 0
var startTime = time.Now()
s.LetsGo()
for _, v := range s {
fmt.Println("总数为:", sum, ";所花时间为:",
(time.Now().Sub(startTime)), "秒。")
Go语言的实现与之前的Scala实现风格完全不一样,其通过“go”关键字实现的goroutine协程工作方式,结合channel,实现并发功能。goroutine和channel是Go语言非常强大的两个招式,简约而不简单。在这里,我们的并发实现模型如下图所示:由上可知,Go语言的并发魔力来源于goroutine和channel。我们定义了一个Sum类型(插一句:Go语言的类型系统设计得也非常特别,这是别的主题了,:)),它有两个方法:LetsGo()和Calculate,LetsGo()首先创建一个计数用的channel,随后发起4个并发计算的协程。每个计算协程调用Calculate()进行分段计算(并会传入channel),Calculate()方法的最后,在分段计算完成时,都会往channel里塞一个计数标志:
ch &- count
总有某个协程抢先运行到此处,那么该协程对应的计数标志就塞进了channel,在channel里的计数标志未被读取之前,其他协程在处理完分段计算的业务逻辑之后,其他协程的计数标志是无法塞进channel里的,其他协程只能等待,因为channel在之前被塞进一个计数标志之后,标志一直未被读取出来,程序阻塞了。再看看以下代码:
for i := 0; i & NCPU; i++ {
在这里,从channel依次取出协程里塞进的计数标志。每取出了一个标志,则意味着该标志对应的协程结束使命,下一个协程在判断channel为空之后,会将它的计数标志塞进channel。如此循环,直至channel里的计数标志全被取出,则所有的协程都处理完毕了。另外,如果读取的channel里没东西了还继续读取它会怎样?那么,程序也会阻塞,直至有东西可读。
对于channel的写入、等待和读取,简单形象地用下图描述:这里为了演示方便,且本例中的协程和业务逻辑也不至于会造成协程僵死或locked,因此未考虑协程永久等待的处理,如果要处理超时,可以这么考虑:
case &-ch: ...
case &-time.After(3 * time.Second): ...
select机制也是Go语言并发处理中的强大武器,由于与本主题关系不大,故不表。但可以看出,Go语言有Unix和C的深深烙印,select、channel概念就是很好的例证。
在所有的分段计算结束后,就可以计算总的累加值了:
for _, v := range s {
这段代码从Sum类型实例中获取分段累加值,最后计算出总的累加统计值。
Go中的channel是可以带缓存的,在缓存未被填满之前,都可以写入。本例中未使用带缓存的channel,虽然这样做在理论上可以节省写入channel时的等待时间,但在这里可以忽略,大型应用中就要慎重对待了。
来看看单线程的计算运行模式:
for i := start; i &= end; i++ {
for j := 1; j &= 3000000; j++ {
五. 对Scala与Go的感知
先来看看运行效率。我的操作系统是Windows 8.1 64位,分别用以下命令编译及运行Scala和Go程序并发程序:
scalac -cp lib\akka-actor_2.11-2.3.4.jar Sum.scala
go build Sum.go
具体运行时间如下所列:
Scala:7.秒(单线程模式),3.秒(并发模式)
Golang 12.987232秒(单线程模式),7.1636263秒(并发模式)
从上可知,Scala与Go语言的并发实现都比单线程实现快了45%左右,这个数据还是比较可观的。而Golang并发却比Scala并发慢了不少,事实的确如此吗?我在另一台比较旧的32位操作系统机器上运行,Scala的并发足足花了近300秒,而Go语言并发差不多是20秒。因此,拿Scala和Go的并发效率来对比,应该是没什么意义的,其间要受到各自内部实现、类型系统、内存使用机制、并发模式、并发规模以及硬件支持等等复杂因素的影响。如果一定要对两者进行比较,则肯定会引发口水战。模式上的差异
如果前面讲述“Scala与Golang的并发实现思路”时,理解起来还比较抽象,但经过上面的示例说明与比较,相信感知会比较具体了:Akka的actor是解耦的、相对独立的,定义好各个actor间如何沟通,剩下的东西就尽管交给它们处理好了,它们自会按既定方式各司其职,而且每个actor“麻雀虽小五脏俱全”,这也是其解耦性做得好的必然基础。Go语言则独辟蹊径,通过“go”魔法和Unix风格的channel,以更轻量级的协程方式来处理并发,虽说是更轻量,但你仍得花点心思关注下channel的状态,别一不小心阻塞了,特别是channel多了、复杂了,并且其中包含了业务处理所需数据、而不仅仅只是计数标志的情况下;Akka的Actor实现是库的形式,其也能应用于Java语言。Go语言内嵌了协程的并发实现;Akka基于JVM,实现模式是面向对象,天然讲究抽象与封装,虽然可以穿插混合应用函数式风格。而Go语言显然体现了命令式语言的风格,在需要考虑封装性的时候,需要开发者多着墨。是Scala还是Go?
据说Go语言中轻量级的协程可以轻易启动数十上百万个,这对Scala来说当然是有压力的。但相较而言,Go语言的普及及应用程度尚远不及Java生态,我也希望更多的应用能够实践Go语言。此外,从代码简洁程度来看,Go语言应该会更简洁些吧。
在你了解了Akka之后,再回过头来看看Java与它的concurrent,就会有一种弱爆了的感觉,动不动就阻塞、同步。因此,如果是Java平台上的选择,不要说Akka就是很重要的考量指标了。
不得不提的一点是,不同模式有其适用的业务和环境,因此,选择Scala还是Go语言来实现功能,这必须有赖于现实业务与环境的需求──是Scala还是Go?这永远是个问题。六. 结束语
并发实现及场景是复杂的,比如远程调用、异常处理以及选择恰当的并发模式等。需要不断深入学习与实践,才能对并发技能运用自如。希望通过本文的阐述,能够让你了解到一些Scala与Golang的并发实现思路。
文章被以下专栏收录
我是码农,爱读书、爱太极。
那种情怀──论技术人的读书、学习与氛围
我喜欢阅读技术书籍、掌握感兴趣的技术,过程中,也是有不少磕磕碰碰。而多年养成的积淀习惯,觉得应该对自己的读书与学习感触做个梳理。这篇…
简简单单搞掂恼人的Laravel 5安装
想折腾下Laravel 5了。Laravel是这世界上最好且没有之一的语言──PHP──的众多框架中的一个,是我比较感兴趣的PHP Web Framework。但是安装…
碎拍年代史(1992 - 2005)
写在前面:这篇文章是我2006年初完成的,刚好2006年也算碎拍(Breaks)类音乐开始没落的分水岭。十年后翻出来这一篇文章,感慨一下这个在英国…
· 1 个月前 ·
“我是心理治疗师,我未婚夫得了抑郁症”|KY访谈:身为抑郁症患者的恋人
之前我们发起了一次“抑郁症伴侣”的访谈对象招募,得到了许多KY小伙伴的支持,在此想对大家表示感谢。今天我们邀请到了诸多参与者中的一位—…
· 1 个月前 ·
没有更多推荐了,go语言 beego框架关于并发? - 知乎有问题,上知乎。知乎作为中文互联网最大的知识分享平台,以「知识连接一切」为愿景,致力于构建一个人人都可以便捷接入的知识分享网络,让人们便捷地与世界分享知识、经验和见解,发现更大的世界。1被浏览279分享邀请回答暂时还没有回答,开始写第一个回答Go语言构建高并发分布式系统实践(下)
接上一篇:《Go语言构建高并发分布式系统实践(上)》
经验一:Go语言程序开发需要找到一种平衡,既利用协程带来的便利性又做适当集中化处理。当每一次请求都变成一个协程,那在每个协程之内是否有必要再去开一些协程解耦逻辑,这时使用任务池集中合并请求、连接池
Pipeline利用全双工特性提高QPS。
首先要改造通信库,在程序里直接调用一个I/O操作注册执行,不能用短连接。因为对系统性能参数做过优化,正常通讯的时候约10万个端口可用。虽然短连接通信本身没问题,但短连接会创建很多对象(编码Buffer、解码Buffer、服务端的编码Buffer、
Request对象、Response对象、服务端的Request对象、Response对象)。短连接还用了开源的RPC,用各种Buffer都会出现问题。
通信库做一版的迭代,第二版则用了一些值,相当于表面上阻塞的调用了I/O,但实际从连接池拿出一个请求Request,供服务端享用,然后拿到Response再把连接放回去。这样做很多资源(包括Buffer、Request、Response,Sever端、Client端)连接池可以复用。对所有对象做内存复用,但它实际是在线的,所以拿出一个连接往里面写数据等服务端响应,响应后再读取,这时服务端响应时间决定连接的占用时间。第三版要写一个Pipeline操作,Pipeline会带来一些额外的开销,这里的Pipeline指连接是全双工复用的,任何人都可以随时往里写,请求之后阻塞在相关通道上面,由底层去分配一个连接,最后这个连接释放留给其它人去写。整个可以用TCP的全双工特性把QPS跑满,经过集中化处理,RPC库会达到较好的效应,创业公司可以选择GRPC。对于像360消息推送的系统,如果不能控制每个环节就会出问题。如果代码不自己写,别人的代码再简单用起来也会非常困难,如用RPC判断错误类型、调整错误类型这种最简单的情况,返回的Error是个字符串,因此要分析到底是编码问题、网络问题,还是对波返回一个错误信息需要处理,这时业务逻辑层要对RPC做一个字符串的判断。
QPS在RPC上达到较高性能,其实还可以优化,在网络连接上,编解码的难度取决于对业务的需求。整个RPC库能够提高的效率达到瓶颈后,剩下就是怎样减少RPC调用。RPC上的数据是写满的,在不停地运行,对RPC调用时,要把整块的数据都写到RPC连接上,写完连接马上就释放给别人用,如果期望减少调用次数,每次尽量写入多个数据。
连接池上要根据业务做一个任务池,换成任务池后(对不同的接口放不同的任务池),在任务池里接收通道的一些数据,再在任务池里面打包请求,最后对多条数据做一次RPC调用。这样,RPC连接上的瞬间也降低了次数,减少了串行机率。批量调用属于业务级别的优化,RPC接口支持批量的处理,但批量调用后,如果QPS的请求量少,构出的协程就少。开的协程少不会提高效率。在网络不好、阻塞的情况下,每接收一次请,协程暴涨会导致阻塞,如果里面有流控,协程就会把内存崩上去,这也是有些机器隔几天就会内存暴涨还降不下去的原因。通过这种方式减少了协程调用次数,系统性能没有特别大的提高,但在任务池可以做流控,当队列超过一定长度可以做策略,重要的接口要重试,不重要的丢掉。流控可以在RPC底下做,但RPC不识别接口,它没法决定在出现流控策略时是选择丢掉还是定义的接口操作。任务池+Pipeline的连接池可以把整个系统的吞吐量达到最高(不是QPS)。
经验二:Go语言开发追求开销优化的极限,谨慎引入其他语言领域高性能服务的通用方案。
主要关注内存池、对象池适用于代码可读性与整体效率的权衡。这种程序一定情况下会增加串行度。用内存一定要加锁,不加锁用原理操作有额外的开销,程序的可读性会越来越像C语言,每次要malloc,各地方用完后要free,free之前要reset,各种操作做完之后会发现问题。这里的优化策略是仿达达做的数据框架,然后做了一个仿Memorycache形式的内存池。
上图左边的数组实际上是一个列表,这个列表按大小将内存分块。在协议解期时不知道长度,需要动态计算长度,所以申请适配的大小不够,就把这块还回去再申请一个Bucket。加入内存池之后减少了一些机器的开销,但是程序的可读性严重降低。
对象池策略本身有一个Sync库的API,在申请对象的时候要做清理操作,包括通道的清理,防止有障数据,增加开销。其实CPU大部分时间是空闲的,只有在广播的时候比较高,加上这两个策略之后,程序的串行度提高,内存的机器时间加长,但QPS不一定上升。
具有Go特色的运维
依托Go语言做一些常规的运维工作需要一些常识。线上处理就是看一下协程在F上是否有协程疏漏、高阻塞。因为有时看不到,所以他们对线上的实例监控做了一个统一的管理和可视化操作。Go语言提供配套的组合工具做一些更方便开发调试的机制。第一点是Profiling可视化,可以从中发现历史记录,出现问题时的峰值、协程数,可以比较两次上线完之后进程到了什么样的状态。比如运维的时候做一个分析群,然后把一部分的产品分到一个单独集群上,发现这个集群总比另一个集群多4到5个内存(程序是同一个),直接打开图就非常明了地显示。在一个Buffer中,这个集群明显较大的原因是两年前做了一个策略防止重新拷贝。当时写的逻辑针对每个产品开Buffer,开了一百万。这个集群就是一个开源平台,上面有上万个App,数值提供的时候明显不是同一个图,它的Buffer更大。各种问题都可以通过对Go语言提供的Profiling、协程、本机机器时间、相关数量进行监控。
另外,通讯可视化,长连接调用基本是RPC调用,RPC库、Redis的库、MySQL库给力,整个系统就可控。所以要对RPC库、Redis的库做各种代码内嵌,要统计它的QPS、网络带宽占用、各种出错情况。然后再通过各种压测手段,发现要做的优化对性能是否有影响。如果一个系统不可评估就无法优化,而如果可评估就会发现一些潜在的问题。通讯可视化是在RPC库和Redis库植入自己的代码。其实选择RPC库并不重要,重要的是能够对它改造、监控。
可视化还可以做压测。由于压测不能出实时的数据,可选一百台机器,对一台进行压测,通过后台看各种性能参数,然后通过RPC库的结构判断各数据。压测完后,每一个压测的进程要汇总统计数据,业务的QPS数量、协议版本、连接建立成功的时间和每秒钟建立连接数量,这些细节的性能参数决定系统的潜在问题,因此,压测平台最好要做统计数据的功能。360的团队做了一个简单的压测后台,可以选定一些机器进行压测。一台机器压测由于网络问题和机器本身的CPU线路无法测出问题。因此,压测时最好选十几台机器,每台机器开10段连接做压测。
运维对线上进行拆分,可以减少机器时间,但运维压力变大。通过开协程的方式解决相当于把这台机器转嫁到各个进程上,虽然机器时间短,但频繁次数多,所以问题并未得到解决。开多进程可以节省时间,但卡顿时间和体量变成渐进性。系统根据使用的各种资源不同可做一个横向拆分,按业务拆分(助手、卫士、浏览器)、功能拆分(push、聊天、嵌入式产品)和IDC拆分(zwt、bjsc、bjdt、bjcc、shgt、shjc、shhm、Amazon
Singapore),拆解后带来管理成本,引入(ZooKeeper+deployd)/(Keeper+Agent)对各节点进行管理。
正常情况下,运维都采用ZooKeeper管理各个进程的动态配置文件。第二部分相当于Profiling数据,用后台去各个进程中请求,实时监控各个接口,通讯录的数据也通过后台进行请求,这时Keeper的节点要配置,后台也要配置。这种功能可以抽象一下,理论上期望客户端有个SDK,中心节点有个Keeper,然后可以对配置文件进行管理,对Profiling、自己写的各种库的信息进行收集,再汇总,放到本地数据或者文件夹,通过接口对后台提供服务。服务通过网络进行启动,管理层集中在Keeper上而不是在后台和Keeper上,所以Keeper的同步会考虑用一些开源的东西。360团队写了一些工具把正常的配置文件用Key-Value的形式支持一些Map结构,反序相当于写了一个Convert工具。剩下的用Profiling,相当于跟Keeper和节点进行通信,所以Profiling会很高。Keeper的启动相当于用一个Agent启动进程,然后指定Keeper中心节点端口把信息传过去,当Keeper正好配了这个节点就能把配置发过去,如果没有配就丢失。
以上内容由牛小七根据360周洋在Gopher China大会上的技术分享整理而成,希望对大家有所帮助。
周洋,曾供职于金山游戏,以前从事C语言、PHP开发,2012年加入360手机助手,从事360消息系统开发,经历了系统从千万到数亿用户的数次迭代。目前专注于Go语言大规模实时通信系统的架构研究和探索。
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。AssassinGo: 基于 Go 和 Vue 的高并发可扩展的 Web 渗透框架
· 31 天前 · 167 次点击
AssassinGo 是一款使用 Golang 开发,集成了高可用信息收集、基础攻击向量探测、Google-Hacking 综合搜索和 PoC 自定义添加并对目标进行批量检测等功能的自动化 Web 渗透框架,该框架有着基于 Vue 的 WebGUI,前后端交互主要采用 WebSocket 技术,会将结果实时显示在前台,并且其可扩展性极强,用户实现各模块接口即可添加自定义功能。
网站: https://assassin-go.ink
项目地址:
目前尚无回复
& · & 2207 人在线 & 最高记录 3541 & · &
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.1 · 18ms · UTC 11:12 · PVG 19:12 · LAX 04:12 · JFK 07:12? Do have faith in what you're doing.golang的web框架是如何处理高并发场景下的http请求的?
- Go语言中文网 - Golang中文社区
<meta name="author" content="polaris ">
golang的web框架是如何处理高并发场景下的http请求的?
· 978 次点击 ·
开始浏览& &
&/br&golang来处理http请求,有自带的http包,也有各种框架如gin等,但是很少有资料说明这些包和框架在高并发请求下是如何处理的?&/br&&/br&
比如java的spring会创建一个线程池和队列来帮助处理大量请求,那golang的http或者gin框架是如何做的呢?创建一个goroutine池?还有其他更细节的说明吗?网上资料好像很少。。。
978 次点击 &
8 回复 &| &直到
请尽量让自己的回复能够对别人有帮助
支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
支持 @ 本站用户;支持表情(输入 : 提示),见
图片支持拖拽、截图粘贴等方式上传
记住登录状态
1976 人在线
&最高记录 2928
& studygolang.com Go语言中文网,中国 Golang 社区,致力于构建完善的 Golang 中文社区,Go语言爱好者的学习家园。
Powered by
&o&·&CDN 采用
VERSION: V3.5.0&·&14.764616ms&·&为了更好的体验,本站推荐使用 Chrome 或 Firefox 浏览器
登录和大家一起探讨吧
记住登录状态
还不是会员}

我要回帖

更多关于 go 每秒最大并发 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信