<commit_before>---
layout: post
title: 高性能秒杀
categories: [project]
description: 
keywords: 
---

* content
{:toc}


## 云端部署

### 数据库部署

备份

```
mysqldump -uroot -p --databases seckill > ~/Downloads/seckill.sql
```

导入

```
mysql -uroot -p < /usr/local/seckill.sql
```

### 打包上传

本地打包

```bash
mvn clean package
```

上传到服务器

```bash
scp imooc-seckill-1.0-SNAPSHOT.jar root@192.168.127.12:/usr/local
```

登录服务器创建目录

```bash
mkdir -p /var/www/seckill
```

移动 jar 包

```bash
cd /var/www/seckill
mv /usr/local/imooc-seckill-1.0-SNAPSHOT.jar seckill.jar
```

授权

```bash
chmod -R 777 *
```

启动程序

```bash
java -jar seckill.jar
```



### 编写 deploy

```bash
cd /var/www/seckill
```

编写外挂配置

```bash
vim application.properties

server.port=8088
```

重新启动程序, 指定外挂配置

```bash
java -jar seckill.jar --spring.config.addition-locaotion=/var/www/seckill/application.properties
```

编写 deploy 脚本

```bash
vim deploy.sh

nohup java -Xms400m -Xmx400m -XX:NewSize=200m -XX:MaxNewSize=200m -jar seckill.jar --spring.config.addition-locaotion=/var/www/seckill/application.properties
```



## jemeter 压测

### 线程组

### http 请求

### 查看结果树

### 聚合报告

![http://www.milky.show/images/project/seckill/seckill_4.png](http://www.milky.show/images/project/seckill/seckill_4.png)

90% Line: 90% 的请求落在这个毫秒内, 反之就是有 10% 的请求大于这个毫秒

95% Line: 95% 的请求落在这个毫秒内

99% Line: 95% 的请求落在这个毫秒内

### 查看服务器状态

使用 top 命令可以查看 cpu 的使用状态

```bash
top -H
```

![http://www.milky.show/images/project/seckill/seckill_5.png](http://www.milky.show/images/project/seckill/seckill_5.png)

**CPU usage: 7.12% user, 6.3% sys:** 分表代表用户态和系统态的 CPU 使用率, 加在一起不超过 100%

**Load Avg: 2.88, 3.10, 3.08:** 1 分钟, 5 分钟, 15 分钟 CPU 的使用负载, 不超过服务器 CPU 个数最好, load 越高说明 CPU 耗的越多

## Tomcat 调优

我们使用 5000 个线程循环 20 次对接口 /item/get?id=8 进行压测, 最终会报错显示 tomcat 拒绝连接, 初步判断是 tomcat 的线程数不够导致的该问题

### 查看线程数

通过使用 `pstree -p pid | wc -l` 查看 java 服务的线程数发现只有 39 个线程(29 个其他线程), 导致客户端请求不能建立新的连接发生报错 mac 使用`ps -M pid | wc -l`

### 查看内嵌 tomcat 配置

spring-boot-autoconfigure 包下的 spring-configuration-metadata.json 文件查看各个节点的配置可以看到内嵌 tomcat 的默认配置

**默认内嵌 tomcat 配置**

server.tomact.accept-count: 等待队列长度, 默认 100

server.tomcat.max-connections: 最大可被连接数, 默认 10000

server.tomcat.max-threads: 最大工作线程数, 默认 200

server.tomcat.min-spare-threads: 最小工作线程数, 默认 10

默认配置下, 连接超过 10000 后出现拒绝连接情况

默认配置下, 触发的请求超过 200+100 后拒绝处理

**理解 maxConnections, maxThreads, acceptCount 关系**

我们可以把tomcat比做一个火锅店，流程是取号、入座、叫服务员，可以做一下三个形象的类比：

1. acceptCount 最大等待数

    可以类比为火锅店的排号处能够容纳排号的最大数量；排号的数量不是无限制的，火锅店的排号到了一定数据量之后，服务往往会说：已经客满。

2. maxConnections 最大连接数

    可以类比为火锅店的大堂的餐桌数量，也就是可以就餐的桌数。如果所有的桌子都已经坐满，则表示餐厅已满，已经达到了服务的数量上线，不能再有顾客进入餐厅了。

3. maxThreads 最大线程数

    可以类比为厨师的个数。每一个厨师，在同一时刻，只能给一张餐桌炒菜，就像极了JVM中的一条线程

**整个就餐的流程, 大致如下**

1. 取号: 如果maxConnections连接数没有满，就不需要取号，因为还有空余的餐桌，直接被大堂服务员领上餐桌，点菜就餐即可。如果 maxConnections 连接数满了，但是取号人数没有达到 acceptCount，则取号成功。如果取号人数已达到acceptCount，则拿号失败，会得到Tomcat的Connection refused connect 的回复信息。
2. 上桌: 如果有餐桌空出来了，表示maxConnections连接数没有满，排队的人，可以进入大堂上桌就餐。
3. 就餐: 就餐需要厨师炒菜。厨师的数量，比顾客的数量，肯定会少一些。一个厨师一定需要给多张餐桌炒菜，如果就餐的人越多，厨师也会忙不过来。这时候就可以增加厨师，一增加到上限maxThreads的值，如果还是不够，只能是拖慢每一张餐桌的上菜速度，这种情况，就是大家常见的“上一道菜吃光了，下一道菜还没有上”尴尬场景。

### 修改配置

```properties
server.port=8088
# 等待队列长度, 默认100
server.tomact.accept-count=1000
# 最大工作线程数, 默认200, 4核8g内存, 线程数经验值800
# 操作系统做线程之间的切换调度是有系统开销的, 所以不是越多越好
server.tomcat.max-threads=800
# 最小工作空闲线程数, 默认10, 适当增大一些, 以便应对突然增长的访问量
server.tomcat.min-spare-threads=100
```

**修改参数后启动程序使用 ps -M pid|wc -l 查看发现线程数增多到 129 个, 再次使用 2000 个线程循环 50 次进行压测, 可以看到线程数增加到 832 个, 虽然最终也会报错, 但是不会像默认配置一样很快就发生报错, 说明提高 tomcat 线程数可以增强系统的访问能力**



### 定制化内嵌 Tomcat 开发

**如果我们 keepalive 一直保持连接, 但是网页一直没有操作, 就会占用线程浪费资源**

keepAliveTimeOut: 多少毫秒后不响应的断开 keepalive

maxKeepAliveRequests: 多少次请求后 keepalive 断开失效

使用 WebServerFactoryCustomizer\<ConfigurableServletWebServerFactory\> 定制化内嵌 tomcat 配置

```java
/**
 * 当 Spring 容器内没有 TomcatEmbeddedServletContainerFactory 这个 bean 时，会吧此 bean 加载进 spring 容器中
 *
 * @author miaoqi
 * @date 2020-01-29
 *
 * @return
 */
@Component
public class WebServerConfiguration implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {

    @Override
    public void customize(ConfigurableWebServerFactory factory) {
        // 使用对应工厂类提供给我们的接口定制化我们的 tomcat connector
        ((TomcatServletWebServerFactory) factory).addConnectorCustomizers(new TomcatConnectorCustomizer() {
            @Override
            public void customize(Connector connector) {
                Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();<commit_msg>update blog<commit_after>---
layout: post
title: 高性能秒杀
categories: [project]
description: 
keywords: 
---

* content
{:toc}


## 云端部署

### 数据库部署

备份

```
mysqldump -uroot -p --databases seckill > ~/Downloads/seckill.sql
```

导入

```
mysql -uroot -p < /usr/local/seckill.sql
```

### 打包上传

本地打包

```bash
mvn clean package
```

上传到服务器

```bash
scp imooc-seckill-1.0-SNAPSHOT.jar root@192.168.127.12:/usr/local
```

登录服务器创建目录

```bash
mkdir -p /var/www/seckill
```

移动 jar 包

```bash
cd /var/www/seckill
mv /usr/local/imooc-seckill-1.0-SNAPSHOT.jar seckill.jar
```

授权

```bash
chmod -R 777 *
```

启动程序

```bash
java -jar seckill.jar
```



### 编写 deploy

```bash
cd /var/www/seckill
```

编写外挂配置

```bash
vim application.properties

server.port=8088
```

重新启动程序, 指定外挂配置

```bash
java -jar seckill.jar --spring.config.addition-locaotion=/var/www/seckill/application.properties
```

编写 deploy 脚本

```bash
vim deploy.sh

nohup java -Xms400m -Xmx400m -XX:NewSize=200m -XX:MaxNewSize=200m -jar seckill.jar --spring.config.addition-locaotion=/var/www/seckill/application.properties
```



## jemeter 压测

### 线程组

### http 请求

### 查看结果树

### 聚合报告

![http://www.milky.show/images/project/seckill/seckill_4.png](http://www.milky.show/images/project/seckill/seckill_4.png)

90% Line: 90% 的请求落在这个毫秒内, 反之就是有 10% 的请求大于这个毫秒

95% Line: 95% 的请求落在这个毫秒内

99% Line: 95% 的请求落在这个毫秒内

### 查看服务器状态

使用 top 命令可以查看 cpu 的使用状态

```bash
top -H
```

![http://www.milky.show/images/project/seckill/seckill_5.png](http://www.milky.show/images/project/seckill/seckill_5.png)

**CPU usage: 7.12% user, 6.3% sys:** 分表代表用户态和系统态的 CPU 使用率, 加在一起不超过 100%

**Load Avg: 2.88, 3.10, 3.08:** 1 分钟, 5 分钟, 15 分钟 CPU 的使用负载, 不超过服务器 CPU 个数最好, load 越高说明 CPU 耗的越多

## Tomcat 调优

我们使用 5000 个线程循环 20 次对接口 /item/get?id=8 进行压测, 最终会报错显示 tomcat 拒绝连接, 初步判断是 tomcat 的线程数不够导致的该问题

### 查看线程数

通过使用 `pstree -p pid | wc -l` 查看 java 服务的线程数发现只有 39 个线程(29 个其他线程), 导致客户端请求不能建立新的连接发生报错 mac 使用`ps -M pid | wc -l`

### 查看内嵌 tomcat 配置

spring-boot-autoconfigure 包下的 spring-configuration-metadata.json 文件查看各个节点的配置可以看到内嵌 tomcat 的默认配置

**默认内嵌 tomcat 配置**

server.tomact.accept-count: 等待队列长度, 默认 100

server.tomcat.max-connections: 最大可被连接数, 默认 10000

server.tomcat.max-threads: 最大工作线程数, 默认 200

server.tomcat.min-spare-threads: 最小工作线程数, 默认 10

默认配置下, 连接超过 10000 后出现拒绝连接情况

默认配置下, 触发的请求超过 200+100 后拒绝处理

**理解 maxConnections, maxThreads, acceptCount 关系**

我们可以把tomcat比做一个火锅店，流程是取号、入座、叫服务员，可以做一下三个形象的类比：

1. acceptCount 最大等待数

    可以类比为火锅店的排号处能够容纳排号的最大数量；排号的数量不是无限制的，火锅店的排号到了一定数据量之后，服务往往会说：已经客满。

2. maxConnections 最大连接数

    可以类比为火锅店的大堂的餐桌数量，也就是可以就餐的桌数。如果所有的桌子都已经坐满，则表示餐厅已满，已经达到了服务的数量上线，不能再有顾客进入餐厅了。

3. maxThreads 最大线程数

    可以类比为厨师的个数。每一个厨师，在同一时刻，只能给一张餐桌炒菜，就像极了JVM中的一条线程

**整个就餐的流程, 大致如下**

1. 取号: 如果maxConnections连接数没有满，就不需要取号，因为还有空余的餐桌，直接被大堂服务员领上餐桌，点菜就餐即可。如果 maxConnections 连接数满了，但是取号人数没有达到 acceptCount，则取号成功。如果取号人数已达到acceptCount，则拿号失败，会得到Tomcat的Connection refused connect 的回复信息。
2. 上桌: 如果有餐桌空出来了，表示maxConnections连接数没有满，排队的人，可以进入大堂上桌就餐。
3. 就餐: 就餐需要厨师炒菜。厨师的数量，比顾客的数量，肯定会少一些。一个厨师一定需要给多张餐桌炒菜，如果就餐的人越多，厨师也会忙不过来。这时候就可以增加厨师，一增加到上限maxThreads的值，如果还是不够，只能是拖慢每一张餐桌的上菜速度，这种情况，就是大家常见的“上一道菜吃光了，下一道菜还没有上”尴尬场景。

### 修改配置

```properties
server.port=8088
# 等待队列长度, 默认100
server.tomact.accept-count=1000
# 最大工作线程数, 默认200, 4核8g内存, 线程数经验值800
# 操作系统做线程之间的切换调度是有系统开销的, 所以不是越多越好
server.tomcat.max-threads=800
# 最小工作空闲线程数, 默认10, 适当增大一些, 以便应对突然增长的访问量
server.tomcat.min-spare-threads=100
```

**修改参数后启动程序使用 ps -M pid|wc -l 查看发现线程数增多到 129 个, 再次使用 2000 个线程循环 50 次进行压测, 可以看到线程数增加到 832 个, 虽然最终也会报错, 但是不会像默认配置一样很快就发生报错, 说明提高 tomcat 线程数可以增强系统的访问能力**

### 定制化内嵌 Tomcat 开发

**如果我们 keepalive 一直保持连接, 但是网页一直没有操作, 就会占用线程浪费资源**

keepAliveTimeOut: 多少毫秒后不响应的断开 keepalive

maxKeepAliveRequests: 多少次请求后 keepalive 断开失效

使用 WebServerFactoryCustomizer\<ConfigurableServletWebServerFactory\> 定制化内嵌 tomcat 配置

```java
/**
 * 当 Spring 容器内没有 TomcatEmbeddedServletContainerFactory 这个 bean 时，会吧此 bean 加载进 spring 容器中
 *
 * @author miaoqi
 * @date 2020-01-29
 *
 * @return
 */
@Component
public class WebServerConfiguration implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {

    @Override
    public void customize(ConfigurableWebServerFactory factory) {
        // 使用对应工厂类提供给我们的接口定制化我们的 tomcat connector
        ((TomcatServletWebServerFactory) factory).addConnectorCustomizers(new TomcatConnectorCustomizer() {
            @Override
            public void customize(Connector connector) {
                Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
{'fraction_non_alphanumeric': 0.12271307452030343, 'fraction_numerical': 0.03681392235609103, 'mean_word_length': 4.851827676240209, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 18, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2998086', 'n_tokens_mistral': 5554, 'n_tokens_neox': 5819, 'n_words': 728}