从构建分布式秒杀系统聊聊线程池

前言

从0到1构建分布式秒杀系统案例的代码已经全部上传至码云,文章也被分发到各个平台。其中也收到了不少小伙伴喜欢和反馈,有网友如是说:

说实话,能用上的不多,中小企业都不可能用到,大型企业也不是一个人就能搞起的,大部分人一辈子都用不上,等有这个需要再搞吧。

我的观点是赞同但不支持,基本上任何事物都是呈金字塔分布,互联网也不例外,也就是说大部分可能都是普通人,接触不到所谓大厂的应用场景。但是,书到用时方恨少,机会总是留给有准备的人的,除非有钱难买我乐意,只能说大千世界,每个人都有自己的生活方式,尊重并活着。

进程和线程

前面都是扯淡,也不是什么铺垫,在聊线程池之前我们最好简单了解下什么是进程,什么是线程,进程和线程到底有什么区别?

这里我们,搬运下某百科的释义:

进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

当然,知乎上也有不少网友的回答,每个人都有自己不同的理解方式。这里我们拿Tomcat容器做例子:你可以这么理解,运行中的Tomcat容器就是一个进程,而每个用户的操作(查询、上传)可以当做一个或者多个线程。

线程池

秒杀活动中,瞬时并发是非常大的,如果每一个请求都开启一个新线程,系统就要不断的进行线程的创建和销毁,有时花在创建和销毁线程上的时间会比线程真正执行的时间还长。并且由于硬件条件限制,线程数量又不能无限创建。

那么线程池到底解决了那些问题:

  • 降低资源消耗:通过重用已经创建的线程来降低线程创建和销毁的消耗
  • 提高响应速度:任务到达时不需要等待线程创建就可以立即执行
  • 提高线程的可管理性:线程池可以统一管理、分配、调优和监控
执行流程
  • 调用ThreadPoolExecutor的execute提交线程,首先检查CorePool,如果CorePool内的线程小于CorePoolSize,新创建线程执行任务。
  • 如果当前CorePool内的线程大于等于CorePoolSize,那么将线程加入到BlockingQueue。
  • 如果不能加入BlockingQueue,在小于MaxPoolSize的情况下创建线程执行任务。
  • 如果线程数大于等于MaxPoolSize,那么执行拒绝策略。
模拟测试

为了方便测试,我们在Control中定义了线程池,来模拟用户秒杀动作:

定义初始线程数:

private static int corePoolSize = Runtime.getRuntime().availableProcessors();
  • IO密集型任务 = 一般为2*CPU核心数(常出现于线程中:数据库数据交互、文件上传下载、网络数据传输等等)
  • CPU密集型任务 = 一般为CPU核心数+1(常出现于线程中:复杂算法)
  • 混合型任务 = 视机器配置和复杂度自测而定

定义Executor:

private static ThreadPoolExecutor executor  = new ThreadPoolExecutor(corePoolSize, corePoolSize+1, 10l, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>(1000));
  • corePoolSize用于指定核心线程数量
  • maximumPoolSize指定最大线程数
  • keepAliveTime和TimeUnit指定线程空闲后的最大存活时间
  • workQueue则是线程池的缓冲队列,还未执行的线程会在队列中等待,监控队列长度,确保队列有界;不当的线程池大小会使得处理速度变慢,稳定性下降,并且导致内存泄露。如果配置的线程过少,则队列会持续变大,消耗过多内存;而过多的线程又会 由于频繁的上下文切换导致整个系统的速度变缓——殊途而同归。队列的长度至关重要,它必须得是有界的,这样如果线程池不堪重负了它可以暂时拒绝掉新的请求。
  • ExecutorService 默认的实现是一个无界的LinkedBlockingQueue。

Tomcat线程池

以上只是为了测试方便,模拟出的数据。真实的生产环境,我们要接入Nginx和Tomcat来处理用户的请求。而Tomcat作为一名容器也是有自己的一套连接池的,作为开发人员你并不需要自己去实现。

Tomcat默认使用自带的连接池,这里我们也可以自定义实现,打开/conf/server.xml文件,在Connector之前配置一个线程池:

<Executor name="tomcatThreadPool"   
        namePrefix="tomcatThreadPool-"   
        maxThreads="1000"   
        maxIdleTime="300000"  
        minSpareThreads="200"/>  
  • name:共享线程池的名字。这是Connector为了共享线程池要引用的名字,该名字必须唯一。默认值:None;
  • namePrefix:在JVM上,每个运行线程都可以有一个name 字符串。这一属性为线程池中每个线程的name字符串设置了一个前缀,Tomcat将把线程号追加到这一前缀的后面。默认值:tomcat-exec-;
  • maxThreads:该线程池可以容纳的最大线程数。默认值:200;
  • maxIdleTime:在Tomcat关闭一个空闲线程之前,允许空闲线程持续的时间(以毫秒为单位)。只有当前活跃的线程数大于minSpareThread的值,才会关闭空闲线程。默认值:60000(一分钟)。
  • minSpareThreads:Tomcat应该始终打开的最小不活跃线程数。默认值:25。

配置Connector:

<Connector executor="tomcatThreadPool"
           port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443"
           minProcessors="5"
           maxProcessors="75"
           acceptCount="1000"/>
  • executor:表示使用该参数值对应的线程池;
  • minProcessors:服务器启动时创建的处理请求的线程数;
  • maxProcessors:最大可以创建的处理请求的线程数;
  • acceptCount:指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理。

思考

  • 为什么线程数最好不要太大于CPU核数?
  • 为什么Tomcat中默认线程数远大于CPU核数?

代码案例:从0到1构建分布式秒杀系统

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏木可大大

漫谈虚拟内存

如上图,程序1、程序2、程序3装入到内存,而程序2运行完成被换出,内存空闲出20k,然后进来程序4,大小为25K,此时,只有两处空闲块,10K和20K,没有一处...

5234
来自专栏社区的朋友们

TAF 必修课(六):容错

上一节简单提到了客户端在选取 Invoker 节点时,会对 Invoker 列表执行死活检查,屏蔽掉一定时间内异常的节点,从而达到容灾的目的。下面对 TAF 容...

3290
来自专栏青蛙要fly的专栏

Android技能树 — 网络小结(4)之socket/websocket/webservice

介于自己的网络方面知识烂的一塌糊涂,所以准备写相关网络的文章,但是考虑全部写在一篇太长了,所以分开写,希望大家能仔细看,最好可以指出我的错误,让我也能纠正。

973
来自专栏Seebug漏洞平台

TP-LINK 远程代码执行漏洞 CVE-2017-13772 趣谈

原文地址:https://www.fidusinfosec.com/tp-link-remote-code-execution-cve-2017-13772/ ...

3536
来自专栏Golang语言社区

Go微服务 - 构建我们的第一个服务

虽然通过HTTP提供JSON服务不是内部服务和外部服务的唯一选择,但本文聚焦的是HTTP和JSON. 使用RPC机制和二进制消息格式(例如Protocol Bu...

2084
来自专栏我的小碗汤

6个最好的Go语言Web框架

原文:Top 6 web frameworks for Go as of 2017

1451
来自专栏Jaycekon

Python-WXPY实现微信监控报警

概述:   本文主要分享一下博主在学习wxpy 的过程中开发的一个小程序。博主在最近有一个监控报警的需求需要完成,然后刚好在学习wxpy 这个东西,因此很巧妙的...

1.7K8
来自专栏13blog.site

小测试

可以在 @RequestMapping 注解里面加上 method=RequestMethod.GET 或者使用 @GetMapping 注解

1471
来自专栏杨建荣的学习笔记

Greenplum集群问题修复案例

我看了下GP Master端,看到负载并不高,当然这是一个初步的检测,如果集群响应缓慢,则很可能是segment节点上出现了延迟。一看则吓一跳,这是一个segm...

1062
来自专栏FreeBuf

注意了,使用XSS平台的你可能被“偷窥”

故君子之治人也,即以其人之道,还治其人之身。 Par1:你要了解的事 XSS平台: 玩渗透测试的人,对XSS平台应该不会陌生。 最简单的XSS平台,通常可以记录...

3308

扫码关注云+社区