如何在 Ubuntu VPS 上从 Apache Web 服务器迁移到 Nginx
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。
简介
在启动网站或应用程序时,您需要做出许多选择。有时,您的需求会发生变化,新技术变得可行,或者您的用户群会意外膨胀。无论您的原因是什么,您可能会考虑更改应用程序堆栈的组件之一,即 Web 服务器。
尽管 Apache Web 服务器目前是世界上最流行的 Web 服务器,但 Nginx 正以迅猛的速度获得市场份额。这并不令人意外,考虑到 Nginx 在使用少量资源的同时表现出色。对于许多网站来说,迁移到 Nginx 可以提高性能。
在本指南中,我们将讨论如何将网站从 Apache 迁移到 Ubuntu 12.04 VPS 上的 Nginx。我们将尽量保持建议的通用性,但会为您提供一些可能需要根据自己的目的进行调整的提示。
本指南假定您已经使用此教程安装了 LAMP(Linux、Apache、MySQL 和 PHP)堆栈。如果您在创建 droplet 时只是选择了一键 LAMP 映像,则您的服务器也将具有此配置。
安装 Nginx
我们要做的第一件事是安装新的服务器软件,这将使我们能够通过查看当前的 Apache 配置文件来配置新的服务器。
幸运的是,Nginx 默认情况下存在于 Ubuntu 仓库中。让我们现在安装它:
sudo apt-get update
sudo apt-get install nginx
在我们的用例中,一个非常重要的实现细节是,Nginx 将任何动态处理都转移到一个单独的进程。这使得 Nginx 保持精简和快速。它可以专注于其核心功能,而无需尝试通过模块添加 PHP 支持。相反,它只是将其转移到专为此目的构建的应用程序。
这一点是在此时提到的,是因为我们还需要安装一个 PHP 处理程序,以便处理 PHP 脚本。标准选择是 php5-fpm
,它与 Nginx 配合良好:
sudo apt-get install php5-fpm
您应该已经拥有了所有需要的软件来将您的站点切换到 Nginx。我们仍然需要配置我们的软件以模拟 Apache 运行的配置。
设置测试 Nginx 配置
由于我们当前正在运行 Apache,如果可能的话,我们希望独立于 Apache 配置我们的 Nginx 服务器,以便在过渡期间我们的站点仍然可以正常运行。
这只是在另一个端口上测试 Nginx,直到我们准备好巩固我们的更改。这样,我们可以同时运行两个服务器。
首先打开默认 Nginx 站点的配置文件:
sudo nano /etc/nginx/sites-available/default
在 server {
部分,添加一个 listen 指令,告诉 Nginx 监听除端口 80 以外的端口(Apache 仍在使用端口 80 来提供请求)。在我们的教程中,我们将使用端口 8000。
server {listen 8000;. . .. . .
保存并关闭文件。现在是时候进行抽查,看看我们是否可以访问我们的 Nginx 服务器。启动 Nginx 来测试:
sudo service nginx start
使用我们配置的端口号访问默认的 Nginx 配置。在浏览器中输入:
http://your_ip_or_domain:8000
您的 Apache 实例应该仍在默认端口 80 上运行。您也可以通过访问您的站点(末尾没有 :8000
,我们的示例只是提供默认的 Apache 页面。如果您已经配置了您的网站,那么这里将会是您的网站)来检查:
http://your_ip_or_domain
翻译您的 Apache 配置
现在您已经运行了两个服务器,您可以开始迁移和翻译您的 Apache 配置,以便在 Nginx 中使用。这必须手动完成,因此重要的是您了解 Nginx 的配置方式。
这项任务主要涉及编写 Nginx 服务器块,这类似于 Apache 虚拟服务器。Apache 将这些文件保存在 /etc/apache2/sites-available/
,而 Nginx 也遵循相同的做法,在 Ubuntu 上将其服务器块声明保存在 /etc/nginx/sites-available/
中。
对于每个虚拟服务器声明,您将创建一个服务器块。如果您查看您的 Apache 文件,您可能会发现类似于以下内容的虚拟主机:
<VirtualHost *:80>ServerAdmin webmaster@your_site.comServerName your_site.comServerAlias www.your_site.comDocumentRoot /var/www<Directory />Options FollowSymLinksAllowOverride None</Directory><Directory /var/www/>Options Indexes FollowSymLinks MultiViewsAllowOverride NoneOrder allow,denyallow from all</Directory>ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/<Directory "/usr/lib/cgi-bin">AllowOverride NoneOptions +ExecCGI -MultiViews +SymLinksIfOwnerMatchOrder allow,denyAllow from all</Directory>ErrorLog ${APACHE_LOG_DIR}/error.logLogLevel warnCustomLog ${APACHE_LOG_DIR}/access.log combinedAlias /doc/ "/usr/share/doc/"<Directory "/usr/share/doc/">Options Indexes MultiViews FollowSymLinksAllowOverride NoneOrder deny,allowDeny from allAllow from 127.0.0.0/255.0.0.0 ::1/128</Directory>
</VirtualHost>
我们可以开始使用此配置构建我们的 Nginx 服务器块。
在您的 /etc/nginx/sites-available/
目录中,打开我们之前编辑以声明 Nginx 端口的文件:
sudo nano /etc/nginx/sites-available/default
暂时忽略注释行,它应该看起来像这样:
server {listen 8000;root /usr/share/nginx/www;index index.html index.htm;server_name localhost;location / {try_files $uri $uri/ /index.html;}location /doc/ {alias /usr/share/doc/;autoindex on;allow 127.0.0.1;deny all;}
}
您应该已经开始看到一些似乎对应于我们的 Apache 配置的项目。一般来说,主要指令的翻译如下:
Apache Nginx
------ ------<VirtualHost *:80> server {listen 80;ServerName yoursite.com
ServerAlias www.yoursite.com server_name yoursite.com www.yoursite.com;DocumentRoot /path/to/root root /path/to/root;AllowOverride All (No Available Alternative)DirectoryIndex index.php index index.php;ErrorLog /path/to/log error_log /path/to/log error;CustomLog /path/to/log combined access_log /path/to/log main;Alias /url/ "/path/to/files" location /url/ {
<Directory "/path/to/files"> alias /path/to/files;
如果我们要创建一个模拟上述虚拟主机文件功能的服务器块,它可能看起来像这样:
server {listen 8000; # 我们故意保留此设置,以避免冲突root /var/www;server_name your_site.com www.your_site.com;location / {try_files $uri $uri/ /index.html;}location ~ \.php$ {fastcgi_split_path_info ^(.+\.php)(/.+)$;fastcgi_pass unix:/var/run/php5-fpm.sock;fastcgi_index index.php;include fastcgi_params;}location /doc/ {alias /usr/share/doc/;autoindex on;allow 127.0.0.1;deny all;}location ~/\.ht {deny all;}}
我们已经删除了一些项目,并添加了一些额外的行,我们应该解释一下。
首先,错误日志行已从配置中删除。这是因为它们已经在 /etc/nginx/nginx.conf
文件中定义。这为我们提供了一个默认值,我们将使用它。
我们还删除了 ServerAdmin
指令,因为 Nginx 不会将该信息嵌入其错误页面。
PHP 处理也有所改变。由于 PHP 在 Nginx 中是单独处理的,我们将这些文件传递给我们之前安装的 php-fpm
程序。这是通过一个套接字实现的(我们需要立即配置)。
文档部分已更改以反映 Nginx 文档。它在其他方面基本上是类似的。
最后,我们配置 Nginx 拒绝访问任何以 .ht
开头的 .htaccess
或其他文件。这些是 Apache 特定的配置文件,它们与 Nginx 不兼容。最好不要公开这些配置文件。
完成后保存并关闭文件。
我们必须重新启动我们的 Nginx 服务器,以便识别这些更改:
sudo service nginx restart
配置 PHP-FPM
现在我们已经大部分完成了 Nginx 配置,我们需要修改 php-fpm 配置以便使用我们指定的通道进行通信。
首先,我们应该修改 php.ini 文件,以便它不会以不安全的方式提供文件。
sudo nano /etc/php5/fpm/php.ini
我们需要修改的行将要求 PHP 服务于精确请求的文件,而不是猜测是否存在不完整的匹配。这可以防止 PHP 可能向正在探测 PHP 处理程序弱点的人提供或暴露敏感数据。
找到指定 cgi.fix_pathinfo
指令的行,并修改为:
cgi.fix_pathinfo=0
保存并退出该文件。
接下来,我们将更改 php-fpm 连接到服务器的方式。在编辑器中打开以下文件:
sudo nano /etc/php5/fpm/pool.d/www.conf
找到并修改 listen
指令,使其与我们在服务器块配置文件中设置的值匹配:
listen = /var/run/php5-fpm.sock
如果您在处理大量 PHP 请求时遇到问题,您可能需要回来增加可以同时生成的子进程数量。您需要更改的行是:
pm.max_children = Num_of_children
保存并关闭该文件。
现在,我们的 php-fpm 程序应该已经正确配置。我们需要重新启动它以使更改生效。
sudo service php5-fpm restart
再次重新启动 Nginx 也不会有问题:
sudo service nginx restart
测试您在根目录中拥有的任何 PHP 文件是否能够正常运行。您应该能够像在 Apache 中一样执行 PHP 文件。
如果我们访问在 Ubuntu LAMP 教程中创建的 info.php
文件,它应该呈现如下:
http://your_ip_or_domain:8000/info.php
!DigitalOcean Nginx PHP files
在 PHP Variables 部分,您应该看到 Nginx 被列为 “SERVER_SOFTWARE” 变量:
!DigitalOcean Nginx server software
迁移您的 Nginx 站点
在进行了广泛的测试之后,您可以尝试将您的站点从 Apache 无缝迁移到 Nginx。
这是可能的,因为这两个服务器在重新启动之前都不会实施更改。这使我们能够设置好一切,然后在一个瞬间切换。
实际上,我们唯一需要做的就是修改 Nginx 服务器块中的端口。现在打开该文件:
sudo nano /etc/nginx/sites-available/default
将端口更改回默认的端口 80。这将使其在重新启动后立即开始接受常规的 HTTP 流量。
server {# listen 8000;listen 80;. . .
保存并关闭该文件。
如果您只是将一些站点迁移到 Nginx 并继续从 Apache 提供一些内容,您需要禁用在端口 80 上提供请求的 Apache 虚拟服务器。这是必要的以避免冲突。如果这样做得不正确,Nginx 将无法启动,因为端口已被占用。
如果您计划继续运行 Apache,请检查以下文件和位置以查找端口 80 的使用情况:
/etc/apache2/ports.conf
/etc/apache2/apache2.conf
/etc/apache2/httpd.conf
/etc/apache2/sites-enabled/ ## 在该目录中搜索所有站点
在您确信已经更改了所有必要的端口后,您可以像这样重新启动两个服务:
sudo service apache2 reload && sudo service nginx reload
Apache 应该重新加载,释放端口 80。随后,Nginx 应该重新加载并开始接受该端口上的连接。如果一切顺利,您的站点现在应该由 Nginx 提供服务。
如果您不再打算使用 Apache 来提供站点的任何部分,您可以完全停止其 Web 进程:
sudo service apache2 stop && sudo service nginx reload
如果您不再使用 Apache,那么此时您可以卸载 Apache 文件。您可以通过输入以下命令轻松找到与 Apache 相关的文件:
dpkg --get-selections | grep apache
然后使用 apt-get 卸载它们。例如:
sudo apt-get remove apache2 apache2-mpm-prefork apache2-utils apache2.2-bin apache2.2-common libapache2-mod-auth-mysql libapache2-mod-php5
您还可以删除所有不再需要的依赖包:
sudo apt-get autoremove
迁移复杂性
在尝试切换到 Nginx 时,Apache 世界中有一些常见的事情可能会让您感到困惑。
重写转换和 .htaccess 文件
最基本的区别之一是 Nginx 不遵守目录覆盖。
Apache 使用 .htaccess
文件,结合在位置块中的 AllowOverride All
指令。这允许您在包含文件的目录中放置特定于目录的配置。
Nginx 不允许这些文件。如果配置错误,将配置与正在提供的文件放在一起可能会带来安全问题,并且很容易查看集中式配置文件而不意识到通过 .htaccess 文件进行了设置覆盖。
因此,您在活动 .htaccess 文件中列出的所有配置必须放置在服务器块配置的位置块中。这通常并不更复杂,但您必须像处理虚拟主机定义一样翻译这些规则。
在 .htaccess 文件中保留的常见内容是 Apache 的 mod_rewrite 模块的规则,该模块修改内容的访问 URL 以使其更用户友好。Nginx 有一个类似的重写模块,但使用不同的语法。不幸的是,在本指南的范围之外重写 URL 在 Nginx 中。
模块和外部配置复杂性
另一件需要牢记的事情是,您需要意识到已启用的 Apache 模块提供了哪些功能。
一个简单的例子是 dir
模块。启用后,您可以通过在虚拟主机文件中添加以下行来指定 Apache 将尝试作为目录索引提供的文件顺序:
DirectoryIndex index.html index.htm
此行将确定此虚拟主机的处理方式。但是,如果此行不存在,并且已启用 dir
模块,则提供的文件顺序将由此文件确定:
sudo nano /etc/apache2/mods-enabled/dir.conf
<IfModule mod_dir.c>DirectoryIndex index.php index.html index.cgi index.pl index.xhtml index.htm</IfModule>
提出这一点的目的是,您必须意识到模块或任何外部配置可能在幕后执行某些操作,而这些操作在 Nginx 中必须显式执行。
在此示例中,您可以通过将以下内容添加到服务器块中来指定 Nginx 中的目录索引顺序:
server {. . .index index.php index.html index.htm;. . .
这一点很重要。
如果您正在迁移复杂的站点配置,可能会有所帮助的一点是将所有单独获取的配置文件复制粘贴到一个单一的文件中,并系统地逐行翻译每一行。
这可能会让人头疼,但对于生产服务器来说,它可以节省您大量时间,以便找出导致您无法准确确定的奇怪行为的原因。
结论
从 Apache 迁移到 Nginx 的复杂性几乎完全取决于您特定配置的复杂性。Nginx 可以处理几乎任何 Apache 可以处理的事情,并且以更少的资源完成。这意味着您的站点可以顺利为更多用户提供服务。
虽然迁移对于所有站点都没有意义,而且 Apache 是一个很好的服务器,可以满足许多项目的需求,但您可能会在 Nginx 中看到性能提升或可扩展性增加。如果您仍需要 Apache,另一种选择是将 Nginx 用作 Apache 服务器的反向代理。这种方法可以强大地发挥两个服务器的优势。
相关文章:

如何在 Ubuntu VPS 上从 Apache Web 服务器迁移到 Nginx
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 简介 在启动网站或应用程序时,您需要做出许多选择。有时,您的需求会发生变化,新技术变得可行&#x…...

pikachu靶场总结(一)
最近看到好多人还在打这个靶机所以把以前写的总结放上来了,主要是皮卡丘靶场漏洞的原理,利用方式,防护方法简略总结,纯手敲记录,总结如果不到位请评论区留言! 一、暴力破解 1.基于表单的暴力破解 原理&a…...

No.2 笔记 | 网络安全攻防:PC、CS工具与移动应用分析
引言 在当今数字化时代,网络安全已成为每个人都应该关注的重要话题。本文将总结一次关于网络安全攻防技术的学习内容,涵盖PC端和移动端的恶意程序利用,以及强大的渗透测试工具Cobalt Strike的使用。通过学习这些内容,我们不仅能够了解攻击者的手法,更能提高自身的安全意识和防…...

QD1-P8 HTML格式化标签
本节学习:HTML 格式化标签。 本节视频 www.bilibili.com/video/BV1n64y1U7oj?p8 一、font 标签 用途:定义文本的字体大小、颜色和 face(字体类型)。 示例 <!DOCTYPE html> <html><head><meta cha…...

WordPress修改固定链接后301的重定向方法
网站改版实际上是很忌讳的,尤其是针对已被搜索引擎收录的网站,新站不用考虑这些问题,而已经收录的网站网页在不遵守搜索引擎规则的前提下,是会被降权,关键词排名下滑、流量IP会被剥夺、收录会减少 、业务成交量会急剧下…...

关于Allegro导出Gerber时的槽孔问题
注意点一: 如果设计的板子中有 槽孔和通孔(俗称圆孔),不仅要NC Drill, 还要 NC Route allegro导出的槽孔文件后缀是 .rou 圆型孔后缀 是 .drl ,出gerber时需要看下是否有该文件。 注意点二: 导出钻孔文件时,设置参…...

平时使用的正则总结
1、将某一个字符串的后缀名后面加上“!400_500” 使用场景是将minio拿过来的图片压缩尺寸从而压缩其大小,加快渲染的速度。需要在图片的后缀名后面加上尺寸如下: const str //storage-test.test.shiqiao.com/gateway/common/isopen/2024/10/09/e708e9…...

[万字解析]从零开始使用transformers微调huggingface格式的中文Bert模型的过程以及可能出现的问题
系列文章目录 使用transformers中的pipeline调用huggingface中模型过程中可能遇到的问题和修改建议 [万字解析]从零开始使用transformers微调huggingface格式的中文Bert模型的过程以及可能出现的问题 文章目录 系列文章目录前言模型与数据集下载模型下载数据集下载 数据加载、…...

K8s简介及环境搭建
一、Kubernetes简介 kubernetes 的本质是一组服务器集群,它可以在集群的每个节点上运行特定的程序,来对节点中的容器进行管理。目的是实现资源管理的自动化,主要提供了如下的主要功能: 自我修复:一旦某一个容器崩溃&a…...

Python对PDF文件页面的旋转和切割
Python对PDF文件页面的旋转和切割 利用Python的.rotate()方法和.mediabox属性对PDF页面进行旋转和切割,最终生成一个PDF。下面结合案例进行说明,本示例中的名为split_and_rotate.pdf文件在practice_files文件夹中, 示例(1&#…...

Android 10.0 修改Systemui三键导航栏虚拟按键颜色功能实现
1.前言 在10.0的系统ROM定制化开发中,在对systemui的相关定制化开发中,在某些产品中,需要修改相关的 导航栏三键导航的虚拟按键的颜色,修改掉原来默认的虚拟按键的黑白色,接下来就来实现相关的功能 2.修改Systemui三键导航栏虚拟按键颜色功能实现的核心类 frameworks\ba…...

『网络游戏』客户端使用PESorket发送消息到服务器【14】
上一章服务器已经完成使用PESorket 现在我们将其导出在客户端中使用 生成成功后复制 粘贴到Unity项目中 进入Assets文件夹 粘贴两个.dll 创建脚本:ClientSession.cs 编写脚本: ClientSession.cs 编写脚本:GameStart.cs 将GameStart.cs脚本绑定在摄像机上 运行服务器 运行客户端…...

JVM(学习预热 - 走进Java)(持续更新迭代)
目录 一、彻底认识Java虚拟机 开创世纪:Sun Classic 开创世纪:Exact VM 武林霸主:HotSpot VM 移动端虚拟机:Mobile/Embedded VM “三大”其二:BEA JRockit/IBM J9 VM 软硬结合:BEA Liquid VM/Azul VM…...

43 C 程序动态内存分配:内存区域划分、void 指针、内存分配相关函数(malloc、calloc、realloc、_msize、free)、内存泄漏
目录 1 C 程序内存区域划分 1.1 代码区 (Code Section) 1.2 全局/静态区 (Global/Static Section) 1.3 栈区 (Stack Section) 1.4 堆区 (Heap Section) 1.5 动态内存分配 2 void 指针(无类型指针) 2.1 void 指针介绍 2.2 void 指针的作用 2.3 …...

编译链接的过程发生了什么?
一:程序的翻译环境和执行环境 在 ANSI C 的任何一种实现中,存在两个不同的环境。 第 1 种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。 第 2 种是执行环境,它用于实际执行代码 也就是说:↓ 1࿱…...

【D3.js in Action 3 精译_028】3.4 小节 DIY 实战:使用 Observable 在线绘制 D3 条形图
当前内容所在位置(可进入专栏查看其他译好的章节内容) 第一部分 D3.js 基础知识 第一章 D3.js 简介(已完结) 1.1 何为 D3.js?1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践(上)1.3 数据可…...

【Linux】 TCP短服务编写和守护进程
文章目录 TCP 短服务编写流程进程组和会话和守护进程 TCP 短服务编写流程 TCP服务器是面向连接的,客户端在发送数据之前需要先与服务器建立连接。 因此,TCP服务器需要能够监听客户端的连接请求。为了实现这一功能,需要将TCP服务器创建的套接字…...

自学数据库-MYSQL
自学数据库-MYSQL 一.表和视图1.表1.1 表创建1.2 索引1.2.1 这里是废话,不感兴趣的可以直接更具目录的跳过这里的内容1.2.1.1 索引是什么1.2.1.2 相关数据结构:二叉树、红黑树、B-Tree、BTree、Hash…①普通索引②唯一索引③全文索引④组合索引 1.3 表数据操作(更新…...

机器学习——多模态学习
多模态学习:机器学习领域的新视野 引言 多模态学习(Multimodal Learning)是机器学习中的一个前沿领域,它涉及处理和整合来自多个数据模式(如图像、文本、音频等)的信息。随着深度学习的蓬勃发展࿰…...

ceph掉电后无法启动osd,pgs unknown
处理办法: 只有1个osd,单副本,掉电损坏osd,只能考虑重建pg,丢失部分数据了。生产环境务必考虑2,3副本设计。避免掉电故障风险。 掉电后osdmap丢失无法启动osd的解决方案 - 武汉-磨渣 - 博客园 https://zhuanlan.zhih…...

HTML5实现古典音乐网站源码模板1
文章目录 1.设计来源1.1 网站首页1.2 古典音乐界面1.3 著名人物界面1.4 古典乐器界面1.5 历史起源界面2.效果和源码2.1 动态效果2.2 源代码源码下载万套模板,程序开发,在线开发,在线沟通作者:xcLeigh 文章地址:https://blog.csdn.net/weixin_43151418/article/details/142…...

快速生成单元测试
1. Squaretest插件 2. 依赖 <dependency><groupId>junit</groupId>...

WebGL系列教程十一(光照原理及Blinn Phong着色模型)
快速导航(持续更新中) WebGL系列教程一(开篇) WebGL系列教程二(环境搭建及着色器初始化) WebGL系列教程三(使用缓冲区绘制三角形) WebGL系列教程四(绘制彩色三角形&…...

《ASP.NET Web Forms 实现短视频点赞功能的完整示例》
在现代Web开发中,实现一个动态的点赞功能是非常常见的需求。本文将详细介绍如何在ASP.NET Web Forms中实现一个视频点赞功能,包括前端页面的展示和后端的处理逻辑。我们将确保点赞数量能够实时更新,而无需刷新整个页面。 技术栈 ASP.NET We…...

Linux SSH服务
Linux SSH(Secure Shell)服务是一种安全的远程登录协议,用于在Linux操作系统上远程登录和执行命令。它提供了加密的通信通道,可以在不安全的网络环境中安全地进行远程访问。 SSH服务在Linux系统中通常使用OpenSSH软件包来实现。它…...

MySQL--视图(详解)
目录 一、前言二、视图2.1概念2.2语法2.3创建视图2.3.1目的 2.4查看视图2.5修改数据2.5.1通过真实表修改数据,会影响视图2.5.2通过修改视图,会影响基表 2.6注意2.7 删除视图2.8 视图的优点 一、前言 欢迎大家来到权权的博客~欢迎大家对我的博客进行指导&…...

Javascript 普通非async函数调用async函数
假设我们有一个异步函数 async function asyncFunction() {console.log("开始执行异步函数");await new Promise(resolve > setTimeout(resolve, 1000)); // 模拟异步操作console.log("异步函数执行完毕"); } 我们在调用这个异步函数时,比…...

【LeetCode】修炼之路-0004-Median of Two Sorted Arrays【python】
题目 Given two sorted arrays nums1 and nums2 of size m and n respectively, return the median of the two sorted arrays. The overall run time complexity should be O(log (mn)). Example 1: Input: nums1 [1,3], nums2 [2] Output: 2.00000 Explanation: merged…...

C++面试速通宝典——10
177. #include <filename> 和 #include "filname.h" 有什么区别? 对于 #include <filename> , 编译器从标准库路径开始搜索 filename.h。 对于 #include "filename.h,编译器从用户的工作…...

肺腺癌预后新指标:全切片图像中三级淋巴结构密度的自动化量化|文献精析·24-10-09
小罗碎碎念 本期这篇文章,我去年分享过一次。当时发表在知乎上,没有标记参考文献,配图的清晰度也不够,并且分析的还不透彻,所以趁着国庆假期重新分析一下。 这篇文章的标题为《Computerized tertiary lymphoid structu…...