l1n6yun's Blog

记录学习的技能和遇到的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* Redis 初始化
*
* @param int $goodsId 商品ID
* @param int $number 商品数量
*/
function initRedis($goodsId, $number)
{
$storeKey = "goods_{$goodsId}_store";

$redis = new redis();
$redis->connect('127.0.0.1', 6379);

$redis->del($storeKey);

for ($i = 0; $i < $number; $i++) {
$redis->lpush($storeKey, 1);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* 秒杀入口
*
* @param int $goodsId 商品ID
* @return bool
*/
function kills($goodsId)
{
$storeKey = "goods_{$goodsId}_store";

$redis = new redis();
$redis->connect('127.0.0.1', 6379);

$count = $redis->lpop($storeKey);
if ($count) {
$data = "";
$data["order_sn"] = rand(1000, 9999); // 测试订单号

// 1. 直接进行数据库操作
$orderModel = new orderModel();
$result = $orderModel->save($data);

if ($result !== false) {
return true;
} else {
$redis->lpush($storeKey, 1);
return false;
}

// 2. 添加队列后继操作
Queue::push($job, $data, 'seckill');
}
}

错误代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
java.lang.RuntimeException: com.mysql.cj.exceptions.InvalidConnectionAttributeException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:61)
at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:85)
at com.mysql.cj.util.TimeUtil.getCanonicalTimezone(TimeUtil.java:132)
at com.mysql.cj.protocol.a.NativeProtocol.configureTimezone(NativeProtocol.java:2241)
at com.mysql.cj.protocol.a.NativeProtocol.initServerSession(NativeProtocol.java:2265)
at com.mysql.cj.jdbc.ConnectionImpl.initializePropsFromServer(ConnectionImpl.java:1319)
at com.mysql.cj.jdbc.ConnectionImpl.connectWithRetries(ConnectionImpl.java:868)
at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:830)
at com.mysql.cj.jdbc.ConnectionImpl.&lt;init&gt;(ConnectionImpl.java:455)
at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:240)
at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:199)
at com.intellij.database.remote.jdbc.impl.RemoteDriverImpl.connect(RemoteDriverImpl.java:41)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:357)
at sun.rmi.transport.Transport$1.run(Transport.java:200)
at sun.rmi.transport.Transport$1.run(Transport.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:573)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:834)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:688)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:687)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748) (no stack trace).
com.mysql.cj.exceptions.InvalidConnectionAttributeException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.

解决方法

advanced 选项中找到 serverTimezone 设置为 UTC

设置cookie

1
2
3
4
5
6
7
function setCookie(name,value) 
{
var Days = 30;
var exp = new Date();
exp.setTime(exp.getTime() + Days*24*60*60*1000);
document.cookie = name + "="+ escape (value) + ";expires=" + exp.toGMTString();
}

读取cookie

1
2
3
4
5
function getCookie(name) 
{
var arr,reg=new RegExp("(^| )"+name+"=([^;]*)(;|$)");
   return (arr=document.cookie.match(reg))?unescape(arr[2]):null;
}

删除cookie(将cookie设置过期即可)

1
2
3
4
5
6
7
8
function delCookie(name) 
{
var exp = new Date();
exp.setTime(exp.getTime() - 1);
var cval=getCookie(name);
if(cval!=null)
document.cookie= name + "="+cval+";expires="+exp.toGMTString();
}

escape(string) 函数可对字符串进行编码,这样就可以在所有的计算机上读取该字符串。
unescape(string) 函数可对通过 escape() 编码的字符串进行解码。

正则表达式(Regular Expression)是一种文本模式,包含普通字符(列如,a 到 z 之间的字母)和特殊字符(元字符)。

正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。

正则表达式 - 语法

普通字符串

字符 描述
[ABC] 匹配 [...] 中的所有字符。例如用 /[lny]/g 来匹配字符串 “l1n6yun”,可以匹配到 l,n,y,n
[!ABC] 匹配除了 [!...] 中的所有字符。例如 /[^lny]/g 来匹配字符串 “l1n6yun”,可以匹配到 1,6,u
[A-Z] [a-z] [A-Z] 表示一个区间,匹配所有大写字母。[a-z] 表示匹配所有小写字母。
. 匹配除换行符 ( \r\n) 之外的任何单字符,相当于 [^\r\n]
[\s\S] 匹配所有。\s 是匹配所有空白符,包含换行,\S 非空白符,不包含换行
\w 匹配字母、数字、下划线。等价于 [A-Za-z0-9_]

非打印字符

字符 描述
\cx 匹配由x指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Za-z 之一。否则,将 c 视为一个原义的 ‘c’ 字符。
\f 匹配一个换页符。等价于 \x0c\cL
\n 匹配一个换行符。等价于 \x0a\cJ
\r 匹配一个回车符。等价于 \x0d\cM
\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [\f\n\r\t\v]。注意 Unicode 正则表达式会匹配全角空格符。
\S 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]
\t 匹配一个制表符。等价于 \x09\cI
\v 匹配一个垂直制表符。等价于 \x0b\cK
\xn 匹配 n,其中 n 为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,’\x41’ 匹配 “A”。’\x041’ 则等价于 ‘\x04’ & “1”。正则表达式中可以使用 ASCII 编码。
\n 标识一个八进制转义值或一个向后引用。如果 \n 之前至少 n 个获取的子表达式,则 n 为向后引用。否则,如果 n 为八进制数字 (0-7),则 n 为一个八进制转义值。
\nm 标识一个八进制转义值或一个向后引用。如果 \nm 之前至少有 nm 个获得子表达式,则 nm 为向后引用。如果 \nm 之前至少有 n 个获取,则 n 为一个后跟文字 m 的向后引用。如果前面的条件都不满足,若 n 和 m 均为八进制数字 (0-7),则 \nm 将匹配八进制转义值 nm。
\nml 如果 n 为八进制数字 (0-3),且 m 和 l 均为八进制数字 (0-7),则匹配八进制转义值 nml。
\un 匹配 n,其中 n 是一个用四个十六进制数字表示的 Unicode 字符。例如, \u00A9 匹配版权符号 (?)。

特殊字符

特别字符 描述
$ 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 ‘\n’ 或 ‘\r’。要匹配 $ 字符本身,请使用 $。
( ) 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 ( 和 )。
* 匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 *。
+ 匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 +。
. 匹配除换行符 \n 之外的任何单字符。要匹配 . ,请使用 . 。
[ 标记一个中括号表达式的开始。要匹配 [,请使用 [。
? 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 ?。
\ 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, ‘n’ 匹配字符 ‘n’。’\n’ 匹配换行符。序列 ‘\‘ 匹配 “",而 ‘(‘ 则匹配 “(“。
^ 匹配输入字符串的开始位置,除非在方括号表达式中使用,当该符号在方括号表达式中使用时,表示不接受该方括号表达式中的字符集合。要匹配 ^ 字符本身,请使用 ^。
{ 标记限定符表达式的开始。要匹配 {,请使用 {。
| 指明两项之间的一个选择。要匹配 |,请使用 |。

限定符

字符 描述
* 匹配前面的子表达式零次或多次。***** 等价于 {0,}
+ 匹配前面的子表达式一次或多次。+ 等价于 {1,}
? 匹配前面的子表达式零次或一次。? 等价于 {0,1}
{n} n 是一个非负整数。匹配确定的 n 次。
{n,} n 是一个非负整数。至少匹配n 次。
{n,m} m 和 n 均为非负整数,其中 n <= m。

*+ 限定符都是贪婪的,因为它们会尽可能多的匹配文字,只有在它们的后面加上一个 ? 就可以实现非贪婪或最小的匹配。

定位符

定位符使您能够将正则表达式固定到行首或行尾。他们还使您能够创建这样的正则表达式,这些正则表达式出现在一个单词内、在一个单词的开头或者一个单词的结尾。

字符 描述
^ 匹配输入字符串开始的位置。如果设置了 RegExp 对象的 Multiline 属性,^ 还会与 \n 或 \r 之后的位置匹配。
$ 匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性,$ 还会与 \n 或 \r 之前的位置匹配。
\b 匹配一个单词边界,即字与空格间的位置。例如”Hello World”,结果”\dHello\d \dWorld\d”
\B 非单词边界匹配。例如”Hello World”,结果”H\Be\Bl\Bl\Bo W\Bo\Br\Bl\Bd”

选择

用圆括号 () 将所有选择项括起来,相邻的选择项之间用 | 分割。

1
/([1-9])([a-z]+)/g
表达式 描述
exp1(?=exp2) 查找 exp2 前面的 exp1
(?<=exp2)exp1 查找 exp2 后面的 exp1
exp1(?!exp2) 查找后面不是 exp2 的 exp1
(?<!exp2)exp1 查找前面不是 exp2 的 exp1

反向引用

对一个正则表达式模式或部分模式两边添加圆括号将导致相关匹配存储到一个临时缓冲区中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。缓冲区编号从 1 开始,最多可存储 99 个捕获的子表达式。每个缓冲区都可以使用 \n 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。

可以使用非捕获元字符 ?:?=?! 来重写捕获,忽略对相关匹配的保存。

反向引用的最简单的、最有用的应用之一,是提供查找文本中两个相同的相邻单词的匹配项的能力。以下面的句子为例:

1
Is is the cost of of gasoline going up up?

上面的句子很显然有多个重复的单词。如果能设计一种方法定位该句子,而不必查找每个单词的重复出现,那该有多好。下面的正则表达式使用单个子表达式来实现这一点:

1
2
3
var str = "Is is the cost of of gasoline going up up";
var patt1 = /\b([a-z]+) \1\b/igm;
document.write(str.match(patt1));

捕获的表达式,正如 [a-z]+ 指定的,包括一个或多个字母。正则表达式的第二部分是对以前捕获的子匹配项的引用,即,单词的第二个匹配项正好由括号表达式匹配。\1 指定第一个子匹配项。

单词边界元字符确保只检测整个单词。否则,诸如 “is issued” 或 “this is” 之类的词组将不能正确地被此表达式识别。

正则表达式后面的全局标记 g 指定将该表达式应用到输入字符串中能够查找到的尽可能多的匹配。

表达式的结尾处的不区分大小写 i 标记指定不区分大小写。

多行标记 m 指定换行符的两边可能出现潜在的匹配。

正则表达式 - 修饰符

标记也称为修饰符,正则表达式的标记用于指定额外的匹配策略。

1
/pattern/flags

下表列出了正则表达式常用的修饰符

修饰符 含义 描述
i ignore - 不区分大小写 将匹配设置为不区分大小写,搜索时不区分大小写: A 和 a 没有区别。
g global - 全局匹配 查找所有的匹配项。
m multi line - 多行匹配 使边界字符 ^$ 匹配每一行的开头和结尾,记住是多行,而不是整个字符串的开头和结尾。
s 特殊字符圆点 . 中包含换行符 \n 默认情况下的圆点 . 是匹配除换行符 \n 之外的任何字符,加上 s 修饰符之后, . 中包含换行符 \n。

最近想离线浏览一个网站,发现 wget 和 HTTrack 这两个工具都挺好用的。一个命令行操作,一个图形界面,各有各的优势。

遇到的场景

事情是这样的,某天看到一篇很棒的教程网站,内容特别丰富,但是那个网站偶尔会挂,而且有时候网速还特别慢。我就想着,要是能把它整个下载下来离线看就好了。

方式一:wget

wget 是个命令行工具,Linux 系统基本都自带。之前一直知道它可以下载文件,没想到还能直接把整个网站”搬”到本地来。

基本用法

最简单的镜像命令:

1
wget --mirror https://example.com

不过这样下载下来的链接还是指向原网站的,点击就跳转走了,体验不太好。所以一般我会加几个参数:

1
wget --mirror --convert-links --adjust-extension --page-requisites --no-parent https://example.com

看着一长串参数有点吓人,其实就是告诉 wget:

  • --mirror:开启镜像模式,会递归下载
  • --convert-links:把链接转换成本地链接,这样点击就不会跳到原网站了
  • --adjust-extension:自动加上正确的文件后缀
  • --page-requisites:把 CSS、图片这些资源也一起下载
  • --no-parent:不要往上跑到父目录去

有个简写的形式,好记多了:

1
wget -mkEpnp https://example.com

踩过的坑

刚开始用的时候踩了几个坑:

下载太多层级。有些网站链接四通八达,不加限制的话可能会下载一大堆不需要的东西。可以用 -l 参数限制深度:

1
wget -r -l 2 https://example.com  # 只下载2层

被服务器封 IP。wget 默认下载速度很快,有些服务器会认为是在攻击。所以最好加点延迟:

1
wget --mirror --wait=2 --limit-rate=200k https://example.com

这样每次请求之间等 2 秒,速度限制在 200KB/s,服务器就不会觉得你在搞事情了。

优点与局限

wget 的优点是简单直接,一行命令搞定,Linux 系统自带不用安装。缺点是只有命令行,不熟悉终端的话上手有点门槛。

另外,wget 对动态渲染的网站支持不太好。现在很多网站内容是通过 JavaScript 动态加载的,这种网站 wget 只能抓到空的 HTML 框架。

方式二:HTTrack

HTTrack 是一个专门做网站镜像的工具,有图形界面,用起来比 wget 友好很多。

安装

  • Windows:去官网下载安装包,一路下一步就行
  • Linux:sudo apt install httrack
  • Mac:brew install httrack

图形界面使用

安装后打开 HTTrack,跟着向导走就行:

  1. 新建项目,起个名字
  2. 选择保存路径
  3. 输入要镜像的网址
  4. 点击开始,等待完成

整个过程点点鼠标就搞定了,完全不需要记命令。

命令行使用

HTTrack 也支持命令行:

1
httrack https://example.com -O ./mirrored-site

-O 参数指定保存目录。如果需要更多控制,可以加参数:

1
httrack https://example.com -O ./mirrored-site -r2 -s0
  • -r2:递归深度为 2
  • -s0:不跟随外部链接

优点与局限

HTTrack 的优点是有图形界面,操作直观,配置选项丰富,还可以暂停续传。缺点是体积比 wget 大,需要单独安装。

和 wget 一样,HTTrack 对 JavaScript 渲染的内容也支持有限。

小结

两个工具各有千秋:

  • wget:命令行党首选,轻量、快速、Linux 自带
  • HTTrack:图形界面友好,适合不想折腾命令行的朋友

对了,镜像网站的时候记得看看人家网站的 robots.txt 和使用条款,别给人家服务器造成太大压力,也不要拿去干不好的事情哈。

在使用 Git 进行版本控制时,很多开发者都会遇到一个常见问题:空目录无法被 Git 直接跟踪和提交。这是因为 Git 的设计理念是跟踪文件的变更,而非目录本身 —— 如果一个目录下没有任何文件,Git 会默认忽略它,导致团队协作时目录结构无法同步。别担心,本文将分享 3 种经过实践验证的解决方案,帮你优雅解决空目录提交问题。

一、推荐方案:使用.gitkeep 文件(最通用)

这是开发中最常用的方法,虽然.gitkeep并非 Git 官方定义的标准文件,但已被 Laravel、Vue 等主流框架广泛采用,成为行业惯例。

核心原理

Git 会跟踪包含文件的目录结构。通过在空目录中创建一个名为.gitkeep的占位文件,就能强制 Git 识别并保留该目录,且不会影响目录的后续使用。

操作步骤(含示例)

  1. 创建空目录及占位文件

    无论是单个空目录还是多级空目录,都可以通过简单命令创建:

    1
    2
    3
    4
    5
    6
    7
    # 创建单个空目录并添加.gitkeep
    mkdir -p project/logs/
    touch project/logs/.gitkeep

    # 创建多级空目录并添加.gitkeep
    mkdir -p src/utils/temp/
    touch src/utils/temp/.gitkeep
  2. 提交到 Git 仓库

    常规的addcommit操作即可完成跟踪:

    1
    2
    git add project/logs/.gitkeep
    git commit -m "Track logs directory with .gitkeep"
  3. 批量处理所有空目录(高效技巧)

    如果项目中有多个空目录,手动创建太繁琐,可通过find命令批量处理:

    1
    find . -type d -empty -exec touch {}/.gitkeep \;

    这条命令会自动查找项目中所有空目录,并在每个目录下创建.gitkeep文件。

核心优势

  • 命名直观:看到.gitkeep就知道是用于保留目录的占位文件
  • 无副作用:仅作为占位符,不影响目录后续文件的添加和跟踪
  • 兼容性强:所有 Git 版本均支持,团队协作无门槛

二、灵活方案:.gitignore 反向配置

如果你的空目录未来需要添加文件,但又想提前保留目录结构,同时避免误跟踪无关文件,这种方法再合适不过。

核心原理

利用.gitignore的忽略规则:先配置忽略目录内所有文件(*),再通过反向规则(!.gitignore)排除自身,让 Git 仅跟踪.gitignore文件,从而间接保留目录结构。

操作步骤

  1. 创建特殊配置的.gitignore 文件

    在需要跟踪的空目录下创建.gitignore,并写入以下内容:

    1
    2
    3
    4
    # 忽略当前目录下所有文件
    *
    # 不忽略.gitignore文件本身
    !.gitignore
  2. 提交配置文件

    1
    2
    git add path/to/empty_dir/.gitignore
    git commit -m "Track empty directory via .gitignore"

适用场景

  • 目录未来会添加文件,但需要明确忽略规则(如日志目录、缓存目录)
  • 避免团队成员误提交目录内的临时文件

核心优势

  • 规则清晰:通过配置文件明确目录的忽略策略
  • 一劳永逸:后续添加符合规则的文件时,无需修改配置

三、语义化方案:添加 README 文档

如果空目录的用途需要向团队说明(比如 “预留目录”“临时文件目录”),这种方法既能保留目录,又能提供文档价值,一举两得。

核心原理

在空目录中放置README.md文件,Git 会跟踪该文档,从而保留目录结构。同时,README 文件可以详细说明目录的用途、使用规范等,提升项目可维护性。

操作步骤

  1. 创建带说明的 README 文件

    1
    2
    # 写入目录说明(可根据实际需求修改内容)
    echo "This directory is reserved for future cache files. Do not delete it." > path/to/empty_dir/README.md
  2. 提交文档文件

    1
    2
    git add path/to/empty_dir/README.md
    git commit -m "Add README to explain empty directory purpose"

适用场景

  • 开源项目中的预留目录(需向贡献者说明用途)
  • 团队协作中需要明确目录职责的场景
  • 需长期保留且用途固定的空目录

核心优势

  • 语义化强:通过文档直接说明目录用途,减少沟通成本
  • 附加价值:提升项目文档完整性,便于新人快速上手

三种方案对比:怎么选?

方法 核心文件 适用场景 核心优点
.gitkeep占位文件 .gitkeep 通用场景、快速保留目录 操作简单、兼容性强、无副作用
.gitignore反向配置 .gitignore 需忽略未来文件的目录 规则明确、一劳永逸
README文档 README.md 需说明用途的目录 语义化清晰、提供文档价值

注意事项(避坑指南)

  1. 命名规范.gitkeep是行业惯例,不建议自定义名称(如.keep),避免团队认知不一致
  2. 前缀禁忌:除.gitignore外,不要使用.git前缀命名文件(Git 会默认忽略此类文件,导致配置失效)
  3. 目录删除:如果后续需要删除已跟踪的空目录,需先删除目录内的占位文件(.gitkeep/.gitignore/README),再删除目录并提交
  4. 批量处理:使用find命令批量创建.gitkeep时,注意目录权限(避免无写入权限导致命令执行失败)

总结

Git 不直接支持空目录跟踪,但通过 “添加占位文件” 的核心思路,我们可以根据实际需求选择不同方案:

  • 快速解决、通用场景:优先选.gitkeep
  • 需忽略规则、未来加文件:选.gitignore反向配置
  • 需说明用途、提升可维护性:选README文档

MPM(多路处理模块)

常见:

  1. perfork 预处理进程方式
  2. worker 工作者模式
  3. winnt 在windows使用

案例:把apache的最大并发数配置成1000个

首先确认apache的mpm方式

1
cmd>httpd.exe -l #可以看到是什么模式了

这里就看 mpm_xxx.c 这个 xxx 就是那个了

修改httpd.conf文件

搜索 mpm ,找到 Server-pool management(MPM specific)

去掉 # Include conf/extra/httpd-mpm.conf

修改 conf/extra/httpd-mpm.conf 文件

prefork模式就修改这里

1
2
3
4
5
6
7
<IfModule mpm_prefork_module>
StartServers 5 # 预先开启的进程
MinSpareServers 5 # 最小预留5个
MaxSpareServers 10 # 最大留10
MaxClients 150 # 最多并发多少个 *
MaxRequestsPerChild 0 # 最多请求多少次 0不限制
</IfModule>

winnt模式

1
2
3
4
<IfModule mpm_winnt_module>
ThreadsPerChild 150 # 最大并发数 *
MaxRequestsPerChild 0 # 最多处理多少次请求 0不限制
</IfModule>

修改后面有*的那个字段的数值然后重新启动 apache

说明:配置到多大,不一定就可能支撑这么大的并发,考虑到本身 apache 所在的机器硬件性能(如:内存,CPU,硬盘IO)

系统是 linux/unix ,配置 perfork

1
2
3
4
5
6
7
<IfModule mpm_prefork_module>
StartServers 5
MinSpareServers 5
MaxSpareServers 10
MaxClients 150 *#并发量
MaxRequestsPerChild 0
</IfModule>

给大家一个合理的建议配置,对大部份网站,中型网站配置

1
2
3
4
5
6
7
8
<IfModule mpm_prefork_module>
StartServers 5 # 预先启动
MinSpareServers 5
MaxSpareServers 10 # 最大空闲进程
ServerLimit 1500 *# 用于修改apache编程参数
MaxClients 1000 *# 最大并发数
MaxRequestsPerChild 0
</IfModule>

注:apache2.2以后才有的ServerLimit这个参数,其中ServerLimit数值大于MaxClients数值

如果网站的pv值百万

1
2
ServerLimit 2500 *# 用于修改apache编程参数
MaxClients 2000 *# 最大并发数

注:调到这就是极限了,要是网站访问还是大,哪就要增加apache服务器了

0%