你很可能遇到过这种情况:脚本正常运行,在网站上进行登录,把商品加入购物车,然后突然切换了 IP,服务器突然表现得像你是第一次访问。用户被登出,购物车空了,令牌失效。烦人吧?那当然。但这个问题有明确的工程解决方案,本指南将从头到尾为你解析。

引言:为什么切换 IP 后会被登出、购物车变空

说实话:当会话在 IP 轮换后失效时,你的第一反应是归咎于地址切换这件事本身。就像说:IP 变了,服务器看到了,就把一切都重置了。有时候确实如此。但大多数情况下,问题并不在于 IP 本身,而在于你的代码实现。它混淆了不同会话的状态,丢失了 cookies,还从新地址发送了旧令牌。我们将学习如何避免这种情况发生。

读者最终将获得什么

到本教程结束时,你将能够做到以下几点。第一,理解服务器端真正与 IP 绑定的是什么,什么只是误解。第二,建立严格的对应规则:一个逻辑会话 = 一套 cookies = 一个 IP。第三,用 Python 和 Node.js 编写可运行的代码,在不同代理之间隔离状态,不让数据混淆。第四,在程序重启之间保存状态,并知道什么时候干脆弃掉它更简单。

本指南适合谁

本指南面向中等水平读者。你已经在用 Python 或 JavaScript 写代码,明白什么是 HTTP 请求,也知道代理的作用。我们不会讨论该选择哪种轮换类型——这有单独的文章。这里我们假设 IP 轮换已经按你的规则在进行,只解决一个任务:如何在轮换过程中不丢失状态。

需要提前了解的知识

了解一些基础概念会有帮助。Cookie 是一小块数据,服务器要求浏览器或客户端记住,并在每次请求时发回来。会话是服务器在多次请求之间识别你的方式。令牌是一串字符串,用来证明你的身份或操作权限。如果这些术语还很模糊,别担心:在基础概念部分,我们会用通俗的语言逐一讲解。

需要多长时间

阅读并理解理论大约需要四十分钟。用你自己的语言环境来解析和运行代码,还需要一到两个小时。通过真实任务进行实验来完全掌握,大约需要两到三个小时。不要着急。与其快速复制一段后来莫名其妙崩溃的代码,不如慢慢弄懂原理。

前期准备:工具和环境

在写代码之前,先把工作环境整理好。这会花一点时间,但能为你之后省下几小时的调试时间。

所需工具

  • Python 3.10 或更高版本——如果你使用 Python。到 2026 年,主流版本是 3.12 和 3.13,但文中所有内容在 3.10 上即可运行。
  • Node.js 20 LTS 或更高版本——如果你使用 JavaScript。22 LTS 也可以。
  • 支持 IP 轮换的代理——你需要能够访问地址池。连接格式通常是:协议、主机、端口、用户名和密码。
  • 代码编辑器——任何都可以,比如带有语法高亮的免费编辑器。
  • 终端或命令行——用于安装库和运行脚本。

系统要求

系统要求很低。任何现代电脑都能胜任。4GB 内存就够用,但如果你打算同时运行大量会话,最好是 8GB 或更多。更关键的是稳定的网络连接,因为网络断开时 cookies 可能无法正确保存。

需要安装什么

对于 Python,你需要安装两个库。打开终端,运行安装 requests 包和代理相关包的命令。写法是:先写包管理器的安装命令,然后是 requests。状态序列化使用标准模块,无需单独安装。

对于 Node.js,你需要安装三个包:用于请求的 axios、用于管理 cookie 存储的 tough-cookie,以及用于通过代理连接的 https-proxy-agent。这三个包可以在项目里用一条安装命令一起安装。

建议:为项目创建一个独立的虚拟文件夹。在 Python 中是虚拟环境,在 Node.js 中是一个带依赖描述文件的独立目录。这样可以避免不同项目的库混在一起,防止版本冲突。

创建备份

如果你已经在把 cookies 保存到文件或数据库,实验前请先做备份。我们会修改序列化逻辑,有可能损坏现有数据。把保存状态的文件夹复制一份,并标记为 backup 即可。

验证:安装完成后,确认一切正常。在终端运行一个简短的命令来查看 Python 或 Node.js 版本。你应该能正常看到版本号,没有错误。然后在交互模式下导入已安装的库——如果导入没有报错,就说明一切就绪。

基础概念:哪些东西真的与 IP 绑定,哪些是误解

这是最重要的理论部分。如果你没搞清楚什么真的与 IP 相关,就只会治错病。我们逐一来看不同类型的数据。

Cookie 会话

Cookie 会话是指服务器以 cookie 的形式给你一个会话标识符,而状态则保存在服务器自己那里。例如,一个名为 sessionid 的 cookie,里面是一长串随机字符。服务器通过这串字符在内存中找到你的记录。这个机制绑定 IP 吗?本身并不绑定。 Cookie 与地址无关。但许多服务会额外增加一层检查:它们记住会话是在哪个 IP 下创建的,如果带有同一个 cookie 的请求来自其他地址,就会认为可疑。这时就会发生登出。

CSRF 令牌

CSRF 令牌用于防御请求伪造。服务器发放令牌,你在提交表单或执行关键操作时必须把它带回来。这个令牌几乎从不绑定 IP。它与会话相关,而不是与地址相关。它出问题的原因在于另外一点:如果你丢失了会话 cookie,那么 CSRF 令牌也就会失效,因为服务器无法把它与会话对应起来。也就是说,这里的问题是次要的——是 cookies 丢失的后果。

JWT

JWT 是一种自带信息并由服务器签名的令牌。客户端保存它,并在每次请求时通过请求头发送。经典的 JWT 完全不绑定 IP。它自包含:服务器只验证签名和有效期,不关心地址。但也存在一些实现,服务器会在自己那边额外保存令牌与 IP 的绑定关系,或者把地址放进令牌内部。这种情况下,切换 IP 会导致校验失败。这不是 JWT 本身的属性,而是具体服务的决定。

服务器会话

服务器会话是服务器在自己的存储中保存状态,并与你的标识关联起来的统称。购物车、浏览历史、登录状态——这些常常都存放在服务器会话里。这里是否绑定 IP 取决于服务的设置。有些服务出于安全考虑,会把会话强绑定到第一个 IP。其他服务则允许地址变化。你通常无法提前知道,所以按“存在绑定”来写代码是更安全的策略。

购物车

购物车是服务器会话或 cookie 的一个典型例子。在简单的商店里,购物车直接以 cookie 形式保存在客户端。在复杂的商店里,则保存在服务器上并与会话关联。如果切换 IP 后购物车变空,说明它绑定在一个会检查地址的服务器会话上。解决方案是一样的:在同一个逻辑会话内不要更换 IP,或者小心地保存好整套 cookies。

小结:IP 真正参与的地方

我们把整体情况梳理一下。cookies、CSRF、JWT 这些 HTTP 机制本身并不绑定 IP。绑定是服务端额外加的检查,你无法控制它。你唯一能控制的,是你这边的会话、cookies 集合和 IP 之间的对应关系。由此引出了本指南的核心规则。

验证:检查一下你的理解。如果切换 IP 后会话失效,但切回旧 IP 后一切恢复,说明服务器在严格检查地址。如果就连切回旧 IP 也无法恢复,那你只是在代码中丢失了 cookies。这是两种不同的诊断,治疗方法也不同。

第 1 步:明确对应规则

本阶段目标:巩固核心原则,并理解如何在代码结构中体现它。

规则很简单:一个逻辑会话 = 一套 cookies = 一个 IP。下面看看它在实践中意味着什么。

  1. 逻辑会话是一串请求,构成一次连续的操作:进入、登录、做某些事、退出。这一切都属于同一个逻辑会话。
  2. 一套 cookies 是一个独立的 cookie 存储,只属于这个逻辑会话,不属于其他任何东西。
  3. 一个 IP——在同一逻辑会话期间,地址不能改变。如果确实发生了轮换,逻辑会话就被视为已结束。

如何用代码表达?非常直观:创建一个容器对象,它内部既包含 cookie 存储,又包含代理设置。只要这个对象存在,逻辑会话就存在。当需要更换 IP 时,我们要么创建一个新容器,要么在服务允许更换地址的情况下,小心地把 cookies 迁移到带有新 IP 的新容器中。

⚠️ 注意:最常见的架构错误是保存一套公共 cookies,然后给它们配上不同的代理。这必定会破坏一切。不同的逻辑会话会互相覆盖 cookies,服务器就会收到相互矛盾的数据。绝对不要这样做。

建议:把逻辑会话想象成一个人。一个人只有一套证件(cookies)和一个家(IP)。不能让两个人共用一套证件,也不能让一个人同时住在两个家里。这个类比能帮你避免大部分错误。

验证:在纸上画出你未来的结构。你应该有多个独立的模块,每个模块都有自己的 cookie 存储和自己的代理。模块之间没有共享数据。如果符合这些,就说明你理解了规则。

第 2 步:Python 实践——每个代理一个 Session

本阶段目标:编写可运行的代码,让每个逻辑会话都有自己的 requests.Session 对象、自己的 CookieJar 和自己的代理,彼此隔离。

在 requests 库中有一个 Session 对象。它本身就是一个容器:内部持有 cookie 存储,并能把设置应用到所有请求上。这是我们逻辑会话的理想基础。

基本结构

  1. 创建一个函数,接收单个代理的数据,返回一个准备好的 Session 对象。
  2. 在函数内部创建一个新的 Session 对象。
  3. 为对象设置 proxies 配置——一个包含 http 和 https 协议代理地址的字典。
  4. 返回该对象。现在你就有了一个隔离的容器。

代码看起来是这样的。逐行说明:导入 requests。定义接收 proxy_url 字符串的 make_session 函数。函数内写 s = requests.Session()。然后 s.proxies = {'http': proxy_url, 'https': proxy_url}。最后 return s。函数就完成了。

为什么这样能隔离状态

每次调用 make_session 都会创建一个全新的对象。这个新对象有自己内部的 cookie 存储,叫作 cookiejar。一个会话收到的 cookies 在物理上不可能进入另一个会话,因为它们是内存中不同的对象。这正是我们想要的效果。

使用会话

  1. 通过调用函数并传入所需代理来获取会话对象。
  2. 通过该对象的方法发起请求:s.get 或 s.post。
  3. 服务器在 Set-Cookie 请求头中发送的 cookies 会自动保存在对象内部。
  4. 之后通过同一个对象发起请求时,这些 cookies 会自动被发送回去。

建议:在同一个逻辑会话内,不要为每个单独请求创建新的 Session。这样 cookies 就无法累积。一个逻辑会话只创建一个对象,并让它处理该会话的所有请求。

线程间的隔离

如果你使用多线程,每个线程必须有自己的 Session 对象。Session 对象不是线程安全的。也就是说,如果两个线程同时向同一个对象写入 cookies,数据可能会损坏。

  1. 使用线程本地数据机制。在 Python 中,这是 threading.local 对象。
  2. 每个线程启动时,为它创建独立的会话,并保存到该线程的本地存储中。
  3. 在线程内部,只访问自己的会话,不要去动其他线程的。

实际操作如下:创建一个全局对象 local = threading.local()。在线程工作开始时,检查 local 是否已有 session 属性。如果没有,就用分配给该线程的代理调用 make_session 来创建它。之后在线程中用 local.session 来处理所有请求。

⚠️ 注意:永远不要把一个 Session 对象作为共享资源在线程间传递。即使看起来请求是依次进行的,调度器也可能在最不合时宜的时候切换线程,导致 cookies 混在一起。每个线程要有自己的对象。

逻辑中的轮换

当 IP 轮换发生、你需要新地址时,可以这样处理。先结束当前逻辑会话:如果服务器强绑定 IP,就直接创建一个带新代理的新 Session 对象,并且从头开始——重新登录。如果服务器允许更换地址,你可以迁移 cookies,这会在“状态存储”那一步中讨论。

验证:启动两个使用不同代理的会话,分别在一个能显示 IP 和 cookies 的测试服务上登录。确认每个会话看到的是自己的 IP 和自己的一套 cookies。如果数据不混淆,说明隔离正确。

第 3 步:在 Node.js 中实现同样效果

本阶段目标:使用 axios、tough-cookie 和代理 agent 在 JavaScript 中构建等效结构。

在 Node.js 生态中没有现成的 Session 级别对象,所以我们要用三个部分来组装它。cookie 存储来自 tough-cookie。通过代理的连接由 agent 负责。请求由 axios 发出。

组装容器

  1. 从 tough-cookie 库导入 CookieJar 类。
  2. 从 https-proxy-agent 库导入创建代理 agent 的函数。
  3. 导入 axios。
  4. 创建一个 makeClient 函数,它接收代理地址并返回配置好的对象。

在函数内部,创建存储的新实例:const jar = new CookieJar()。创建代理 agent,把地址传给它:const agent = new HttpsProxyAgent(proxyUrl)。通过 axios.create 创建 axios 实例,并在配置中传入 httpsAgent: agent 和 httpAgent: agent。

启用自动的 cookie 处理

裸的 axios 无法自动把响应中的 cookies 放入存储,也无法在请求时取出它们。有两种方式。

  1. 第一种:使用现成的封装库,它把 axios 和 tough-cookie 连接起来,通过单独包安装。它会通过传入的 jar 存储自动读写 cookies。
  2. 第二种:通过请求和响应拦截器手动实现。请求前从存储中取出目标地址的 cookies 字符串,放入 Cookie 请求头;响应后读取 Set-Cookie 头,把每条 cookie 写入存储。

建议:刚开始使用现成封装更不容易出错。手动方案留到你需要精细控制流程时再用,比如记录每一条 cookie。

并行任务之间的隔离

Node.js 的模型不同——这里没有线程,而是事件循环中的异步任务。但原则相同:每个逻辑会话都有自己的 client 对象,以及自己专属的 jar 和 agent。

  1. 对每个并行任务单独调用 makeClient。
  2. 将客户端保存在数组或映射中,键为任务标识符。
  3. 绝不要让多个客户端同时共用一个 jar。

⚠️ 注意:在异步代码中,很容易不小心在多个 Promise 链之间共享同一个 client。这样 cookies 就会在逻辑会话间混来混去。始终确保每个链使用自己通过单独调用 makeClient 创建的客户端。

Node.js 中的轮换

逻辑与 Python 相同。当需要新 IP 且服务把会话绑定到地址时,就创建一个带新代理和新空 jar 的 client,然后重新登录。当服务允许更换地址时,把旧 jar 的内容迁移到带新 agent 的新 client 中。

验证:创建两个使用不同代理的客户端。向能够反射 IP 和 cookies 的测试服务发起请求。确认第一个客户端看到的是自己的 IP 和自己的 cookies,第二个客户端看到的是另一个 IP 和自己的 cookies。两者不能有交集。

第 4 步:在程序重启之间保存状态

本阶段目标:学会把 cookies 保存到磁盘,并在下次程序启动时恢复它们,同时注意有效期。

我们经常需要停止脚本,之后再次启动,同时保留登录状态。为此,需要把 cookies 序列化——转换成文本并保存到文件或数据库中。

Python 中的序列化

requests 中的 CookieJar 对象可以通过多种方式保存。最通用的方式是把 cookies 整理成简单的字典,然后写入 JSON 格式。

  1. 通过 s.cookies 遍历会话对象中的所有 cookies。
  2. 对每条 cookie 收集它的名称、值、域、路径和过期时间。
  3. 把这些信息组成字典列表。
  4. 将列表以 JSON 格式写入文件。

恢复时反向操作:读取文件,遍历列表,用设置 cookie 的方法把每条 cookie 添加到新的会话对象中,同时指定域和路径。

建议:把代理标识符或至少一个说明它们属于哪个逻辑会话的标记和 cookies 一起保存。这样你就不会用错误的 IP 恢复别人的 cookies,也不会破坏对应规则。

Node.js 中的序列化

tough-cookie 库内置了序列化方法。jar 对象有一个异步方法,能把整个存储转换成 JSON 对象。反向方法可以从该对象恢复 jar。

  1. 调用存储的序列化方法,得到对象。
  2. 把对象转成字符串并保存到文件。
  3. 启动时读取文件,把字符串解析回对象。
  4. 把对象传给反序列化方法,恢复 jar。

这比 Python 更方便,因为 tough-cookie 自己保存所有必要字段,包括过期时间和安全标志。

Cookie 的生命周期

每条 cookie 都有有效期。有些是会话级 cookie——它们会在浏览器关闭前一直有效,没有明确的日期。有些是持久 cookie——有具体的过期日期。只有还未过期的持久 cookies 才值得保存到磁盘。会话级 cookies 在重启后通常已在服务器端失效。

  1. 保存前检查每条 cookie 的过期日期。
  2. 丢弃已经过期的。
  3. 恢复时再次检查有效期,不要加载已过期的。

什么时候该干脆丢弃状态

不要总是执着于已保存的 cookies。有时候从头开始更快也更可靠。

  • 如果距离上次启动已经过了很久——服务器端的会话很可能已经过期,没有什么可恢复的。
  • 如果用恢复后的 cookies 发出第一个请求,服务器就返回了授权错误——那就丢弃它们并重新登录。
  • 如果你不确定状态文件的完整性——最好从零开始,而不是去调试奇怪的行为。

⚠️ 注意:包含 cookies 和令牌的文件带有访问凭证。要把它们放在安全的地方,不要提交到公共仓库,不要通过不安全的渠道发送。这类文件泄露,就相当于账号访问权限泄露。

验证:保存状态,完全关闭程序,重新启动,恢复状态,然后发起一个需要登录的请求。如果服务器像是面对已登录用户那样响应,说明保存有效。如果被登出,就检查过期时间以及域和路径的恢复是否正确。

第 5 步:常见架构错误分析

本阶段目标:详细解析三种主要错误,让你在自己的代码中认出它们并避免。

错误一:所有代理共用一个 CookieJar

这是万恶之源。开发者创建一个 cookie 存储,然后给它配上不同的代理,以为能省事。实际发生的是:第一个 IP 上的会话 cookies 被带到了第二个 IP 的请求中。服务器看到一个在其他地址下创建的 cookie,于是要么重置会话,要么认为行为可疑。

解决方案很简单:每个代理要有自己的 cookie 存储。没有例外。我们已经在代码步骤中体现了这一点:每个逻辑会话都有自己的 Session 或 client,以及独立的 jar。

错误二:并行请求中的竞态

竞态是指两个操作同时访问同一数据并相互干扰。如果两个线程写入同一个 CookieJar,一次写入可能覆盖另一次。结果是:部分 cookies 会随机丢失,而且这种 bug 时好时坏,非常难以调试。

  1. 按照我们说的,通过线程本地数据为每个线程提供自己的会话。
  2. 在异步代码中,不要在同级任务链之间共享同一个客户端。
  3. 如果出于某种原因对象确实是共享的,就使用锁,确保同一时间只有一个线程在操作它。

建议:避免竞态的最好方法就是根本不使用共享的可变数据。按逻辑会话隔离能从根本上解决问题:如果数据不共享,就不可能有竞态。

错误三:重定向时丢失 Set-Cookie

当服务器返回重定向响应时,通常会在同一个响应中通过 Set-Cookie 头设置重要的 cookies。有些客户端配置在自动跟随重定向时会丢失这些 cookies——它们不会进入存储。

  1. 确保你的客户端在重定向链的每一步都保存 cookies,而不仅仅是在最终响应时。
  2. 在 requests 中,使用 Session 对象时默认就是这样做的——cookies 会一路收集。检查一下你是否在没必要的情况下关闭了跟随重定向。
  3. 在 axios 中手动处理 cookies 时,要处理每个中间响应的 Set-Cookie。如果使用封装库,请确认它能拦截重定向。

⚠️ 注意:如果登录成功,但下一个请求就掉线,常见原因就是在重定向时丢失了某条 cookie。开启对所有 Set-Cookie 头的日志记录,检查所有预期的 cookies 是否都到达了存储。

验证:在目标服务上找一个会触发重定向的操作,比如通过表单登录。跟踪整个链路,比较每一步之后的 cookies。所有服务器发放的 cookies 都应该出现在你的存储中。如果有丢失,你就找到了泄漏点。

第 6 步:把一切整合成工作流

本阶段目标:把你学到的内容整合成一个从开始到轮换都可预测的工作流。

现在你已经掌握了所有细节。让我们把它们组合成一条可以反复执行的动作链。

  1. 从代理池中取出一个代理,为它创建隔离的逻辑会话——在 Python 中是 Session,在 Node.js 中是 client。
  2. 如果该逻辑会话有已保存的状态且未过期,就恢复 cookies。否则重新登录。
  3. 通过该会话对象执行所需请求。cookies 会自动累积。
  4. 定期把状态保存到磁盘,避免故障时丢失进度。
  5. 当需要轮换 IP 时,正确地结束逻辑会话。
  6. 如果服务强绑定 IP,就在新地址上从零开始新的逻辑会话。
  7. 如果服务允许更换地址,就创建带新代理的新容器,并把旧容器中的 cookies 迁移过去。

建议:记录事件日志:会话何时创建、使用哪个 IP、何时发生轮换、是否出现登出。这样的日志能在五分钟内帮你发现规律,否则你可能要找好几个小时。

验证:跑一遍完整流程:创建会话、登录、执行几个操作、保存、轮换、继续。确保每一步的状态都可预测,并且只有在你预期时才会发生登出。

结果检查:最终清单

逐一核对这份清单。如果所有项目都满足,说明你的系统工作正常。

  • 每个逻辑会话都有独立的容器对象,带自己的 cookie 存储。
  • 每个容器在整个会话生命周期内只绑定一个代理。
  • 没有任何地方能让一个会话的 cookies 进入另一个会话。
  • 多线程代码中,每个线程通过线程本地数据使用自己的会话。
  • 异步代码中,每条独立链都有自己的客户端。
  • cookies 在重定向的所有步骤中都能被正确收集。
  • 状态在两次启动之间可以保存和恢复,并考虑过期时间。
  • 更换 IP 时,逻辑会话要么重新开始,要么有意迁移 cookies。
  • 包含 cookies 和令牌的文件受到安全保护。

如何测试

  1. 找一个能显示当前 IP 和收到的 cookies 的测试服务。
  2. 创建两个使用不同代理的会话,确认数据完全隔离。
  3. 登录、保存状态、重启程序、恢复状态——验证登录仍然有效。
  4. 模拟轮换,并观察会话的行为。

成功的标准:不同会话的数据绝不混合;只有在服务器强绑定 IP 时才会登出;恢复的状态可以正常工作;并行工作时没有竞态。

典型错误与解决方法

问题:切换 IP 后用户被登出。原因:服务器将会话强制绑定到地址。解决:在同一个逻辑会话中不要更换 IP;如果必须更换,就在新地址上重新开始会话。

问题:不同会话的 cookies 混在一起。原因:多个代理共用一个 CookieJar。解决:给每个逻辑会话单独的存储,永远不要共享。

问题:并行工作时 bug 时有时无。原因:多个线程向同一对象写入时发生竞态。解决:通过线程本地数据按线程隔离会话,或使用锁。

问题:登录成功但很快失效。原因:重定向时丢失了 cookie。解决:检查重定向链所有步骤中的 cookies 收集,并启用 Set-Cookie 日志。

问题:恢复的状态无法使用。原因:保存了已过期或会话级 cookies,或者域和路径不正确。解决:只保存仍然有效的持久 cookies,并用正确的域和路径恢复。

问题:CSRF 令牌一直无效。原因:令牌所绑定的会话 cookie 丢失。解决:先恢复会话 cookies 的完整性,令牌会自动关联上。

问题:轮换后 JWT 无法使用。原因:具体服务在它们那边把令牌绑定到了 IP。解决:在令牌有效期内不要更换 IP,或者在新地址上获取新令牌。

扩展功能与优化

当基础方案运行正常后,你可以让它变得更强。

现成会话池

与其每次都创建会话,不如维护一个预先准备并已登录的逻辑会话池,每个会话都有自己的代理。取一个空闲会话使用,用完再放回池中。这样能加快速度,因为不需要重复登录。

自动存活检查

添加一个函数,向服务发送轻量请求,检查会话是否仍然有效。如果服务器像面对未登录用户那样响应,就会将会话标记为需要重新授权。这样你就能在过期问题破坏重要操作之前发现它。

建议:存活检查不要放在每个请求之前,而是按计划或长时间停顿后进行。过于频繁的检查只会增加无谓的负载,没有实际好处。

集中式状态存储

对于大型项目,不使用文件,而是用数据库作为 cookie 存储。键是逻辑会话标识符,值是序列化后的状态。这样更容易扩展,也存储得更安全。

指标与可观测性

统计登出发生的次数、需要重建多少会话、恢复成功的频率。这些数字能反映系统的健康状况,并提示哪些地方配置得不理想。

FAQ:常见问题

每次更换 IP 都必须创建新的会话对象吗?如果服务严格检查地址——是的,因为旧会话在新 IP 上本来就不会被接受。如果服务允许更换——可以把 cookies 迁移到带新代理的新容器,然后继续。

多个逻辑会话可以共用一个代理吗?从代码层面可以,但每个逻辑会话仍然必须有自己的独立 cookie 存储。只有连接设置可以共享,状态不能。

为什么不能把所有 cookies 放在一处,然后按域过滤?因为问题不在于域,而在于与具体逻辑会话和 IP 的绑定。同一个域上,一个会话的 cookies 绝不能进入另一个会话。

如何判断服务是否把会话绑定到 IP?做一个实验:在一个 IP 上登录,更换地址并发起请求。如果被登出,说明可能存在绑定。再切回旧 IP:如果恢复了,说明服务器记住了第一个地址。

保存到磁盘时该怎么处理会话级 cookies?通常没有必要保存它们,因为服务器在连接断开后会认为它们无效。保存仍然有效的持久 cookies 即可。

如果库本身在重定向时不保存 cookies 怎么办?在重定向链的每个中间响应中手动处理 Set-Cookie 头,并把 cookies 放入自己的存储。

如果每个线程都有自己的会话,还需要锁吗?不需要。如果数据不共享,就不可能有竞态,也不需要锁。只有在必须共同访问同一对象时才需要锁。

可恢复状态能保存多久?直到服务器认为会话仍然有效为止。确切时长取决于服务。更实际的做法是用请求来检查会话是否存活,而不是猜测时间。

保存 cookies 还是保存令牌更重要?这取决于服务的授权机制。有时只有令牌就够了,有时需要一整套 cookies。更安全的是保存完整状态,这样就不会漏掉需要的内容。

cookies 能在 Python 和 Node.js 之间迁移吗?可以,只要你以通用的中性格式保存它们,比如包含名称、值、域、路径和过期时间的 JSON。这样两种系统都能读取。

结论

我们来总结一下你所经历的内容。你明白了切换 IP 后被登出,并不是因为切换这件事本身,而是由于服务器检查和自身架构中的错误。你知道了 cookies、CSRF、JWT 本身并不绑定地址,绑定是由具体服务添加的。你掌握了一条核心原则:一个逻辑会话 = 一套 cookies = 一个 IP。

然后你编写了可运行的代码。在 Python 中,通过为每个代理创建独立的 Session 对象和 CookieJar,并在线程之间进行隔离。在 Node.js 中,通过 axios、tough-cookie 和代理 agent 的组合,为每个逻辑会话创建独立的客户端。你学会了在重启之间保存状态、考虑 cookies 的有效期,并判断什么时候干脆丢弃状态。

你剖析了三种隐蔽的错误:共用的 CookieJar、并行工作时的竞态,以及重定向时 Set-Cookie 的丢失。现在你有了上线前的检查清单,它能防止你发布一个不成熟的产品。

接下来做什么

从小处着手。选一个真实场景,为它实现隔离的逻辑会话,确认状态不会丢失。然后加入磁盘保存。再扩展到多个会话。一步一步前进,每一步都检查结果。

还可以往哪些方向发展

接下来,你可以在可观测性方面深入:配置会话存活指标和事件日志。然后研究在服务允许更换地址时如何有意识地迁移状态。最后,构建一个现成会话池来加快速度。每一步都会让你的系统更加健壮、更可预测。你一定能做到——你已经理解了原理,剩下的就是练习。