基于消费者生产者模型,使用线程池控制发送HTTP请求的定时器

从零构建Linux高性能HTTP文件服务器:epoll与线程池实战 在Linux高性能网络编程领域,I/O多路复用是解决高并发连接的核心技术。其原理是通过单个线程监控多个文件描述符的状态变化,当某个描述符就绪时再进行I/O操作,从而大幅提升资源利用率。其中epoll作为Linux特有的I/O事件通知机制,相比传统的select/poll具有更高的性能和可扩展性,成为构建现代高并发服务器的基石。结合线程池技术,可以将I/O事件监听与业务逻辑处理解耦,实现高效的任务调度与资源复用。这种架构模式特别适用于需要处理大量并发连接的文件服务器、Web服务器等场景。本文通过一个支持文件上 阅读详情

摘要:

      在业务中遇到了一种场景,在对于被请求方难以被优化的情况下,尝试通过在发送端通过线程池控制允许同时发送请求的数量,以降低直接请求服务端的压力,也要考虑请求发送失败时的重试机制;同时,需要每一次请求后的数据的集合,进行数据库的批量查询,因此使用多线程中的经典的消费者生产者模型进行控制变量,等待所有请求处理完毕后,再统一进行操作。

上干货

       先来看线程池的声明,因为考虑到,线程池满了之后,也会有需要发送请求的情况,所以使用线程池工厂管理线程池;最重要的是,当线程池满的时候,执行恰当的回绝策略,优雅的处理满线程池之后的其他请求,而不是简单的拒绝。

// 线程池size
private static int threadPoolSize = 50;

private static ThreadFactory threadFactory = new EmailTaskThreadFactory();
private static RejectedExecutionHandler rejectedHandler = new EmailTaskIgnorePolicy();
// 声明一个线程池
private static ExecutorService threadpool = new ThreadPoolExecutor(threadPoolSize, threadPoolSize,
        60L, TimeUnit.SECONDS,
        new ArrayBlockingQueue<>(threadPoolSize), threadFactory, rejectedHandler);

// 线程池工厂
public static class EmailTaskThreadFactory implements ThreadFactory{
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;

    EmailTaskThreadFactory(){
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
        namePrefix = "EmailTaskThreadPool - " + poolNumber.getAndIncrement() + " - thread - ";
    }

    @Override
    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
        if(t.isDaemon()){
            t.setDaemon(false);
        }
        if (t.getPriority() != Thread.NORM_PRIORITY){
            t.setPriority(Thread.NORM_PRIORITY);
        }
        return t;
    }
}

public static class EmailTaskIgnorePolicy implements RejectedExecutionHandler{
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        // 线程池满时的回绝策略
        LOGGER.error(r.toString() + "job is rejected, because the thread pool is full!");

        // 判断线程池状态,等待一段时间后,再次执行
        if(!executor.isShutdown()) {
            try {
                Thread.sleep(10L);
            } catch (InterruptedException e) {
                LOGGER.error("回绝策略执行时,等待报错: ", e);
            }
            if (executor.getQueue().size() < executor.getMaximumPoolSize()) {
                executor.execute(r);
            }
        }
    }
}

       然后,考虑使用生产者,进行HTTP请求的发送处逻辑

if(taskRejectFlag.get() > 0){
    // 等待一段时间,再去请求服务方
    // 这里是我设置的一个AtomicInteger变量,会随定时器的执行,逐渐变小
    taskRejectFlag.getAndDecrement();
}
for(int i = 0;i < registrys.size();i++){
    RegistryDTO registry = registrys.get(i);
    Runnable thread = new Runnable() {
        @Override
        public void run() {
            List<String> parentNode = null;
            try {
                if (taskRejectFlag.get() == 0) {
                    parentNode = /** 发送HTTP请求,查询所需信息 **/;
                }
            } catch (IOException e) {
                // 发送请求失败时的重试机制
                int errorCode = Integer.parseInt(e.getMessage());
                if (errorCode != 404) {
                    try {
                        Thread.sleep(10L);
                        parentNode = /** 发送HTTP请求,查询所需信息 **/;
                    } catch (IOException ex) {
                        LOGGER.error("发送HTTP请求到服务方查询,重试失败。");
                    } catch (InterruptedException ex) {
                        LOGGER.error("重试请求到服务方等待时,报错:", ex);
                    }
                } else {
                    // 服务器挂了,停止查询服务方
                    // 对应上面的判断,会等待1小时
                    taskRejectFlag.set(60);
                }
            }

            // 原子操作,+1
            taskSendEmailFlag.getAndIncrement();

            // 只有当发送请求的实际数量taskSendEmailFlag(也是个AtomicInteger变量)和发送请求的总数量相等时,才执行如下操作,唤醒主线程
            if (taskSendEmailFlag.get() == registrys.size()) {
                try {
                    synchronized (lock) {
                        lock.notify();
                    }
                }catch (IllegalMonitorStateException e){
                    LOGGER.error("唤醒线程等待队列时,出错:", e);
                }
            }
        }
    };
    threadpool.execute(thread);
}

     然后,考虑使用消费者,主线程在等待所有子线程执行完毕之后,被唤醒以执行后续操作

try{
    synchronized (lock) {
        // 挂起主线程,等待所有子线程执行完毕后唤醒主线程
        if (taskSendEmailFlag.get() != registrys.size()) {
            LOGGER.info("挂起线程,等待查询服务方后再继续");
            lock.wait();
        }

        // 业务逻辑业务逻辑
        // 业务逻辑业务逻辑
        // 业务逻辑业务逻辑

    }
} catch (IllegalMonitorStateException | InterruptedException e) {
    LOGGER.error("等待查询服务方时,线程进行后续操作时报错:", e);
}
// 所有定时器逻辑执行完毕后,恢复为0
taskSendEmailFlag.set(0);

         感谢能看到最后。

从零手搓C++ HTTP服务器:网络编程、并发模型与协议解析实战 HTTP协议是Web通信的基石,它定义了客户端与服务器之间请求与响应的标准格式。其底层依赖于TCP协议,通过三次握手建立可靠连接,确保数据有序、无差错地传输。理解HTTP协议与TCP连接管理,对于构建高性能、高可用的网络服务具有核心价值,尤其在微服务、API网关和分布式系统等应用场景中至关重要。本文聚焦于C++网络编程实践,通过手把手实现一个支持HTTP/1.1的服务器,深入剖析Socket编程、请求解析、响应生成等底层细节。你将直面并发模型选择、线程池设计等工程挑战,并深刻理解"502 Bad Gatew 阅读详情

相关推荐

C++11轻量级Web服务器实现:从Reactor模式到线程池的实战解析

Web服务器是现代网络应用的核心基础设施,其核心原理在于高效处理并发连接与HTTP协议通信。通过I/O多路复用技术(如epoll)实现事件驱动,服务器能够以单线程监控大量连接,这是高并发架构的基石。结合线程池技术,将耗时的业务逻辑处理与高效的事件分发解耦,有效解决了阻塞与资源竞争问题,从而在工程实践中实现高性能与可扩展性。这种Reactor模式与线程池的组合,广泛应用于后端服务、API网关等需要处理海量并发的场景。本文以C++11实现的轻量级Web服务器为例,深入剖析其核心架构,涵盖事件分发、连接管理、HT

weixin_33674976的博客 375

线程池处理高并发请求

背景:本系统会在每个月特定时间接收上游系统发起的大量请求并进行处理,并将处理结果通过主动回调或提供查询接口的方式反馈给上游系统。

xiaoyi52的专栏 8386

C++高性能Web服务器TinyWebServer源码解析与并发模型实践

在Linux服务器开发中,I/O多路复用与并发模型是构建高性能网络服务的核心基础。epoll作为Linux下高效的I/O事件通知机制,通过水平触发(LT)与边沿触发(ET)两种模式管理海量连接,其本质是操作系统提供的事件驱动编程接口。结合线程池技术,可构建出Reactor与Proactor两种经典并发架构,前者将I/O操作交由工作线程处理,后者则由主线程完成数据读写,实现业务逻辑与I/O的解耦,从而大幅提升吞吐量与资源利用率。这些技术广泛应用于需要高并发的Web服务器、API网关及实时通信系统中。本文以Gi

weixin_30457551的博客 625

多线程并发http任务的设计

假设某用户有23个销售员,每个销售员,每月会分别有其“销售明细报表”和“费用明细报表”,来方便会计与其核对“销售提成报表”的数据的正确性,那么每个月,会产生23*2+1 = 47个报表,其中,“销售明细报表”的数据流相对是比较大的,明细到每单、每笔产品的销售明细;这个数值10,也是并发压测时,同步刷新UI的上限,超出这个数值,UI界面,在线程的同步函数中,是刷新不过来的,必须当前线程稍作内旋暂停后再回到该线程,即:每执行10次并发请求,刷新一次界面,否则刷新不过来。原本,底层TCP协议最初,是不限制的;

pulledup的博客 2196

C#线程模型重校准:从线程池饥饿到异步状态机

在现代.NET应用中,线程已不再是简单的执行单元,而是受CLR调度器、同步上下文和异步状态机共同约束的资源载体。理解线程池饥饿成因与工作窃取机制,是避免CPU飙升与请求堆积的关键;掌握`SynchronizationContext`与`ConfigureAwait(false)`的协同逻辑,则决定了`await`回调的线程归属与上下文开销。这些底层原理直接关联高并发Web服务、定时任务调度、UI响应等典型场景。本文聚焦.NET 6+运行时下的真实线程行为,结合`dotnet-trace`诊断、IL级状态机分

aobai7842的博客 395

C++手搓WebServer:从Socket到epoll的并发服务器实战

网络编程是构建高性能服务端应用的核心技术,其原理基于操作系统提供的Socket API和I/O模型。通过理解TCP/IP协议栈和事件驱动机制,开发者能够构建高并发、低延迟的网络服务,这对于后端开发和系统架构至关重要。在众多I/O模型中,I/O多路复用技术,特别是Linux下的epoll,因其高效的事件通知机制,成为实现高并发服务器的关键技术。其技术价值在于能够以少量线程管理海量连接,显著提升资源利用率和系统吞吐量。典型的应用场景包括Web服务器、API网关和实时通信服务等。本文聚焦于使用C++从零实现一个轻

cumo3681的博客 548

Java使用线程池执行定时任务

从上述结果可以看出,定时任务在 3s 之后开始执行,以后每隔 4s 执行一次,这 4s 包含了,定时任务执行花费的 2s,加上每隔 2s 执行一次的时间间隔,也就是说 scheduleWithFixedDelay 是在任务执行完 N 秒之后,再执行下一次定时任务。使用 scheduleWithFixedDelay 方法执行定时任务,执行多次定时任务,它是在定时任务执行完之后,再隔 N 秒开始执行下一次定时任务,它的执行时间受定时任务执行时长影响。第 2 个参数:添加定时任务后,再过多久开始执行定时任务;..

zilin-lynn的博客 3912

C语言手搓Web服务器:从Socket到epoll的并发架构实战

网络编程是后端开发的核心基础,其本质在于如何高效管理多个网络连接。I/O多路复用技术通过一个系统调用监控多个文件描述符的状态变化,是实现高并发服务器的关键技术原理。这种事件驱动模型的价值在于,它能以极少的线程资源支撑海量连接,显著提升系统吞吐量。在Web服务器、即时通讯等需要处理大量并发请求的应用场景中,I/O多路复用结合线程池的架构已成为行业标准实践。本文聚焦于使用C语言从零构建此类服务器,深入剖析如何将epoll事件驱动引擎与线程池任务分发机制相结合,以解决经典的C10K高并发问题,并涵盖HTTP协议解

weixin_30736301的博客 415

线程池 http请求

package com.aibi.cmdc.test; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import...

ankuaixiao2504的博客 222

C++后端开发实战指南:从网络编程到高性能服务器架构

网络编程是构建高性能后端服务的核心技术,其核心在于理解I/O模型与并发处理机制。从基础的Socket API到高效的I/O多路复用(如epoll),开发者需要掌握如何管理连接、处理数据以及避免性能瓶颈。多线程编程则通过锁、条件变量等同步机制解决数据竞争问题,而设计模式(如Reactor)为高并发场景提供了清晰的架构范式。这些技术的结合,使得开发者能够构建出支撑海量请求的稳定服务,广泛应用于Web服务器、即时通讯、金融交易等场景。本文以C++后端开发为切入点,系统梳理了从环境搭建、核心语法到网络编程、多线程及

weixin_30838873的博客 437

springmvc+spring线程池处理http并发请求数据同步控制问题

springmvc+spring线程池处理http并发请求数据同步控制问题

java使用HttpClient线程池支持大量并发发起http请求

package com.ig.common.util; import com.ig.common.utils.PropertiesHander; import org.apache.commons.lang3.StringUtils; import org.apache.http.HeaderElement; import org.apache.http.HeaderElementIterato...

u010486495的专栏 1万+

多线程控制,线程池模式,HTTP线程

多线程控制 线程池模式 HTTP线程 用户点击数量控制在一个线程池模式下

C++高并发Web服务器实战:从Reactor到Proactor的架构设计与实现

网络编程是现代软件开发的核心技能之一,其本质是通过操作系统提供的套接字接口实现进程间通信。在Linux环境下,epoll作为高效的I/O多路复用机制,通过事件驱动模型管理大量并发连接,解决了传统阻塞I/O的性能瓶颈。结合线程池技术,能够将计算密集型任务与I/O操作解耦,显著提升系统吞吐量。这种架构模式在需要高并发处理的场景中具有重要价值,例如实时通信系统、在线交易平台和内容分发网络。本文以C++轻量级Web服务器为例,深入解析半同步/半反应堆线程池模型的设计原理,探讨Reactor与模拟Proactor两种

380

单细胞分析实战:用R语言绘制带连线的堆叠柱状图(附完整代码)

本文详细介绍了如何使用R语言绘制带连线的堆叠柱状图进行单细胞转录组数据分析,特别适用于展示细胞比例变化趋势。通过完整代码示例和实战技巧,帮助研究者优化多组别比较的可视化效果,提升科研图表质量。

yhn4567890的博客 390

别再纠结Dify了!实测对比RAGFlow和Dify,聊聊企业知识库到底该怎么选

本文深度对比了RAGFlow和Dify在企业知识库选型中的实际表现,从技术架构、性能指标、部署成本等多维度进行分析。RAGFlow在复杂文档处理和准确率方面表现突出,适合有专职技术团队的企业;而Dify则以低门槛和快速部署见长。文章提供了六维度评估法,帮助企业根据文档复杂度、准确率要求等技术指标做出明智选择。

weixin_30897233的博客 675

上位机定时器管理实战:单一定时器与任务调度机制详解

在上位机开发中,定时器是实现周期性数据采集、界面刷新、心跳上报与超时检测等基础功能的核心组件。然而,多Timer滥用常导致UI卡顿、逻辑混乱和不可维护等问题。本文从定时器类型与工作原理入手,对比System.Windows.Forms.Timer、System.Threading.Timer、System.Timers.Timer及Task.Delay的适用场景,剖析多任务定时管理中的典型陷阱。针对“多动症”程序结构,提出“单一定时器 + 任务调度表”的设计模式,通过统一调度循环、防重入标志、异常隔离及Be

weixin_34236869的博客 582

差分运放电路输出电压偏置设计解析

差分运算放大电路是信号处理中的基础模块,通过差分对管结构实现共模抑制与差模放大。其核心原理基于运放的虚短虚断特性,当电阻网络满足对称条件时,输出电压与输入差分成正比。在工业测量系统中,由于ADC通常仅支持正电压输入,需要通过偏置电路将双极性信号平移至正电压范围。典型设计采用精密基准电压源和匹配电阻网络,既保持信号完整性又满足系统接口要求。本文以-100V高压测量为例,详细解析了分压网络设计、参考电压选取以及电阻匹配等关键技术要点,为传感器接口电路设计提供实践参考。

weixin_35750747的博客 471

Dify API批量处理效率翻倍:3步搞定标准请求格式构建

掌握Dify API批量请求格式,3步构建高效请求,提升处理效率。适用于多任务并发、数据同步等场景,通过标准化参数封装与异步调用,显著降低响应延迟。方法简单实用,开发者快速上手,值得收藏。

BreakNexus的博客 844

手把手教你使用YOLOv11完成人脸分类识别,YOLOv11人脸识别,解决中文乱码问题,全网最详细教程

手把手教你使用YOLOv11完成人脸分类识别,YOLOv11人脸识别,解决中文乱码问题,全网最详细教程

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 4449

Tesla v100 4张显卡组装机安装vllm和ollama在Ubuntu系统上的安装---AI大模型系统从零开始0032

本文提供Ubuntu22.04系统下vLLM的完整安装指南,适配4×32G Tesla显卡的单机4卡张量并行部署。教程包含:1)硬件要求(Ubuntu22.04、NVIDIA驱动≥535、CUDA12.1+);2)NVIDIA驱动自动安装方法;3)Python3.10虚拟环境配置;4)vLLM官方预编译包一键安装;5)单机4卡启动Qwen3.6-27B模型的完整参数说明(含FP8量化、多进程并行等关键配置)。文末附分布式部署的Ray集群方案参考链接,适用于多机8卡场景。

添柴程序猿的专栏 188

C++实战项目入门指南:从语法到工程,精选十大练手项目

C++作为一门系统级编程语言,其核心价值在于将底层硬件控制与高级抽象相结合,通过面向对象、模板元编程等机制实现高性能与灵活性。理解其内存管理、多态机制及标准模板库(STL)的原理,是掌握现代C++开发的基础。在工程实践中,这些特性直接转化为构建高性能服务器、游戏引擎、嵌入式系统等核心应用的能力。对于初学者而言,理论学习后常面临“无从下手”的困境,关键在于通过**实战项目**搭建从知识到技能的桥梁。本文聚焦**C++项目开发实战**,系统梳理了从控制台工具到网络服务器的项目分类,并深度解析了如TinyWebS

babing2770的博客 454

C#部署YOLO三种方案深度实测:OpenCV DNN vs ONNX Runtime vs TensorRT

在工业视觉项目落地阶段,C#生态下部署YOLO模型主要有三条技术路线。很多开发者仅凭经验选型,往往在部署后期踩中性能瓶颈、兼容故障、维护成本超标的坑。本文基于工控机常见硬件配置,针对YOLOv8系列模型做控制变量实测,从推理性能、部署难度、工业稳定性、综合成本等多个维度做横向对比,给出可直接落地的选型结论。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 470

cvte面筋(2018秋招提前批)

面试岗位: C/C++后台开发工程师 cvte一面(电话面,22分钟) 有没有用c++做过什么项目(畅聊) 讲一下项目 序列化和反序列化怎么实现的 ,还问了一下jsoncpp这个库 当前是一个聊天室,如何扩展为多个 传输过程是用什么协议完成的 为什么选择udp udp和tcp的区别 对tcp的滑动窗口的了解 ......

J4Ya_的博客 2009

Allegro如何输出ODB文件操作指导

在PCB设计完成之后,需要输出生产文件用于生产加工,除了gerber文件可以用生产制造,ODB文件同样也可以用于生产,如下图。用Allegro如何输出ODB文件,具体操作如下。如何输出ODB文件操作指导。

weixin_54787054的博客 7218

HyperLynx仿真(一)LineSim简单介绍

可以选择设置信号线与信号线之间默认的距离和信号线与同平面的平面之间的距离,这些设置根据工程师项目上经常使用的情况而设置,也可以选择不设置,在每次仿真时根据需要再对每一个仿真单独设置。仿真属性的设置对话框包含一般设置、 显示颜色设置、LineSim 设置、BoardSim 设置、 层叠默认设置、焊盘类型设置、仿真示波器默认设置、电路仿真器默认设置、电源完整性设置及高级设置。第一次使用HyperLynx软件时需要设置仿真相关环境,包括仿真软件的基本设置、模型库路径设置、仿真控制器控制、单位等。...

小幽余生不加糖 8955
上一篇: jdbcTemplate使用变量名的方式替换SQL,实现批量查询和模糊查询
下一篇: Java下得到ArrayList<String>占用内存大小的方法
CCCAAAAFFFFF
博客等级 码龄10年 5粉丝 27原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值