异步、并发、协程原理
发布于
io

进程、线程、协程的特点及区别

支持协程的语言

适合使用协程的场景

协程与异步和并发的联系

惊群
发布于
io

多个进程/线程同时阻塞等同一个事件时,事件发生后,唤醒所有的进程

但最终只可能有一个进程/线程对该事件进行处理,其他在失败后重新休眠,这种性能浪费就是惊群

accept 惊群

主进程创建socket, bind, listen后,fork出多个子进程都循环处理(accept)这个socket

每个进程都阻塞在accpet上,当一个新的连接到来时,所有的进程都会被唤醒,但其中只有一个进程会accept成功,其余皆失败,重新休眠。这就是accept惊群

fork出多个进程是为了利用多核CPU

内核早解决这问题了

多个进程/线程都阻塞在对同一个 socket 的 accept 调用上时,新连接到来,内核只唤醒一个进程,其他进程保持休眠,压根就不会被唤醒

epoll惊群

accept 已无惊群问题,但 epoll 还有

即,如果多个进程/线程阻塞在监听同一个 listening socket fd 的 epoll_wait 上,当有一个新的连接到来时,多个子进程被唤醒

为什么内核不处理 epoll 惊群

accept 应该只能被一个进程调用成功,内核很清楚这一点

但 epoll 监听的fd,除可能被 accept 调用外,还有可能是其他网络 IO 事件的

其他 IO 事件是否只能由一个进程处理(如一个文件会由多个进程来读写),得用户决定,内核不能强制

所以,对 epoll 的惊群,内核则不予处理

Actor VS Thread VS Coroutine
发布于
concurrent

大规模并发服务的技术,归纳起来就是两种方式:

  • thread per client,用blocking I/O

  • 多个clients一个thread,用nonblocking I/O或asynchronous I/O

Linux asynchronous I/O还不好,一般都是用nonblocking I/O

多数都是用epoll()的edge triggering(select()性能问题)

aio
发布于
io

宏观与微观角度看epoll+nonblock

宏观角度可以叫做全异步

微观角度还是同步 IO(数据到达后得到系统通知,然后同步执行 recv 取回数据,没有 iowait)

真正的异步 IO(AIO)应像 Windows IOCP 一样,传入文件句柄,缓存区,尺寸等参数和一个函数指针,当os真正完成了 IO 操作,再执行对应的函数

  1. 实际上 socket ,epoll 已经是最高效的模型了,虽然比 AIO 多一次 recv 系统调用,但总体来看没有任何 IO 等待,效率很高

  2. epoll 天然 reactor 模型,程序实现更容易。AIO 如 windows 的 IOCP,异步回调的方式,开发难度很高

为什么还是需要 AIO

文件句柄跟 socket 完全不同,它总是处于可读状态,不能 epoll+nonblock 实现异步化

在 epoll 的全异步 Server 中,读写文件必须得用 AIO

python aop
发布于
python aop

动态语言提供了在运行时改变程序结构的能力,有时候会提供巨大的方便

函数调用拦截是aop的基础

class Target:
    def targetFunc(self):
        print ("targetFunction")

temp=Target.targetFunc

def foo(self):
    print ("before call")
    temp(self)
    print ("after call")

Target.targetFunc=foo

t=Target()
t.targetFunc()

对调用者来说根本就不知道函数已经给动了手脚

用Java, C++来实现就麻烦些了

tcp_tw_reuse、tcp_tw_recycle
发布于
network

tcp 使用场景,一般区分为“长连接”和“短连接”

如何解决tcp短连接的TIME_WAIT问题

短连接最大缺点:占用资源(如:本地端口、socket句柄)


正常的TCP client关闭后,进入TIME_WAIT,持续的时间一般在1~4分钟

每秒建1000个短连接(Web请求访问memcached)

假TIME_WAIT时间是1分钟,1分钟内需要建立6W个短连接,这些短连接1分钟内都处于TIME_WAIT状态,都不会释放

Linux默认本地端口范围:net.ipv4.ip_local_port_range = 32768 61000

不到3W,由于没有本地端口就不能建立了

为什么linux下epoll是最好,netty要比nio.2好
发布于
io netty

基本的 IO(包括网络和文件) 编程过程

  • 打开文件描述符(windows:handler,java:stream 或 channel)
  • 多路捕获 IO 可读写状态(Multiplexe:select/poll/epoll)
  • 对可读写的 FD 进行 IO 读写

IO 设备速度比CPU、内存慢,开多线程更好利用 CPU和内存,每个线程读写一个fd

C10K

海量网络连接下,瓶颈不在机器设备和网络速度,在于os和 IO 应用程序的沟通协作的方式

一万个 socket 连接过来,传统阻塞式 IO 编程模型要开一万个线程来应对

一万个线程要不断的关闭线程重建线程,资源都浪费在这上面了

一个线程耗 1M 内存,1 万个线程至少10G ,IA-32 机器架构基本不可能的(要开 PAE),x64 架构才有可能舒服点,这仅仅是粗略算的内存消耗,还有别的资源

高性能网络编程(即 IO 编程)

一,需要解耦 IO 连接和应用程序线程的对应关系,这就是非阻塞(nonblocking)、 异步(asynchronous)要求的由来

(构造一个线程池,epoll 监控到有数据的 fd,把 fd 传入线程池,由这些 worker thread 来读写 io)

二,需要高性能的 OS 对 IO 设备可读写(数据来了)的通知方式:

从 level-triggerednotification 到 edge-triggered notification

多重继承
发布于
lang pattern

继承是面向对象编程的一个重要的方式,通过继承,子类就可以扩展父类的功能

Animal类层次设计

4 种动物:Dog(狗)、Bat(蝙蝠)、Parrot(鹦鹉)、Ostrich(鸵鸟)

哺乳动物鸟类归类,类层次:

1

能跑能飞 归类,类层次

2

要把上面的两种分类都包含进来,就得设计更多的层次

哺乳类:能跑的哺乳类,能飞的哺乳类

鸟类:能跑的鸟类,能飞的鸟类

这么一来,类的层次就复杂了:

3

如果要再增加 “宠物类” 和 “非宠物类”,这么搞下去,类数量指数增长(类爆炸)