前面写过一篇文章介绍了不买高防 IP,照样抵挡 DDoS 攻击(IP 篇),如果攻击的是域名(CC),那么...
本文适用场景:攻击的是 web 网址而非 IP,来源有许多各地的 IP 地址,每个 IP 地址每小时只有若干请求,但整个网站每分钟有数万请求量。
《小白鼠A》(贵)
阿里云有一款产品叫“Web 应用防火墙”,开通并接入云产品,试用按量付费即可。
在防护配置-Web核心防护 中可配置各种规则,譬如:
“自定义规则”可添加网址/UA/IP/Cookie/等中包含某些关键词的请求;
“区域封禁”可按地区来限制请求;
另外还有“CC防护”等各种实用规则。
添加规则后记得关联实例。
配置完成后可在总览页面查看请求情况,如果攻击停止可关闭防火墙。
试用了12小时,平均每分钟请求2-5万次,消耗 15000SeCU,费用大概是750元。
《小白鼠B》(推荐)
阿里云有一款产品叫“边缘安全加速 ESA”,开通接入域名(注意填写域名时不要写 www)。
创建购买成功后会有一个 CNAME 域名,解析生效。配置 HTTPS 证书。
打开“我正在遭受攻击”,开启后站点进入严格防护模式,默认对所有 HTTP 请求做滑块挑战。这是非常能节省费用的操作,实测开启与不开启的数量相差20倍(具体视攻击强弱)。
另外,ESA 自带的“安全防护-WAF”中若想根据 URL 中包含某个关键词来拒绝请求,需要升级套餐。可以直接在“规则-重定向”设置 302 跳转也能达到类似的效果,但是节省流量的最好办法还是开启滑块。
费用的话,大部费用是由“客户端请求到边缘加速服务器的流量”和“边缘加速服务器响应给客户端的流量”组成的,而且滑块页面不计入流量。可免费试用。
在开启滑块的情况下,1分钟产生10M流量,基础版包含的50GB可以用3天。标准版(500GB)375元可以用一个月。(以我站实测为例,各站情况不同)。
具体费用在“计费管理-套餐管理-套餐总量 (月)-
”查看。超过套餐的流量会使用CDN流量包抵扣。
ECS 获取客户端 IP 的问题有两种解决方案,1. 设置四层代理分析(企业版),2. 设置重定向规则,重定向到直接解析到该网站的另一个域名。
为了尽量减少滑块对用户体验的影响,建议加白名单:ESA-站点-安全防护-WAF-白名单规则,譬如添加省份规则,跳过全部规则(包括滑块)。
其它问题:滑块会拒绝蜘蛛?
其它相似产品:Cloudflare、腾讯云 EdgeOne。
----------------下面是停站维护时显示临时页面的做法----------------
被攻击期借用阿里云 OSS/CDN 显示“维护中”页面的方法。
1.
首先让 AI 快速生成一个维护中的页面,上传到 OSS。
虽然这个文件有一个对应的 CDN 链接,但是域名解析使用“显性 URL”模式并不能正常访问到这个页面。
2.
在阿里云 CDN 添加一个域名(也就是网站域名),会生成一个 CNAME 域名,网站域名 CNAME 到这个域名上。
配置这个 CDN 域名的 OSS 实例、HTTPS 等信息。
这样生效后,访问网站域名就能请求到这个 OSS 上了。
3.
但是它并不像网站一样有默认文档,需要添加重写规则。
打开 CDN-域名管理-缓存配置-重写访问URL,添加,添加重写规则。
例:如果目标页面路径是 https://域名/index.html,那么重写规则就是:
^/(?!.*index\.html$).*$也就是把所有除 /index.html 以外的路径全部重写到 /index.html。
4.
下一步只要观察这个域名的实时流量和带宽,如果攻击停止了,将域名解析回 ECS。
CDN-统计分析-实时监控-访问数据-选择域名-查询。
方法一:打开 事件查看器-> Windows 日志-> 应用程序。

方法二:启用ASP.NET Core Module Stdout 日志。
修改网站根目录下的 web.config 文件,在 <aspNetCore> 节点中启用标准输出日志。你需要先手动创建 logs 文件夹并确保有写入权限。
<aspNetCore processPath="dotnet"
arguments=".\YourAppName.dll"
stdoutLogEnabled="true"
stdoutLogFile=".\logs\stdout"
hostingModel="inprocess" />重现错误后,即可在 logs 目录下查看生成的日志文件。注意:出于性能考虑,问题解决后建议将 stdoutLogEnabled 设为 false。
今天遇到一个向基于 .NET Framework 框架开发的 Web 网站 POST 数据响应 405 Method Not Allowed 的问题。
服务端接口地址是 https://***/api/abc/Default.aspx,
若请求 https://***/api/abc/Default.aspx 或 https://***/api/abc/ 则正常 ,
若请求 https://***/api/abc 就会出现这个问题。
原因可能是服务器可能返回 301 重定向到 https://***/api/abc/Default.aspx,
导致最终接口接受到的请求方法不是 POST 。
确保已正确配置环境网站项目。
-----------------------------------------
2025/11/3 启动失败,查到项目日志过大(位于 F:\www\wwwlogs\net),清除后正常启动。
-----------------------------------------
不定时更新。
在 Linux 上运行 .NET 网站,通过
HttpContext.Connection.RemoteIpAddress获取客户端的 IP 地址,结果是
::ffff:127.0.0.1解决方法:
打开 Program.cs 文件,在 var app = builder.Build(); 之前(尽量往前)添加以下代码:
if (OperatingSystem.IsLinux())
{
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor
| ForwardedHeaders.XForwardedProto
| ForwardedHeaders.XForwardedHost;
// 清除 KnownNetworks 和 KnownProxies,表示信任来自本机的代理(如 Nginx)
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
Console.WriteLine("ForwardedHeaders enabled (Running on Linux)");
}然后在 app.UseRouting(); 之前添加以下代码:
if (OperatingSystem.IsLinux())
{
app.UseForwardedHeaders();
Console.WriteLine("UseForwardedHeaders() applied.");
}其中,OperatingSystem.IsLinux() 用于判断只在 Linux 环境中生效,你可以视自身情况作判断。
.NET 项目发布到 Linux / CentOS / nginx 上,调用 RedirectToAction 方法跳转到 127.0.0.1:443,而不是正在访问的域名,怎么办?
打开该网站的 nginx 配置文件,找到反射代理配置(proxy_pass),将:
proxy_set_header Host 127.0.0.1:$server_port;改为
proxy_set_header Host $host;早前记录过在 CentOS 中部署 ASP.NET Core 网站,现在宝塔面板已经直接支持创建 .NET 网站了,再次记录一下。
本文环境:VS2022、.NET 9、宝塔面板v11。
一、在 VS 中创建一个 .NET 9 网站,项目名称例:WebApplication1
发布选项:

关于“生成单个文件”选项,若勾选,发布后的项目启动文件不带后缀名(.dll),尝试在宝塔面板 v11.0.0 中无法顺利启动。
二、在 Linux 服务器上创建目录:/www/wwwroot/WebApplication1/
将发布后的文件上传到这个目录上,最终的文件结构如下:

三、进入宝塔面板,在 网站-Net项目 中安装 .Net环境管理,这里以 9.0.201 为例:

四、添加项目
项目名称:随意
运行路径:项目所在目录,本例中为:
/www/wwwroot/WebApplication1启动命令:使用 dotnet 命令,dotnet 命令可以不使用全路径(/www/server/dotnet/9.0.201/dotnet),第一个参数是项目启动文件,--urls 是反向代理的服务器和端口。服务器名可以是*也可以是localhost。例:
dotnet WebApplication1.dll --urls=http://*:5000项目端口:与启动命令中的端口号一致
开机启动:一般会勾选
启动用户:尽量用 www,最小权限原则。

正常情况下,网站已启动,如果未启动,检查配置,根据报错内容排查问题。
五、配置网站
添加域名、SSL 证书等。
友情提示:
网站版本更新重新发布后,需要手动重启网站才能生效,习惯于发布到 IIS(会自动生效)的同学要特别注意。
新手尽量以空项目或默认项目来部署,避免因特殊原因导致一系列其它问题。
nginx 反向代理并不会转发所有 Header,需要手动配置。
nginx.conf 中使用 resolver 设置了 DNS 后,nginx 会遵循这个指令,而不是直接使用系统的 hosts 配置。
所以,如果 resolver 设置了外部 DNS(譬如一般的公共 DNS),在 access_by_lua_block 中使用 resty.http 库发起对包含域名的网址的请求,会忽略 hosts 的设置。
解决方案:
1、修改 resolver 指向为本机 DNS。(AI 推荐,实测无效,可以研究一下配合 dnsmasq 使读取 hosts)
查看本机 DNS 的命令:
cat /etc/resolv.conf或
nmcli dev show | grep DNS2、request_uri 中直接请求带 IP 地址的网址。
要求这个网站允许直接使用 IP 地址访问。
需要禁用 SSL 验证(ssl_verify = false)。
IIS 中使用 HttpPlatformHandler 模块部署 python web 项目时遇到 502.3 网关错误:
HTTP 错误 502.3 - Bad Gateway
There was a connection error while trying to route the request.
最可能的原因:
CGI 应用程序没有返回一组有效的 HTTP 错误。
由于父网关中出现错误,充当代理或网关的服务器无法处理该请求。
可尝试的操作:
使用 DebugDiag 排查 CGI 应用程序。
确定此错误是否由代理或网关引起。
详细错误信息:
模块 httpPlatformHandler
通知 ExecuteRequestHandler
处理程序 httpplatformhandler
错误代码 见下方表格
详细信息:
当 CGI 应用程序未返回一组有效的 HTTP 头,或者代理或网关无法将请求发送至父网关时,便会出现此错误。您可能需要获取一个网络跟踪,或者与代理服务器管理员联系(如果不是 CGI 的问题)。
| 错误代码 | 可能的错误原因 |
|---|---|
| 0x8007053d | 未安装相关组件库,建议执行命令:pip install -r requirements.txt |
| 0x80070005 | 若显示配置错误“由于权限不足而无法读取配置文件”则是应用代码目录未配置相应用户的读取权限; 访问受限,请正确配置 IIS 中的网站凭据、应用程序池标识、项目目录“读取”、解释器目录“读取和执行”权限等 |
| 0x80072ee2 | 执行超时,一般发生在批量读写的时候 |
| 0x8007042b | |
| 任何错误 | 查看日志文件。 可能的错误原因:
|
临界区与 lock 关键字
核心作用:
通过将多线程访问串行化,保护共享资源或代码段。lock 关键字是 Monitor 类的语法糖,提供异常安全的临界区实现。
实现示例:
// 创建私有静态只读对象 // private static readonly object _lockObj = new object(); private static readonly System.Threading.Lock _locker = new(); // .NET 9+ 推荐使用 Lock 类型,避免传统 object 的性能损耗 public void ThreadSafeMethod() { lock (_lockObj) { // 临界区代码(每次仅一个线程可进入) } }超时机制:
高并发场景可结合 Monitor.TryEnter 设置超时,避免无限等待:
if (Monitor.TryEnter(lockObject, TimeSpan.FromSeconds(1))) { try { /* 操作 */ } finally { Monitor.Exit(lockObject); } }关键特性:
用户态锁(无内核切换开销)
自动调用Monitor.Enter和Monitor.Exit
必须使用专用私有对象作为锁标识
注意事项:
❌ 避免锁定this、Type实例或字符串(易引发死锁)
❌ 避免嵌套锁(需严格按顺序释放)
✅ 推荐readonly修饰锁对象
❌ lock 不适用于异步代码(async/await),需使用 SemaphoreSlim 实现异步锁
互斥锁(Mutex)
核心作用:
系统级内核锁,支持跨进程同步,但性能开销较高(用户态/内核态切换)。
实现示例:
using var mutex = new Mutex(false, "Global\\MyAppMutex"); try { // 等待锁(最大等待时间500ms) if (mutex.WaitOne(500)) { // 临界区代码 } } finally { if (mutex != null) { mutex.ReleaseMutex(); } }关键特性:
支持跨应用程序域同步
线程终止时自动释放锁
支持命名互斥体(系统全局可见)
适用场景:
单实例应用程序控制
进程间共享文件访问
硬件设备独占访问
信号量(Semaphore)
核心作用:
通过许可计数器控制并发线程数,SemaphoreSlim为轻量级版本(用户态实现)。
实现对比:
类型 跨进程 性能 最大许可数 Semaphore ✔️ 低 系统限制 SemaphoreSlim ❌ 高 Int32.Max 代码示例:
// 创建初始3许可、最大5许可的信号量 var semaphore = new SemaphoreSlim(3, 5); semaphore.Wait(); // 获取许可 try { // 资源访问代码 } finally { semaphore.Release(); }异步编程
private readonly SemaphoreSlim _asyncLock = new(1, 1); public async Task UpdateAsync() { await _asyncLock.WaitAsync(); try { /* 异步操作 */ } finally { _asyncLock.Release(); } }典型应用:
数据库连接池(限制最大连接数)
API 请求限流
批量任务并发控制
事件(Event)
核心机制:
通过信号机制实现线程间通知,分为两种类型:
类型 信号重置方式 唤醒线程数 AutoResetEvent 自动 单个 ManualResetEvent 手动 所有 使用示例:
var autoEvent = new AutoResetEvent(false); // 等待线程 Task.Run(() => { autoEvent.WaitOne(); // 收到信号后执行 }); // 信号发送线程 autoEvent.Set(); // 唤醒一个等待线程高级用法:
配合WaitHandle.WaitAll实现多事件等待
使用ManualResetEventSlim提升性能
读写锁(ReaderWriterLockSlim)
核心优势:
实现读写分离的并发策略,适合读多写少场景(如缓存系统)。
锁模式对比:
模式 并发性 升级支持 读模式(EnterReadLock) 多线程并发读 ❌ 写模式(EnterWriteLock) 独占访问 ❌ 可升级模式 单线程读→写 ✔️ 代码示例:
var rwLock = new ReaderWriterLockSlim(); // 读操作 rwLock.EnterReadLock(); try { // 只读访问 } finally { rwLock.ExitReadLock(); } // 写操作 rwLock.EnterWriteLock(); try { // 排他写入 } finally { rwLock.ExitWriteLock(); }最佳实践:
优先使用ReaderWriterLockSlim(旧版有死锁风险)
避免长时间持有读锁(可能饿死写线程)
原子操作(Interlocked)
原理:
通过CPU指令实现无锁线程安全操作。
常用方法:
int counter = 0; Interlocked.Increment(ref counter); // 原子递增 Interlocked.Decrement(ref counter); // 原子递减 Interlocked.CompareExchange(ref value, newVal, oldVal); // CAS操作适用场景:
简单计数器
标志位状态切换
无锁数据结构实现
自旋锁(SpinLock)
核心特点:
通过忙等待(busy-wait)避免上下文切换,适用极短临界区(<1微秒)。
实现示例:
private SpinLock _spinLock = new SpinLock(); public void CriticalOperation() { bool lockTaken = false; try { _spinLock.Enter(ref lockTaken); // 极短临界区代码 } finally { if (lockTaken) _spinLock.Exit(); } }优化技巧:
单核CPU需调用Thread.SpinWait或Thread.Yield
配合SpinWait结构实现自适应等待
同步机制对比指南
机制 跨进程 开销级别 最佳适用场景 lock ❌ 低 通用临界区保护 Mutex ✔️ 高 进程间资源独占 Semaphore ✔️ 中 并发数限制(跨进程) SemaphoreSlim ❌ 低 并发数限制(进程内) ReaderWriterLockSlim ❌ 中 读多写少场景 SpinLock ❌ 极低 纳秒级临界区 Interlocked - 无锁 简单原子操作
选择原则:
优先考虑用户态锁(lock/SpinLock/SemaphoreSlim)
跨进程需求必须使用内核对象(Mutex/Semaphore)
读写比例超过10:1时考虑读写锁
自旋锁仅用于高频短操作(如链表指针修改)
通过以上结构化的分类和对比,开发者可以更精准地选择适合特定场景的线程同步方案。建议在实际使用中配合性能分析工具(如BenchmarkDotNet)进行量化验证。
💡 ASP.NET 的异步编程(async/await)本质是单进程内的线程调度,不算“跨进程”。每个 IIS 应用程序池对应一个独立的工作进程(w3wp.exe),不同用户访问同一应用程序池下的 ASP.NET 网站,两者的请求均由同一个 w3wp.exe 进程处理。可能跨进程的场景有:Web Garden 配置、多应用程序池部署等。
在 C# 中,除了常规锁机制(如 lock、Mutex、Semaphore 等),还有一些内置类型通过内部锁或无锁设计实现线程安全。以下是常见的几类:
线程安全集合(System.Collections.Concurrent)这些集合通过细粒度锁或无锁算法(如 CAS)实现线程安全,适合高并发场景。
ConcurrentDictionary:分段锁机制,将数据分片存储,每个分片独立加锁,减少锁竞争。
ConcurrentQueue / ConcurrentStack:基于原子操作(Interlocked)保证线程安全。
ConcurrentBag:每个线程维护本地存储,减少争用,适合频繁添加和移除的场景。
BlockingCollection:基于 ConcurrentQueue 和信号量(SemaphoreSlim)实现生产-消费者模式,支持阻塞和超时。
不可变集合(System.Collections.Immutable) 通过数据不可变性实现线程安全(无需锁),每次修改返回新对象。
Lazy 的线程安全初始化(Lazy<T>) 通过锁或 Interlocked 确保延迟初始化的线程安全。
通道(System.Threading.Channels)用于异步生产-消费者模型,内部通过锁和信号量管理容量限制。
内存缓存(System.Runtime.Caching.MemoryCache)内部使用锁保护共享状态,确保线程安全。
原子操作类型(Interlocked 类、Volatile 关键字、Unsafe 类)通过 CPU 指令实现无锁线程安全。
其他同步工具(Barrier、CountdownEvent)虽然不是严格意义上的锁,但用于协调线程。