<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>并发 on Leanku 的博客</title><link>https://www.leanku.com/tags/%E5%B9%B6%E5%8F%91/</link><description>Recent content in 并发 on Leanku 的博客</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Wed, 10 Jul 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://www.leanku.com/tags/%E5%B9%B6%E5%8F%91/index.xml" rel="self" type="application/rss+xml"/><item><title>一个 HTTP 请求的并发链路：从 Web Server 到 MySQL</title><link>https://www.leanku.com/post/computerscience/http-request-concurrency-chain-webserver-mysql/</link><pubDate>Wed, 10 Jul 2024 00:00:00 +0000</pubDate><guid>https://www.leanku.com/post/computerscience/http-request-concurrency-chain-webserver-mysql/</guid><description>&lt;h1 id="一个-http-请求的并发链路从-web-server-到-mysql"&gt;一个 HTTP 请求的并发链路：从 Web Server 到 MySQL&lt;/h1&gt;&#10;&lt;p&gt;前面的文章分别讨论了并发是什么、进程线程协程如何执行请求、IO 多路复用如何管理大量连接，以及如何用 QPS、并发数和响应时间衡量系统。&lt;/p&gt;&#10;&lt;p&gt;但这些概念真正进入后端系统之后，会形成一条完整的链路：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Client&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Web Server / Nginx&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Application&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Process / Thread / Coroutine&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Redis / RPC / MySQL&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Response&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Client&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;真正的高并发问题，通常也不是发生在某一个组件上，而是发生在&lt;strong&gt;整个链路的容量不匹配&lt;/strong&gt;上。&lt;/p&gt;&#10;&lt;p&gt;例如：&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;Web Server 可以接收 10000 个连接，应用可以运行 1000 个协程，但 MySQL 连接池只有 50 个连接。&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;这并不意味着系统只能处理 50 个 HTTP 请求，但意味着&lt;strong&gt;同时进行数据库操作的请求最多受到这 50 个连接的约束&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;p&gt;因此，理解高并发，不能只看 Web Server 或应用服务器，而要沿着一次请求完整地看下去。&lt;/p&gt;&#10;&lt;h2 id="1-一个-http-请求到底经过哪些组件"&gt;1. 一个 HTTP 请求到底经过哪些组件？&lt;/h2&gt;&#10;&lt;p&gt;假设用户访问：&lt;/p&gt;</description></item><item><title>IO 多路复用：一个线程为什么可以同时处理大量 HTTP 请求？</title><link>https://www.leanku.com/post/computerscience/io-multiplexing-epoll-event-loop/</link><pubDate>Thu, 20 Jun 2024 00:00:00 +0000</pubDate><guid>https://www.leanku.com/post/computerscience/io-multiplexing-epoll-event-loop/</guid><description>&lt;h1 id="io-多路复用一个线程为什么可以同时处理大量-http-请求"&gt;IO 多路复用：一个线程为什么可以同时处理大量 HTTP 请求？&lt;/h1&gt;&#10;&lt;p&gt;我们知道，一个 Web 服务同时面对大量 HTTP 请求时，并不一定需要：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;1000 个请求&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;1000 个线程&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;对于 IO 密集型服务，更常见的做法是让执行单元在等待网络 IO 时不要一直阻塞，然后利用事件机制处理其他连接。&lt;/p&gt;&#10;&lt;p&gt;这就引出了一个非常重要的概念：&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;IO 多路复用（I/O Multiplexing）。&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;它是理解 Linux 网络服务器、Nginx、Node.js、Swoole、Hyperf 等高并发网络程序的基础。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="1-先从一个-socket-开始"&gt;1. 先从一个 Socket 开始&lt;/h2&gt;&#10;&lt;p&gt;一个 HTTP 服务器，本质上需要处理大量网络连接。&lt;/p&gt;&#10;&lt;p&gt;例如服务器监听：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;0.0.0.0:80&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;客户端建立 TCP 连接后，操作系统会为连接提供对应的 Socket。&lt;/p&gt;&#10;&lt;p&gt;可以简单理解为：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Client A ─── Socket A ──┐&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Client B ─── Socket B ──┤&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Client C ─── Socket C ──┤&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Client D ─── Socket D ──┤&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▼&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Web Server&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;假设现在有：&lt;/p&gt;</description></item><item><title>进程、线程、协程：后端服务到底是怎么处理并发请求的？</title><link>https://www.leanku.com/post/computerscience/process-thread-coroutine-concurrent-request/</link><pubDate>Mon, 17 Jun 2024 00:00:00 +0000</pubDate><guid>https://www.leanku.com/post/computerscience/process-thread-coroutine-concurrent-request/</guid><description>&lt;h1 id="进程线程协程后端服务到底是怎么处理并发请求的"&gt;进程、线程、协程：后端服务到底是怎么处理并发请求的？&lt;/h1&gt;&#10;&lt;p&gt;在上一篇 &lt;a href="https://www.leanku.com/post/computerscience/understanding-concurrency-http-request"&gt;《理解并发：一个 HTTP 请求到后端到底发生了什么？》&lt;/a&gt; 我们从一个 HTTP 请求出发，理解了并发最基本的概念：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;HTTP 请求&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Web Server&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;后端服务&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;CPU / IO&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;MySQL / Redis / RPC&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;HTTP 响应&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但这里留下了一个非常重要的问题：&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&lt;strong&gt;后端服务到底由谁来执行这些请求？&lt;/strong&gt;&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;如果同时来了 1000 个 HTTP 请求，服务器是不是创建 1000 个进程？&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;如果不是，是不是创建 1000 个线程？&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;如果也不是，那么一个线程又是怎么处理大量请求的？&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;要回答这些问题，就必须理解后端程序最核心的三个执行概念：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;进程&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;线程&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;协程&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#10;&lt;h2 id="1-先建立一个整体认识"&gt;1. 先建立一个整体认识&lt;/h2&gt;&#10;&lt;p&gt;可以先把三者理解成三个不同层次的概念：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;进程&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├── 线程&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ │&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ ├── 协程&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ ├── 协程&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ └── 协程&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └── 线程&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但这里有一个需要特别注意的地方：&lt;/p&gt;</description></item><item><title>理解并发：一个 HTTP 请求到后端到底发生了什么？</title><link>https://www.leanku.com/post/computerscience/understanding-concurrency-http-request/</link><pubDate>Mon, 10 Jun 2024 00:00:00 +0000</pubDate><guid>https://www.leanku.com/post/computerscience/understanding-concurrency-http-request/</guid><description>&lt;h1 id="理解并发一个-http-请求到后端到底发生了什么"&gt;理解并发：一个 HTTP 请求到后端到底发生了什么？&lt;/h1&gt;&#10;&lt;p&gt;在开发后端系统时，我们经常会看到这样的描述：&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;“这个接口支持 1000 并发。”&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;但“1000 并发”到底意味着什么？&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;是不是意味着服务器同时启动 1000 个线程？&lt;/li&gt;&#10;&lt;li&gt;是不是意味着 CPU 同时执行 1000 个请求？&lt;/li&gt;&#10;&lt;li&gt;如果一个请求正在等待 MySQL，CPU 又在做什么？&lt;/li&gt;&#10;&lt;li&gt;一个 PHP/Hyperf 服务为什么可以同时处理大量 HTTP 请求？&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;要真正理解高并发，不能一开始就从 QPS、限流、缓存这些概念入手，而应该先理解一个最基本的问题：&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;一个 HTTP 请求进入后端以后，到底发生了什么？&lt;/strong&gt;&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="1-先理解什么是并发"&gt;1. 先理解什么是并发&lt;/h2&gt;&#10;&lt;p&gt;&lt;strong&gt;并发&lt;/strong&gt;（Concurrency）描述的是：&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;多个任务在同一个时间段内处于进行状态，并且它们的执行过程存在交错。&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;并发和并行（Parallelism）不是同一个概念。&lt;/p&gt;&#10;&lt;p&gt;例如：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;任务 A：████████&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;任务 B： ████████&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;两个任务都在进行，但可能只有一个 CPU 核心。&lt;/p&gt;&#10;&lt;p&gt;CPU 可以先执行 A：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;A A A A&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后因为 A 正在等待 IO，转而执行 B：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;A A → 等待&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; B B B B&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这属于&lt;strong&gt;并发&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;p&gt;如果机器有多个 CPU 核心：&lt;/p&gt;</description></item></channel></rss>