内容简介:协程的一些特性和优点我就不说了,网上很多文章都讲述的很透彻。协程可以理解为纯用户态的线程,其通过协作而不是抢占来进行切换。相对于进程或者线程,协程所有的操作都可以在用户态完成,创建和切换的消耗更低。开发者可以无感知的用同步的代码编写方式达到异步IO的效果和性能,避免了传统异步回调所带来的离散的代码逻辑和陷入多层回调中导致代码无法维护。1、golang::goroutine
协程的一些特性和优点我就不说了,网上很多文章都讲述的很透彻。
协程可以理解为纯用户态的线程,其通过协作而不是抢占来进行切换。相对于进程或者线程,协程所有的操作都可以在用户态完成,创建和切换的消耗更低。开发者可以无感知的用同步的代码编写方式达到异步IO的效果和性能,避免了传统异步回调所带来的离散的代码逻辑和陷入多层回调中导致代码无法维护。
1、golang::goroutine
最近在学习 go,一些高大上的特性果然是为高并发而生,自带的 net/http 包对请求的处理也透明的放在了协程上下文中,真开箱即用。
server.go
package main import ( "fmt" "net/http" "time" "log" "runtime" "bytes" "strconv" ) func main() { // 注册请求 handler http.HandleFunc("/", func (responseWrite http.ResponseWriter, request *http.Request) { log.Println("goroutine: ", GetGID(), "start") // 模拟2秒的IO耗时操作 会发生协程切换 time.Sleep(2 * time.second) // 协程继续执行 log.Println("goroutine: ", GetGID(), "end") // 结束请求 fmt.Fprintln(responseWrite, "<h1>hello world!</h1>") }) log.Println("server start ...") http.ListenAndServe(":8081", nil) } // 获取 goroutine 的协程 id func GetGID() uint64 { b := make([]byte, 64) b = b[:runtime.Stack(b, false)] b = bytes.TrimPrefix(b, []byte("goroutine ")) b = b[:bytes.IndexByte(b, ' ')] n, _ := strconv.ParseUint(string(b), 10, 64) return n }
golang 自带的 net/http 包是会自行的创建一个协程去处理请求的,所以阻塞的 2秒 并不会将主进程挂起,主进程仍在不停的接收请求并创建一个协程去处理请求。
2、swoole::coroutine
Swoole在2.0开始内置协程(Coroutine)的能力,提供了具备协程能力IO接口(统一在命名空间SwooleCoroutine*)。
server.php
<?php // 高并发下多协程也需要一定的内存 ini_set('mermory_limit', '1024M'); $server = new Swoole\Http\Server('127.0.0.1', 8082); // 工作进程数设为1 这样和 golang 的进程模型基本一致 // swoole worker 默认的 max_coroutine 数为 3000 可根据业务负载自行配置 // @reference https://wiki.swoole.com/wiki/page/950.html $server->set([ 'worker_num' => 1, ]); $server->on("start", function ($server) { echo "server start ..." . PHP_EOL; }); // 处理请求 $server->on('request', function ($request, $response) { echo "goroutine: " . Swoole\Coroutine::getuid() . " start" . PHP_EOL; /** * 这里我们需要使用 Swoole\Coroutine::sleep() 来模拟IO耗时 触发协程切换 * 真实的业务场景中你可能使用的是以下协程客户端 * Coroutine\Client Coroutine\Http\Client Coroutine\Http2\Client * Coroutine\Redis Coroutine\Socket Coroutine\MySQL Coroutine\PostgreSQL * php 的 sleep() 是没办法触发协程切换的 会被同步阻塞 * 这也是为什么 swoole 的协程中要使用指定的 IO 客户端才能发挥协程的性能 */ Swoole\Coroutine::sleep(2); echo "goroutine: " . Swoole\Coroutine::getuid() . " end" . PHP_EOL; $response->end("<h1>Hello world!</h1>"); }); $server->start();
swoole 的 Server 同 golang 的 net/http 一样的将协程透明化,不过我们在 swoole 中需要使用一些预先提供的方法或客户端才能触发协程切换,进而发挥协程的高性能。
swoole 的各协程客户端: https://wiki.swoole.com/wiki/...
3、ab压测
服务器配置略渣,vsphere上的一台小小自用服务器 1 核 2G,不要在意
压测 200 并发 5000 请求
ab -c 200 -n 5000 -k http://127.0.0.1:8081 ab -c 200 -n 5000 -k http://127.0.0.1:8082
go
swoole
压测 500 并发 5000 请求
ab -c 500 -n 5000 -k http://127.0.0.1:8081 ab -c 500 -n 5000 -k http://127.0.0.1:8082
go
swoole
压测 1000 并发 5000 请求
ab -c 1000 -n 5000 -k http://127.0.0.1:8081 ab -c 1000 -n 5000 -k http://127.0.0.1:8082
go
swoole
压测 2000 并发 5000 请求
ab -c 2000 -n 5000 -k http://127.0.0.1:8081 ab -c 2000 -n 5000 -k http://127.0.0.1:8082
go
swoole
可以通过以上几组数据看出,golang::goroutine 和 swoole::coroutine 两个协程的性能基本没有什么差距,而且协程在高并发下才能更直观的体现出优异的性能。
同样 5000 的请求,200 - 500 - 1000 - 2000 处理耗时越来越小,说明不是我吃不掉,是你发的太慢。当 2000 并发时,我这台 1核 2G 的服务器都能在阻塞 2 秒的场景下跑到 600- 并发。
但 swoole 还是要占更多些的内存,我测试机内存比较小,没办法用 swoole 完美的跑 c10k,内存不够用,没办法创建更多的协程去并发处理请求,但 go 还可以,后面在看下 go::goroutine 是不是占用内存更小吧,先跑一个看看。
go 10k 并发 200k请求
go 20k 并发 1000k请求
ab 最大并发模拟量为 20k,仅供参考
以上所述就是小编给大家介绍的《golang/goroutine 和 swoole/coroutine 协程性能测试对比》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Kubernetes 存储性能对比
- 快排和堆排性能对比
- 性能压测工具选型对比
- 性能对比:ReentrantLock vs Synchronized
- Mobx 与 Redux 的性能对比
- Protobuf的使用及性能对比测试
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
如何把事情做到最好
乔治·伦纳德 / 张乐 / 中国青年出版社 / 2014-2 / 29.90元
•改变全球9800万人的人生指导书 •全美第一本系统阐述学习与成功之道的经典著作 •长期盘踞全美畅销书榜单 •21年后,这本传奇之书终于在中国震撼上市 •把事情做到最好,第一不强求天赋,第二不介意起步的早晚,你要做的就是“起步走”并“不停地走” 《如何把事情做到最好》出 版于1992年,经久不衰,经过一代又一代的读者口碑相传后,畅销至今。作者以其独特的视角告诉人们,如......一起来看看 《如何把事情做到最好》 这本书的介绍吧!