博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
swoole系列之八:进程内连接池的作用和实现
阅读量:3986 次
发布时间:2019-05-24

本文共 17696 字,大约阅读时间需要 58 分钟。

摘自:https://mp.weixin.qq.com/s/nTEkdrHM2bv3aRdj4mEQeg

连接池的种类

其实也就是连接池的使用场景

1、可以是一个独立部署的服务,通过套接字提供代理服务。例如我们的常用的mysqlproxy。

2、可以是一个服务内部进程间共享的连接池,这种相对更加轻量,可以理解为项目级别,只对内提供服务。
3、进程内的连接池,更加轻量,当前进程内的线程或者协程可以使用。

今天我们这里要介绍的是进程内的连接池,我们以PHP为例,使用协程并发的场景来观察连接池的作用效果。

首先我们要心里琢磨,我们连接池的连接作用:
1、减少客户端使用连接时,创建和销毁连接的时间和系统资源开销,这里涉及到TCP的三次握手也四次挥手,还有TCP的慢启动预热。
2、避免极端情况大量连接直接涌入后端服务,对整个系统服务造成危害。
但同时也有一些缺点,比如空闲状态下也要维护一定数量的连接,占用客户端和服务端的资源,这里可以根据实际需求动态调配连接数,达到效率和资源利用的平衡。哪有一点资源不占用,还想系统高效稳定的事情,建个水坝还得占片地,护坝人间断性的职守呢。

例如我们要提供100QPS的服务用户查询服务,后端DB是Redis(也可以是mysql,我们这里只是假设,实际上redis的单机处理能力是10w/s这个数量级),我可以先事先创建好100个redis连接,每个请求到来拿一个连接使用,请求结束后再归还到连接池中。但是万一有超过预期并发量的连接应该怎么办呢,一般可以排队处理或者降级处理。排队时等待当前服务进程空闲后再处理,当然这会增加客户端的响应时间。降级处理是返回其他的数据,不走DB请求。

下面秀出我们的基础代码,这里只是演示功能,没有对模块做进一步封装。

Step 1
最简单的http服务器

server = $server; } function request($request, $response){
$redis = new redis; $redis->connect("127.0.0.1", 6379); $val = $redis->get("key"); $response->end("

Hello Swoole redis val $val #" . rand(1000, 9999) . ""); } function start(){
$this->server->on('request', [$this, "request"]); $this->server->set([ 'worker_num' => 1 ]); $this->server->start(); }}(new MyServer())->start();

我们使用 swoole process 多进程模式,只开启一个进程为方便调试,运行脚本后

$ ps -ef  | grep -v grep |grep server1.phpshiguan+ 30587  8251  0 20:37 pts/11   00:00:00 php server1.phpshiguan+ 30588 30587  0 20:37 pts/11   00:00:00 php server1.phpshiguan+ 30590 30588  0 20:37 pts/11   00:00:00 php server1.php

我们可以发现三个进程,熟悉swoole的同学都知道30590进程是工作进程.

我们在命令行执行 curl 'http://127.0.0.1:9501' 可以得到服务器反馈

Hello Swoole redis val value2 #6642

然后我们通过lsof -p 30590 查看工作进程打开的文件描述符, 发现并没有redis的连接.这是为什么呢?自问自答一波,因为在php的执行流程中,所有局部变量在退出当前作用域时,都会进行释放,也就是16行建立连接的$redis对象,在执行完毕当前请求后进行了释放,我们可以通过strace进一步验证

$ sudo strace -s 1000 -p 30590strace: Process 30590 attachedepoll_wait(3, [{
EPOLLIN, {
u32=4, u64=12884901892}}], 4096, -1) = 1read(4, "\2\0\0\0N\0\0\0\0\0\0\0\3\0\0\0GET / HTTP/1.1\r\nHost: 127.0.0.1:9501\r\nUser-Agent: curl/7.58.0\r\nAccept: */*\r\n\r\n", 425952) = 94brk(0x55bdabaae000) = 0x55bdabaae000socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 7fcntl(7, F_GETFL) = 0x2 (flags O_RDWR)fcntl(7, F_SETFL, O_RDWR|O_NONBLOCK) = 0connect(7, {
sa_family=AF_INET, sin_port=htons(6379), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in progress)poll([{
fd=7, events=POLLIN|POLLOUT|POLLERR|POLLHUP}], 1, 60000) = 1 ([{
fd=7, revents=POLLOUT}])getsockopt(7, SOL_SOCKET, SO_ERROR, [0], [4]) = 0fcntl(7, F_SETFL, O_RDWR) = 0setsockopt(7, SOL_TCP, TCP_NODELAY, [1], 4) = 0setsockopt(7, SOL_SOCKET, SO_KEEPALIVE, [0], 4) = 0poll([{
fd=7, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout)sendto(7, "*2\r\n$3\r\nGET\r\n$3\r\nkey\r\n", 22, MSG_DONTWAIT, NULL, 0) = 22poll([{
fd=7, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout)poll([{
fd=7, events=POLLIN|POLLERR|POLLHUP}], 1, 60000) = 1 ([{
fd=7, revents=POLLIN}])recvfrom(7, "$6\r\nvalue2\r\n", 8192, MSG_DONTWAIT, NULL, NULL) = 12close(7) = 0sendto(4, "\2\0\0\0\305\0\0\0\0\0\0\0\0\0\0\0HTTP/1.1 200 OK\r\nServer: swoole-http-server\r\nConnection: keep-alive\r\nContent-Type: text/html\r\nDate: Tue, 22 Oct 2019 12:43:10 GMT\r\nContent-Length: 44\r\n\r\n

Hello Swoole redis val value2 #4823", 213, 0, NULL, 0) = 213brk(0x55bdab88e000) = 0x55bdab88e000epoll_wait(3, [{
EPOLLIN, {
u32=4, u64=12884901892}}], 4096, -1) = 1read(4, "\2\0\0\0\0\0\0\0\0\0\4\0\3\0\0\0", 425952) = 16sendto(4, "\2\0\0\0\0\0\0\0\0\0\4\0\0\0\0\0", 16, 0, NULL, 0) = 16epoll_wait(3,

我们可以发现连接到redis的fd,在执行完recv以后close(7)关闭了连接.

话外音: 行走江湖重要招式,lsof -p pid, strace -p pid
到这里和连接池没有半毛钱关系,因为这个服务是短连接,每次处理请求需要创建连接,关闭连接,对应有tcp的三次握手和四次挥手等老生长谈的问题,具体可以参考我们郭新华老师在Swoole微课程中的视频教程.

Step 2

感受一下长连接,我们可以通过将连接对象的变量赋值给类属性的简单操作,增加其引用计数,从而使得请求结束后不能对对象进行释放.

server = $server; } function request($request, $response){
$redis = new redis; $redis->connect("127.0.0.1", 6379); $this->pool[] = $redis; $val = $redis->get("key"); $response->end("

Hello Swoole redis val $val #" . rand(1000, 9999) . ""); } function start(){
$this->server->on('request', [$this, "request"]); $this->server->set([ 'worker_num' => 1 ]); $this->server->start(); }}(new MyServer())->start();

通过简单的代码修改,然后通过 lsof -p 查看工作进程打开的文件描述符

php     31598 shiguangqi    4u     unix 0x0000000000000000       0t0  6577793 type=DGRAMphp     31598 shiguangqi    5u     unix 0x0000000000000000       0t0  6577794 type=DGRAMphp     31598 shiguangqi    6u  a_inode               0,13         0    11932 [signalfd]php     31598 shiguangqi    7u     IPv4            6579223       0t0      TCP localhost:48048->localhost:6379 (ESTABLISHED)

我们可以发现在最下方真的有打开的redis连接,同样也可以strace来跟踪请求的系统调用,这里我们省去.这个代码是我们每次请求都去创建新的连接,没有任何复用,基本无法使用.

Step 3

渐入佳境,我们想要的是可以重复利用的一个连接池,有几种选择

当请求到来的时候,尝试从连接池中获取连接对象,如果连接池为空,创建连接对象,请求结束的时候,归还至连接池.

进程启动的时候,创建固定数量的连接对象,当请求到来的时候,尝试从连接池中获取连接对象,如果连接池为空,继续等待或者服务降级; 不为空的话正常服务,请求结束的时候,归还至连接池.
我们这里选择第一种方式,每个方式都各有优势,我们可根据自己情况进行取舍,下面是动态创建连接的实例代码

server = $server; $this->pool = new \SplQueue(); } function request($request, $response){
if ($this->pool->count() > 0) {
$redis = $this->pool->pop(); } else {
$redis = new redis; $redis->connect("127.0.0.1", 6379); } $val = $redis->get("key"); $response->end("

Hello Swoole redis val $val #" . rand(1000, 9999) . ""); $this->pool->push($redis); } function start(){
$this->server->on('request', [$this, "request"]); $this->server->set([ 'worker_num' => 1 ]); $this->server->start(); }}(new MyServer())->start();

我们这里实现了连接的动态创建和复用,可以通过strace来验证发现,两次连续的请求,第一次会创建连接,第二次会复用我们的fd

$ sudo strace -s 1000 -p 1001[sudo] shiguangqi 的密码:strace: Process 1001 attachedbrk(0x556c8955b000)                     = 0x556c8955b000epoll_wait(3, [{
EPOLLIN, {
u32=4, u64=12884901892}}], 4096, -1) = 1read(4, "\1\0\0\0N\0\0\0\0\0\0\0\3\0\0\0GET / HTTP/1.1\r\nHost: 127.0.0.1:9501\r\nUser-Agent: curl/7.58.0\r\nAccept: */*\r\n\r\n", 425952) = 94mmap(NULL, 2101248, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f61bd028000socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP) = 7close(7) = 0socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 7fcntl(7, F_GETFL) = 0x2 (flags O_RDWR)fcntl(7, F_SETFL, O_RDWR|O_NONBLOCK) = 0connect(7, {
sa_family=AF_INET, sin_port=htons(6379), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in progress)poll([{
fd=7, events=POLLIN|POLLOUT|POLLERR|POLLHUP}], 1, 60000) = 1 ([{
fd=7, revents=POLLOUT}])getsockopt(7, SOL_SOCKET, SO_ERROR, [0], [4]) = 0fcntl(7, F_SETFL, O_RDWR) = 0setsockopt(7, SOL_TCP, TCP_NODELAY, [1], 4) = 0setsockopt(7, SOL_SOCKET, SO_KEEPALIVE, [0], 4) = 0poll([{
fd=7, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout)sendto(7, "*2\r\n$3\r\nGET\r\n$3\r\nkey\r\n", 22, MSG_DONTWAIT, NULL, 0) = 22poll([{
fd=7, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 1 ([{
fd=7, revents=POLLIN}])recvfrom(7, "$", 1, MSG_PEEK, NULL, NULL) = 1poll([{
fd=7, events=POLLIN|POLLERR|POLLHUP}], 1, 60000) = 1 ([{
fd=7, revents=POLLIN}])recvfrom(7, "$6\r\nvalue2\r\n", 8192, MSG_DONTWAIT, NULL, NULL) = 12getpid() = 1001getpid() = 1001fcntl(4, F_GETFL) = 0x802 (flags O_RDWR|O_NONBLOCK)fcntl(4, F_SETFL, O_RDWR|O_NONBLOCK) = 0sendto(4, "\1\0\0\0\305\0\0\0\0\0\0\0\0\0\0\0HTTP/1.1 200 OK\r\nServer: swoole-http-server\r\nConnection: keep-alive\r\nContent-Type: text/html\r\nDate: Tue, 22 Oct 2019 13:17:45 GMT\r\nContent-Length: 44\r\n\r\n

Hello Swoole redis val value2 #6094", 213, 0, NULL, 0) = 213munmap(0x7f61bd028000, 2101248) = 0epoll_wait(3, [{
EPOLLIN, {
u32=4, u64=12884901892}}], 4096, -1) = 1read(4, "\1\0\0\0\0\0\0\0\0\0\4\0\3\0\0\0", 425952) = 16sendto(4, "\1\0\0\0\0\0\0\0\0\0\4\0\0\0\0\0", 16, 0, NULL, 0) = 16epoll_wait(3, [{
EPOLLIN, {
u32=4, u64=12884901892}}], 4096, -1) = 1read(4, "\2\0\0\0N\0\0\0\0\0\0\0\3\0\0\0GET / HTTP/1.1\r\nHost: 127.0.0.1:9501\r\nUser-Agent: curl/7.58.0\r\nAccept: */*\r\n\r\n", 425952) = 94brk(0x556c8977b000) = 0x556c8977b000poll([{
fd=7, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout)sendto(7, "*2\r\n$3\r\nGET\r\n$3\r\nkey\r\n", 22, MSG_DONTWAIT, NULL, 0) = 22poll([{
fd=7, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout)poll([{
fd=7, events=POLLIN|POLLERR|POLLHUP}], 1, 60000) = 1 ([{
fd=7, revents=POLLIN}])recvfrom(7, "$6\r\nvalue2\r\n", 8192, MSG_DONTWAIT, NULL, NULL) = 12sendto(4, "\2\0\0\0\305\0\0\0\0\0\0\0\0\0\0\0HTTP/1.1 200 OK\r\nServer: swoole-http-server\r\nConnection: keep-alive\r\nContent-Type: text/html\r\nDate: Tue, 22 Oct 2019 13:17:46 GMT\r\nContent-Length: 44\r\n\r\n

Hello Swoole redis val value2 #6308", 213, 0, NULL, 0) = 213epoll_wait(3, [{
EPOLLIN, {
u32=4, u64=12884901892}}], 4096, -1) = 1read(4, "\2\0\0\0\0\0\0\0\0\0\4\0\3\0\0\0", 425952) = 16sendto(4, "\2\0\0\0\0\0\0\0\0\0\4\0\0\0\0\0", 16, 0, NULL, 0) = 16epoll_wait(3,

可以验证在38行,第二次请求的时候,并没有重新创建连接,完全符合我们程序预期

Step 4

重点来了,通过观察系统调用我们可以发现,以上的例子我们使用的是单进程同步模式,也就是不支持单进程的并发处理.到这里协程的威力要出来了,我们可以支持单进程并发(php对多线程的支持不好,几乎没人使用php的ZTS版本)
重点又又来了,我们需要做的只需要在(new MyServer())->start();前增加一行代码

Swoole\Runtime::enableCoroutine();(new MyServer())->start();

接下来观察工作进程两次请求的系统调用

$ sudo strace -s 1000 -p 1747strace: Process 1747 attachedbrk(0x55f6d8c9e000)                     = 0x55f6d8c9e000epoll_wait(3, [{
EPOLLIN, {
u32=4, u64=12884901892}}], 4096, -1) = 1read(4, "\1\0\0\0N\0\0\0\0\0\0\0\3\0\0\0GET / HTTP/1.1\r\nHost: 127.0.0.1:9501\r\nUser-Agent: curl/7.58.0\r\nAccept: */*\r\n\r\n", 425952) = 94mmap(NULL, 2101248, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3edce28000socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC, IPPROTO_IP) = 7fcntl(7, F_GETFL) = 0x2 (flags O_RDWR)fcntl(7, F_SETFL, O_RDWR|O_NONBLOCK) = 0setsockopt(7, SOL_TCP, TCP_NODELAY, [1], 4) = 0setsockopt(7, SOL_TCP, TCP_NODELAY, [1], 4) = 0connect(7, {
sa_family=AF_INET, sin_port=htons(6379), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in progress)brk(0x55f6d8cc0000) = 0x55f6d8cc0000epoll_ctl(3, EPOLL_CTL_ADD, 7, {
EPOLLOUT, {
u32=7, u64=25769803783}}) = 0brk(0x55f6d8ca0000) = 0x55f6d8ca0000epoll_wait(3, [{
EPOLLOUT, {
u32=7, u64=25769803783}}], 4096, 60000) = 1epoll_ctl(3, EPOLL_CTL_DEL, 7, NULL) = 0getsockopt(7, SOL_SOCKET, SO_ERROR, [0], [4]) = 0setsockopt(7, SOL_TCP, TCP_NODELAY, [1], 4) = 0setsockopt(7, SOL_SOCKET, SO_KEEPALIVE, [0], 4) = 0recvfrom(7, 0x7f3ee07b69ef, 1, MSG_PEEK, NULL, NULL) = -1 EAGAIN (Resource temporarily unavailable)sendto(7, "*2\r\n$3\r\nGET\r\n$3\r\nkey\r\n", 22, 0, NULL, 0) = 22recvfrom(7, 0x7f3ee07b69ef, 1, MSG_PEEK, NULL, NULL) = -1 EAGAIN (Resource temporarily unavailable)recvfrom(7, "$6\r\nvalue2\r\n", 8192, 0, NULL, NULL) = 12getpid() = 1747getpid() = 1747fcntl(4, F_GETFL) = 0x802 (flags O_RDWR|O_NONBLOCK)fcntl(4, F_SETFL, O_RDWR|O_NONBLOCK) = 0sendto(4, "\1\0\0\0\305\0\0\0\0\0\0\0\0\0\0\0HTTP/1.1 200 OK\r\nServer: swoole-http-server\r\nConnection: keep-alive\r\nContent-Type: text/html\r\nDate: Tue, 22 Oct 2019 13:27:04 GMT\r\nContent-Length: 44\r\n\r\n

Hello Swoole redis val value2 #3338", 213, 0, NULL, 0) = 213munmap(0x7f3edce28000, 2101248) = 0epoll_wait(3, [{
EPOLLIN, {
u32=4, u64=12884901892}}], 4096, -1) = 1read(4, "\1\0\0\0\0\0\0\0\0\0\4\0\3\0\0\0", 425952) = 16sendto(4, "\1\0\0\0\0\0\0\0\0\0\4\0\0\0\0\0", 16, 0, NULL, 0) = 16epoll_wait(3, [{
EPOLLIN, {
u32=4, u64=12884901892}}], 4096, -1) = 1read(4, "\2\0\0\0N\0\0\0\0\0\0\0\3\0\0\0GET / HTTP/1.1\r\nHost: 127.0.0.1:9501\r\nUser-Agent: curl/7.58.0\r\nAccept: */*\r\n\r\n", 425952) = 94brk(0x55f6d8ec0000) = 0x55f6d8ec0000recvfrom(7, 0x7f3ee07b69ef, 1, MSG_PEEK, NULL, NULL) = -1 EAGAIN (Resource temporarily unavailable)sendto(7, "*2\r\n$3\r\nGET\r\n$3\r\nkey\r\n", 22, 0, NULL, 0) = 22recvfrom(7, "$", 1, MSG_PEEK, NULL, NULL) = 1recvfrom(7, "$6\r\nvalue2\r\n", 8192, 0, NULL, NULL) = 12sendto(4, "\2\0\0\0\305\0\0\0\0\0\0\0\0\0\0\0HTTP/1.1 200 OK\r\nServer: swoole-http-server\r\nConnection: keep-alive\r\nContent-Type: text/html\r\nDate: Tue, 22 Oct 2019 13:28:50 GMT\r\nContent-Length: 44\r\n\r\n

Hello Swoole redis val value2 #1576", 213, 0, NULL, 0) = 213brk(0x55f6d8ca0000) = 0x55f6d8ca0000epoll_wait(3, [{
EPOLLIN, {
u32=4, u64=12884901892}}], 4096, -1) = 1read(4, "\2\0\0\0\0\0\0\0\0\0\4\0\3\0\0\0", 425952) = 16sendto(4, "\2\0\0\0\0\0\0\0\0\0\4\0\0\0\0\0", 16, 0, NULL, 0) = 16epoll_wait(3,

可以发现是通过epoll_wait来监听redis句柄的读写事件.

但是有个问题,如果当前时间有大量的请求涌入,会建立大量的redis连接,对后端服务造成杀伤,我们来通过ab压测演示一下

$ ab -c 1000 -n 10000 'http://127.0.0.1:9501/'This is ApacheBench, Version 2.3 <$Revision: 1807734 $>Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/Licensed to The Apache Software Foundation, http://www.apache.org/Benchmarking 127.0.0.1 (be patient)Completed 1000 requestsCompleted 2000 requestsCompleted 3000 requestsCompleted 4000 requestsCompleted 5000 requestsCompleted 6000 requestsCompleted 7000 requestsCompleted 8000 requestsCompleted 9000 requestsCompleted 10000 requestsFinished 10000 requestsServer Software:        swoole-http-serverServer Hostname:        127.0.0.1Server Port:            9501Document Path:          /Document Length:        44 bytesConcurrency Level:      1000Time taken for tests:   0.512 secondsComplete requests:      10000Failed requests:        0Total transferred:      1920000 bytesHTML transferred:       440000 bytesRequests per second:    19528.96 [#/sec] (mean)Time per request:       51.206 [ms] (mean)Time per request:       0.051 [ms] (mean, across all concurrent requests)Transfer rate:          3661.68 [Kbytes/sec] receivedConnection Times (ms)              min  mean[+/-sd] median   maxConnect:        0    3   5.8      2      39Processing:     1    5   6.3      4      61Waiting:        1    4   6.3      3      61Total:          4    7   9.2      5      64Percentage of the requests served within a certain time (ms)  50%      5  66%      6  75%      6  80%      6  90%      7  95%     38  98%     44  99%     56 100%     64 (longest request)

然后通过查看通过压测建立了多少连接

$ lsof -p 2323  | grep 'localhost:6379 (ESTABLISHED)' | wc -l 129

这里只是本地使用redis执行最简单的操作,如果请求IO时间较长,连接不能及时释放,会建立更多的连接.这里会对后端造成不可预估的杀伤.有没有什么办法可以限制并发数,对服务资源进行控制呢,答案是肯定的.我们可以使用channel来限制并发

Step 5

server = $server; $this->pool = new \SplQueue(); } function request($request, $response){
$this->chan->push(true); if ($this->pool->count() > 0) {
$redis = $this->pool->pop(); } else {
$redis = new redis; $redis->connect("127.0.0.1", 6379); } $val = $redis->get("key"); $response->end("

Hello Swoole redis val $val #" . rand(1000, 9999) . ""); $this->pool->push($redis); $this->chan->pop(); } function workerStart($server, $worker_id){
echo "worker start $worker_id\n"; Swoole\Runtime::enableCoroutine(); $this->chan = new Swoole\Coroutine\Channel(10); } function start(){
$this->server->on('request', [$this, "request"]); $this->server->on('workerStart', [$this, "workerStart"]); $this->server->set([ 'worker_num' => 1 ]); $this->server->start(); }}(new MyServer())->start();

我们对上面的代码进行多次压测,

$ ab -c 1000 -n 10000 'http://127.0.0.1:9501/'This is ApacheBench, Version 2.3 <$Revision: 1807734 $>Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/Licensed to The Apache Software Foundation, http://www.apache.org/Benchmarking 127.0.0.1 (be patient)Completed 1000 requestsCompleted 2000 requestsCompleted 3000 requestsCompleted 4000 requestsCompleted 5000 requestsCompleted 6000 requestsCompleted 7000 requestsCompleted 8000 requestsCompleted 9000 requestsCompleted 10000 requestsFinished 10000 requestsServer Software:        swoole-http-serverServer Hostname:        127.0.0.1Server Port:            9501Document Path:          /Document Length:        44 bytesConcurrency Level:      1000Time taken for tests:   0.477 secondsComplete requests:      10000Failed requests:        0Total transferred:      1920000 bytesHTML transferred:       440000 bytesRequests per second:    20949.87 [#/sec] (mean)Time per request:       47.733 [ms] (mean)Time per request:       0.048 [ms] (mean, across all concurrent requests)Transfer rate:          3928.10 [Kbytes/sec] receivedConnection Times (ms)              min  mean[+/-sd] median   maxConnect:        4   17   4.0     18      24Processing:     8   28   5.5     29      43Waiting:        5   23   6.0     23      39Total:         27   46   3.9     46      56Percentage of the requests served within a certain time (ms)  50%     46  66%     47  75%     48  80%     49  90%     50  95%     52  98%     53  99%     53 100%     56 (longest request)

发现最多只会有10个连接,这里的连接数是我们进行硬编码设置.

$ lsof -p 5093 | grep 'localhost:6379 (ESTABLISHED)' | wc -l 10

总结

需要说明的是,我们的实例代码只是演示,很多地方并不严谨而且没有模块化的封装,例如redis连接的建立没有检查成功,也没有处理redis请求的失败重连,还有很多细节需要完善.在生产环境当中,需要对外部的每一项资源保持警惕,不信任.连接很可能被服务端切断.这里也没有涉及到用户提交的参数等过程.
我们通过渐进式的演进,来验证长连接的作用和使用方式,并且学会通过channel掌控并发能力,保护当前服务的资源,包括后端的资源,使我们的服务稳定,健壮.

转载地址:http://ogxui.baihongyu.com/

你可能感兴趣的文章
面试---刷牛客算法题
查看>>
Android下调用收发短信邮件等(转载)
查看>>
Android中电池信息(Battery information)的取得
查看>>
SVN客户端命令详解
查看>>
Android/Linux 内存监视
查看>>
Linux系统信息查看
查看>>
用find命令查找最近修改过的文件
查看>>
Android2.1消息应用(Messaging)源码学习笔记
查看>>
在android上运行native可执行程序
查看>>
Phone双模修改涉及文件列表
查看>>
android UI小知识点
查看>>
Android之TelephonyManager类的方法详解
查看>>
Android 获取屏幕的分辨率
查看>>
android raw读取超过1M文件的方法
查看>>
ubuntu下SVN服务器安装配置
查看>>
MPMoviePlayerViewController和MPMoviePlayerController的使用
查看>>
CocoaPods实践之制作篇
查看>>
[Mac]Mac 操作系统 常见技巧
查看>>
苹果Swift编程语言入门教程【中文版】
查看>>
[转载]CSDN精选iPhone开发博客
查看>>