博客 (83)

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 DNS

2、request_uri 中直接请求带 IP 地址的网址。

要求这个网站允许直接使用 IP 地址访问。

需要禁用 SSL 验证(ssl_verify = false)。

xoyozo 半个月前
1,721

QQ20250716-111451.png

保存工作再继续。Visual Studio Standard Collector Service 150 (VSStandardCollectorService150)

尝试关闭正在运行的程序,发现是“腾讯元宝”启动了这个进程。

xoyozo 2 个月前
1,857

安装 PyPI(包/库/组件)

pip install 包名

如果安装失败,尝试用国内镜像

pip install 包名 -i https://pypi.tuna.tsinghua.edu.cn/simple

如果不想每次都加 -i 参数,可以更改全局配置

pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple


查看所有配置

pip config list


查看 python 文件路径

# Windows 或 Linux 通用
python -c "import sys; print(sys.executable)"

默认路径为:C:\Users\<user name>\AppData\Local\Programs\Python\Python<version>\python.exe


常用组件介绍

包名介绍
Flask一款基于 Python 的轻量级 Web 开发框架
Django一款基于 Python 的重量级 Web 开发框架
Pandas一个数据分析包,提供 Series、Time-Series、DataFrame、Panel、Panel4D、PanelND 等数据结构


更多

xoyozo 3 个月前
640
  1. 临界区与 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 实现异步锁

    lock 示例

     

  2. 互斥锁(Mutex)

    核心作用:

    系统级内核锁,支持跨进程同步,但性能开销较高(用户态/内核态切换)。

    实现示例:

    using var mutex = new Mutex(false, "Global\\MyAppMutex");
    
    try 
    {
        // 等待锁(最大等待时间500ms)
        if (mutex.WaitOne(500)) 
        {
            // 临界区代码
        }
    }
    finally 
    {
        if (mutex != null)
        {
            mutex.ReleaseMutex();
        }
    }

    关键特性:

    支持跨应用程序域同步

    线程终止时自动释放锁

    支持命名互斥体(系统全局可见)

    适用场景:

    单实例应用程序控制

    进程间共享文件访问

    硬件设备独占访问

    Mutex 示例

     

  3. 信号量(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 请求限流

    批量任务并发控制

    Semaphore 示例

     

  4. 事件(Event)

    核心机制:

    通过信号机制实现线程间通知,分为两种类型:

    类型
    信号重置方式
    唤醒线程数
    AutoResetEvent
    自动
    单个
    ManualResetEvent
    手动
    所有

    使用示例:

    var autoEvent = new AutoResetEvent(false);
    
    // 等待线程
    Task.Run(() => 
    {
        autoEvent.WaitOne();
        // 收到信号后执行
    });
    
    // 信号发送线程
    autoEvent.Set();  // 唤醒一个等待线程

    高级用法:

    配合WaitHandle.WaitAll实现多事件等待

    使用ManualResetEventSlim提升性能

    AutoResetEvent 示例

     

  5. 读写锁(ReaderWriterLockSlim)

    核心优势:

    实现读写分离的并发策略,适合读多写少场景(如缓存系统)。

    锁模式对比:

    模式
    并发性
    升级支持
    读模式(EnterReadLock)
    多线程并发读

    写模式(EnterWriteLock)
    独占访问

    可升级模式
    单线程读→写
    ✔️

    代码示例:

    var rwLock = new ReaderWriterLockSlim();
    
    // 读操作
    rwLock.EnterReadLock();
    try 
    {
        // 只读访问
    }
    finally 
    {
        rwLock.ExitReadLock();
    }
    
    // 写操作
    rwLock.EnterWriteLock();
    try 
    {
        // 排他写入
    }
    finally 
    {
        rwLock.ExitWriteLock();
    }

    最佳实践:

    优先使用ReaderWriterLockSlim(旧版有死锁风险)

    避免长时间持有读锁(可能饿死写线程)

    ReaderWriterLockSlim 示例

  6. 原子操作(Interlocked)

    原理:

    通过CPU指令实现无锁线程安全操作。

    常用方法:

    int counter = 0;
    Interlocked.Increment(ref counter);      // 原子递增
    Interlocked.Decrement(ref counter);      // 原子递减
    Interlocked.CompareExchange(ref value, newVal, oldVal);  // CAS操作

    适用场景:

    简单计数器

    标志位状态切换

    无锁数据结构实现

     

  7. 自旋锁(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结构实现自适应等待

     

  8. 同步机制对比指南

    机制
    跨进程
    开销级别
    最佳适用场景
    lock


    通用临界区保护
    Mutex
    ✔️

    进程间资源独占
    Semaphore
    ✔️

    并发数限制(跨进程)
    SemaphoreSlim


    并发数限制(进程内)
    ReaderWriterLockSlim


    读多写少场景
    SpinLock

    极低
    纳秒级临界区
    Interlocked
    -无锁
    简单原子操作

选择原则:

  1. 优先考虑用户态锁(lock/SpinLock/SemaphoreSlim)

  2. 跨进程需求必须使用内核对象(Mutex/Semaphore)

  3. 读写比例超过10:1时考虑读写锁

  4. 自旋锁仅用于高频短操作(如链表指针修改)

通过以上结构化的分类和对比,开发者可以更精准地选择适合特定场景的线程同步方案。建议在实际使用中配合性能分析工具(如BenchmarkDotNet)进行量化验证。

💡 ASP.NET 的异步编程(async/await)本质是单进程内的线程调度,不算“跨进程”。每个 IIS 应用程序池对应一个独立的工作进程(w3wp.exe),不同用户访问同一应用程序池下的 ASP.NET 网站,两者的请求均由同一个 w3wp.exe 进程处理。可能跨进程的场景有:Web Garden 配置、多应用程序池部署等。

在 C# 中,除了常规锁机制(如 lock、Mutex、Semaphore 等),还有一些内置类型通过内部锁或无锁设计实现线程安全。以下是常见的几类:

  1. 线程安全集合(System.Collections.Concurrent)这些集合通过细粒度锁或无锁算法(如 CAS)实现线程安全,适合高并发场景。

    • ConcurrentDictionary:分段锁机制,将数据分片存储,每个分片独立加锁,减少锁竞争。

    • ConcurrentQueue / ConcurrentStack基于原子操作(Interlocked)保证线程安全。

    • ConcurrentBag:每个线程维护本地存储,减少争用,适合频繁添加和移除的场景。

    • BlockingCollection:基于 ConcurrentQueue 和信号量(SemaphoreSlim)实现生产-消费者模式,支持阻塞和超时。

  2. 不可变集合(System.Collections.Immutable) 通过数据不可变性实现线程安全(无需锁),每次修改返回新对象。

  3. Lazy 的线程安全初始化(Lazy<T>) 通过锁或 Interlocked 确保延迟初始化的线程安全。

  4. 通道(System.Threading.Channels)用于异步生产-消费者模型,内部通过锁和信号量管理容量限制。

  5. 内存缓存(System.Runtime.Caching.MemoryCache)内部使用锁保护共享状态,确保线程安全。

  6. 原子操作类型(Interlocked 类、Volatile 关键字、Unsafe 类)通过 CPU 指令实现无锁线程安全。

  7. 其他同步工具(Barrier、CountdownEvent)虽然不是严格意义上的锁,但用于协调线程。

xoyozo 4 个月前
1,158
数据库 数据类型 收费情况 调用方式 数据来源 资料
BaoStock 仅提供历史数据(如日线、周线),无实时行情 免费开源 Python 数据来源为交易所或合作机构的标准化接口 文档
AkShare 股票、期货、债券、期权、外汇、货币、指数…… 免费开源 Python / Anaconda / R / MATLAB / 本地 Web API

基于爬虫技术从大型财经网站抓取公开数据

包含北交所行情数据                

文档 / 数据字典 / AKTools
Tushare Pro 股票、指数、公募、期货、期权、债券、外汇、港股、美股…… 免费 / 收费 Web API / Python / Matlab / R

通过社区的采集和整理存入数据库经过质量控制后再提供给用户

包含北交所行情数据                

积分 / 权限 / 文档
麦蕊智数 沪深股票基础数据、实时交易数据、指数数据 免费 / 收费 Web API licence / 文档
JoinQuant/JQData 股票、基金等行情数据 试用 / 收费 Python 文档
xoyozo 6 个月前
1,391

前几天实现了在 nginx 中使用 lua 实现远程鉴权,今天想试试在 IIS 中能不能实现相同的功能。查询资料发现需要使用 URL 重写和 HTTP 请求模块,没有深究。干脆使用 ASP.NET 中间件来实现吧。

在 StratUp.cs 的 Configure 方法中,或 Program.cs 文件中添加以下代码:

// 远程鉴权
app.Use(async (context, next) =>
{
    var ip = context.Connection.RemoteIpAddress!.ToString();
    var ua = context.Request.Headers.UserAgent.ToString();
    var host = context.Request.Host.Host;
    var uri = new Uri(context.Request.GetDisplayUrl()).PathAndQuery;

    var client = new HttpClient();
    client.Timeout = TimeSpan.FromSeconds(1); // 设置超时时间

    try
    {
        var requestUrl = "https://鉴权地址/";

        var requestMessage = new HttpRequestMessage(HttpMethod.Get, requestUrl);
        requestMessage.Headers.Add("X-Real-IP", ip);
        requestMessage.Headers.Add("User-Agent", ua);
        requestMessage.Headers.Add("X-Forwarded-Host", host);
        requestMessage.Headers.Add("X-Forwarded-Uri", uri);

        // 发送请求
        var response = await client.SendAsync(requestMessage);

        // 检查响应状态码
        if (response.StatusCode == HttpStatusCode.Forbidden)
        {
            // 如果返回403,则拒绝访问
            context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
            await context.Response.WriteAsync("Access Denied");
        }
        else
        {
            // 如果返回其他状态码,则继续执行管道中的下一个中间件
            await next();
        }
    }
    catch (TaskCanceledException ex) when (ex.CancellationToken.IsCancellationRequested)
    {
        // 如果请求超时(任务被取消),则继续执行管道中的下一个中间件
        await next();
    }
    catch
    {
        // 如果遇到错误,则继续执行管道中的下一个中间件
        await next();
    }
});

代码很简单,使用 HttpClient 发送请求,若返回 403 则拒绝访问,其它情况继续执行业务逻辑,超时或报错的情况按需修改即可。

若鉴权接口在私网中,建议将鉴权接口域名和私网 IP 添加到 hosts 文件中。

xoyozo 9 个月前
973
  • location 优先于 access_by_lua_block,即使 access_by_lua_block 放在 http 中。

  • location 的 3 种匹配的优先级,精确匹配(=) > 正则匹配(~) > 前缀匹配(无符号,直接是“/”开头的 uri 前缀)。

  • 相同匹配类型的多个 location(比如有多个正则匹配),按定义顺序来进行匹配。

  • 一旦有一个 location 被匹配到,就不再继续匹配其它 location。

  • 如果使用 access_by_lua_block 鉴权,那么不要放在任何 location 中,这样,匹配完 location 后只要没有命中直接返回语句,就会继续执行 access_by_lua_block。并且,如果 access_by_lua_block 中鉴权拒绝,后续的业务处理(如 php 业务)不会继续进行。


xoyozo 9 个月前
1,202
http {
    ...

    lua_shared_dict cpu_cache 1m;  # 定义共享字典用于缓存 CPU 使用率
    access_by_lua_block {
        local cpu_cache = ngx.shared.cpu_cache
        local cache_time = cpu_cache:get("cache_time") or 0
        local current_time = ngx.now()

        if current_time - cache_time > 5 then  -- 每 5 秒更新一次
            local cpu_info = io.popen("top -bn1 | grep 'Cpu(s)'"):read("*all")
            local cpu_usage = cpu_info:match("(%d+%.%d+) id")  -- 获取空闲 CPU 百分比
            local cpu_used = 100 - tonumber(cpu_usage)  -- 计算使用率

            cpu_cache:set("cpu_usage", cpu_used)
            cpu_cache:set("cache_time", current_time)
        end

        local cpu_usage = cpu_cache:get("cpu_usage")
        ngx.log(ngx.INFO, "CPU 使用率: " .. cpu_usage .. "%")
        -- 将 cpu_usage 发送到远程鉴权接口,可根据服务器压力来决定是否拒绝一些不重要的请求
    }
}

代码解析:

共享字典:使用 lua_shared_dict 定义一个共享字典 cpu_cache,用于存储 CPU 使用率和缓存时间。

获取 CPU 使用率:在 access_by_lua_block 中,检查缓存时间,如果超过 5 秒,则重新获取 CPU 使用率,并更新共享字典。

记录日志:使用 ngx.log 将 CPU 使用率记录到 Nginx 日志中。

注意事项:

确保 Nginx 配置中已经加载了 Lua 模块(如 ngx_http_lua_module)。

根据实际需求调整缓存时间,以平衡性能和数据的实时性。

尝试过使用 ifconfig 或 ip 命令获取网卡流量,在宝塔面板中失败了,怀疑是权限问题,有空再研究。临时方案是鉴权接口定时调用宝塔面板 API 或阿里云控制台 API 来获取 ECS 的 CPU 和带宽使用率。

如何配置 nginx 远程鉴权

xoyozo 9 个月前
732
  1. 使 lua 支持 resty.http

  2. 在 access_by_lua_block 代码块中实现远程鉴权:

    #鉴权-START
    #resolver 223.5.5.5; # cat /etc/resolv.conf
    access_by_lua_block {
        local http = require("resty.http")
        local httpc = http.new()
        httpc:set_timeout(500)  -- 连接超时
        local res, err = httpc:request_uri("https://鉴权地址/", {
            method = "GET",
            headers = {
                ["X-Real-IP"] = ngx.var.remote_addr,
                ["User-Agent"] = ngx.var.http_user_agent,
                ["X-Forwarded-Host"] = ngx.var.host,
                ["X-Forwarded-Uri"] = ngx.var.request_uri,
            },
            ssl_verify = false, -- 禁用 SSL 验证
            timeout = 500,     -- 读取超时
        })
    
        if not res then
            ngx.log(ngx.ERR, "Failed to request: " .. err)
        end
            
        if res and res.status == 403 then
            ngx.exit(ngx.HTTP_FORBIDDEN)
            -- return ngx.redirect("https://一个显示403友好信息的页面.html")
        end
    }
    #鉴权-END

    注意更改接口地址和友情显示 403 页面地址。

    本示例仅捕获 403 状态码,500、408 等其它异常情况视为允许访问,请根据业务需求自行添加状态码的判断。

    若超时也会进入 if not res then 代码块。

    建议将此代码部署在 nginx 主配置文件的 http 代码块中(宝塔面板中的路径:/www/server/nginx/nginx/conf/nginx.conf),如果你只想为单个网站鉴权,也可以放在网站配置文件的 server 块中。

  3. 若鉴权接口在私网中,建议将鉴权接口域名和私网 IP 添加到 hosts 文件中。


  1. 直接输出字符串

    ngx.header.content_type = "text/plain";
    ngx.say("hello world!")
  2. 输出到日志

    ngx.log(ngx.ERR, "Response status: " .. res.status)

    日志在网站的 站名.error.log 中查看。

    宝塔面板查看方式:日志 - 网站日志 - 异常

  3. 若想获取服务器 CPU 使用率等信息并传递给远程鉴权接口,请参考此文

  4. 常见问题

    no resolver defined to resolve

    原因:没有定义 DNS 解析器

    解决方法:在 http 块或 server 块中添加 resolver 8.8.8.8 valid=30s;,推荐使用接入商自己的公共 DNS,如果走内网,推荐使用本机 DNS

    unable to get local issuer certificate

    原因:没有配置 SSL 证书信息

    解决方法:添加 request_uri 参数:

    ssl_verify = true,  -- 启用 SSL 验证
    ssl_trusted_certificate = "证书路径",  -- 指定 CA 证书路径

    ssl_verify = false,  -- 禁用 SSL 验证
  5. 若您不想用 lua,可以用 nginx 原生自带的  auth_request 模块来实现

xoyozo 9 个月前
1,634
  1. 安装 OpenResty

    在 宝塔面板 - 软件商店 中搜索 nginx,安装版本选择 openresty 版

    image.png

  2. 安装 LuaRocks

  3. 安装 resty.http

    luarocks install lua-resty-http
  4. 检测已安装的组件

    nginx -v
    lua -v
    openresty -v
    luarocks --version
  5. 测试代码

    access_by_lua_block {
        local http = require("resty.http")
    }

    access_by_lua '
        local http = require("resty.http")
    ';
xoyozo 9 个月前
2,120