1.单个JVM下多线程的问题
实例:多线程实现网络购票,用户提交购票信息后,
第一步:网站修改网站车票数据
第二部;显示出票反馈信息给用户
线程类:
public class Site implements Runnable {
//记录剩余票数
private int count = 10;
//记录当前抢到第几张票
private int num = 0;
public void run() {
//循环,当剩余票数为0时,结束
while (true) {
if (count <= 0) {
break;
} else {
// 1、修改数据(剩余票数,抢到第几张票)
count--;
// 2、显示信息,反馈用户抢到第几张票
num++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "抢到第" + num + "张票,剩余" + count + "张票");
}
}
}
}
测试类:
public static void main(String[] args) {
Site site = new Site();
Thread person1 = new Thread(site,"ange");
Thread person2 = new Thread(site,"pika");
Thread person3 = new Thread(site,"rock");
person1.start();
person2.start();
person3.start();
}
运行结果:
ange抢到第3张票,剩余7张票
pika抢到第3张票,剩余7张票
rock抢到第3张票,剩余7张票
rock抢到第6张票,剩余4张票
ange抢到第6张票,剩余4张票
pika抢到第6张票,剩余4张票
pika抢到第9张票,剩余1张票
rock抢到第9张票,剩余1张票
ange抢到第9张票,剩余1张票
pika抢到第10张票,剩余0张票
从运行结果看,简直不能看,输出的数据都是些什么东西啊。存在的问题如下:
1、不是从第一张票开始
2、存在多人抢到一张票的情况
3、有些票号没有被抢到
当多个线程共享同一资源时,一个线程未完成全部操作的时候,其他线程修改数据,造成数据的不安全问题。
原因分析:
第一个线程在休眠的1000ms中,其他的两个线程也完成了抢票动作然后进入休眠。等第一个线程一觉醒来一脸懵逼地发现只剩七张票了。剩下两个线程也是这样。
解决思路:将这两步操作绑定在一起,只有当一个线程全部完成这些操作时,才能允许其他线程进行操作
修改:
1、当用synchronized关键字来修改run方法时:
public synchronized void run() {
//循环,当剩余票数为0时,结束
while (true) {
if (count == 0) {
break;
} else {
// 1、修改数据(剩余票数,抢到第几张票)
count--;
// 2、显示信息,反馈用户抢到第几张票
num++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "抢到第" + num + "张票,剩余" + count + "张票");
}
}
}
输出结果为:
ange抢到第1张票,剩余9张票
ange抢到第2张票,剩余8张票
ange抢到第3张票,剩余7张票
ange抢到第4张票,剩余6张票
ange抢到第5张票,剩余5张票
ange抢到第6张票,剩余4张票
ange抢到第7张票,剩余3张票
ange抢到第8张票,剩余2张票
ange抢到第9张票,剩余1张票
ange抢到第10张票,剩余0张票
此时第一个线程抢到了全部的票。因为第一个线程一直在run方法中的while中循环,直到票被抢完才会终止。此时运行第二和第三个线程的时候没有票可抢。
2、但是将代码修改为以下时:
public void run() {
//循环,当剩余票数为0时,结束
while (true) {
if (count <= 0) {
break;
} else {
this.a();
}
}
}
synchronized public void a() {
// 1、修改数据(剩余票数,抢到第几张票)
count--;
// 2、显示信息,反馈用户抢到第几张票
num++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "抢到第" + num + "张票,剩余" + count + "张票");
}
输出结果为:
ange抢到第1张票,剩余9张票
ange抢到第2张票,剩余8张票
ange抢到第3张票,剩余7张票
ange抢到第4张票,剩余6张票
ange抢到第5张票,剩余5张票
rock抢到第6张票,剩余4张票
rock抢到第7张票,剩余3张票
rock抢到第8张票,剩余2张票
rock抢到第9张票,剩余1张票
rock抢到第10张票,剩余0张票
pika抢到第11张票,剩余-1张票
ange抢到第12张票,剩余-2张票
从结果来看,多抢了两张票。
当把if的控制条件改为以下时:
public void run() {
//循环,当剩余票数为0时,结束
while (true) {
if (count <= 2) {
break;
} else {
this.a();
}
}
}
输出结果为:
ange抢到第1张票,剩余9张票
pika抢到第2张票,剩余8张票
pika抢到第3张票,剩余7张票
pika抢到第4张票,剩余6张票
pika抢到第5张票,剩余5张票
pika抢到第6张票,剩余4张票
pika抢到第7张票,剩余3张票
pika抢到第8张票,剩余2张票
rock抢到第9张票,剩余1张票
ange抢到第10张票,剩余0张票
此时的结果没问题,但是为什么啊?在我看来,控制条件无论如何也不应该是 <= 2。
原因可能如下:三个线程都会运行run方法,在运行过程中都走过了if的判断条件,只是在else中的语句前停下,因为else中的语句设置了锁,在他们等待运行的时间中,票数可能就改变了,变得小于0了,但是他们还是会运行完自己的语句。直到下一次运行进不去else的条件时为止。
同时,如果将条件改为count == 0
的话,结果可能会更糟,因为在他们等待的时间中,票数可能已经变化过去0了,一旦这样的话就会陷入到一个死循环中。
3、同时这样改也没问题:定义了一个flag来控制while循环条件
public class Site implements Runnable {
//记录剩余票数
private int count = 10;
//记录当前抢到第几张票
private int num = 0;
boolean flag = false;
public void run() {
//循环,当剩余票数为0时,结束
while (!false) {
this.a();
}
}
synchronized public void a() {
if (count <= 0) {
flag = true;
return;
} else {
}
// 1、修改数据(剩余票数,抢到第几张票)
count--;
// 2、显示信息,反馈用户抢到第几张票
num++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "抢到第" + num + "张票,剩余" + count + "张票");
}
}
此时结果为:
ange抢到第1张票,剩余9张票
rock抢到第2张票,剩余8张票
rock抢到第3张票,剩余7张票
rock抢到第4张票,剩余6张票
rock抢到第5张票,剩余5张票
rock抢到第6张票,剩余4张票
pika抢到第7张票,剩余3张票
pika抢到第8张票,剩余2张票
pika抢到第9张票,剩余1张票
pika抢到第10张票,剩余0张票
4、使用修改代码块的方式来修改如下:
public class Site implements Runnable {
//记录剩余票数
private int count = 10;
//记录当前抢到第几张票
private int num = 0;
boolean flag = false;
public void run() {
//循环,当剩余票数为0时,结束
while (!flag) {
synchronized (this) {
this.a();
}
}
}
synchronized public void a() {
if (count <= 0) {
flag = true;
return;
} else {
// 1、修改数据(剩余票数,抢到第几张票)
count--;
// 2、显示信息,反馈用户抢到第几张票
num++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "抢到第" + num + "张票,剩余" + count + "张票");
}
}
}
运行结果如下:
ange抢到第1张票,剩余9张票
ange抢到第2张票,剩余8张票
rock抢到第3张票,剩余7张票
rock抢到第4张票,剩余6张票
pika抢到第5张票,剩余5张票
pika抢到第6张票,剩余4张票
pika抢到第7张票,剩余3张票
rock抢到第8张票,剩余2张票
rock抢到第9张票,剩余1张票
rock抢到第10张票,剩余0张票
总结:
多个并发线程访问同一资源的同步代码块时
1、同一时刻只能有一个线程进入synchronized同步代码块。
2、当一个线程访问一个synchronized(this),其他synchronized(this)同步代码块同样被锁定
3、当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码。
什么锁,其实就是个厕所,你不出来我就进不去
2.构建SpringBoot的redis-lock项目
-
pom.xml
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies>
-
application.yml
spring: redis: host: 192.168.126.134 database: 0 port: 6379 server: port: 8090
2.思路与场景
我们在Zookeeper中提到过分布式锁,这里我们先用redis实现一个简单的分布式锁,这里是我们一个简单的售卖减库存的小实例,剩余库存假设存在数据库内。
RedisController.java
@Autowired
private StringRedisTemplate stringRedisTemplate;
//我们在Zookeeper中提到过分布式锁,
// 这里我们先用redis实现一个简单的分布式锁,
// 这里是我们一个简单的售卖减库存的小实例,
// 剩余库存假设存在数据库内
@RequestMapping("/getLock")
public String getLock(){
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
if (stock > 0) {
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + "");
System.out.println("售卖成功,剩余" + realStock + "");
return "success";
}else{
System.out.println("剩余库存不足");
return "fail";
}
}
这样简单的实现了一个售卖的过程,现在看来确实没什么问题的,但是如果是一个并发下的场景就可能会出现超卖的情况了,我们来改造一下代码。
@GetMapping(value = "/getLock")
public String getLock() {
synchronized (this) {
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
if (stock > 0) {
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + "");
System.out.println("售卖成功,剩余" + realStock + "");
return "success";
} else {
System.out.println("剩余库存不足");
return "fail";
}
}
}
貌似这回就可以了,可以抗住高并发了,但是新的问题又来了,我们如果是分布式的场景下,synchronized关键字是不起作用的啊。也就是说还是会出现超卖的情况的啊,我们再来改造一下
@GetMapping(value = "/getLock")
public String getLock() {
String lockKey = "product_001";
Boolean bool = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "Ange");//相当于我们的setnx命令 setnotexists 如果有相同的键,则该代码不会再次执行 不存在则返回true
if(!bool){
return "error";
}
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
if (stock > 0) {
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + "");
System.out.println("售卖成功,剩余" + realStock + "");
stringRedisTemplate.delete(lockKey);
return "success";
} else {
System.out.println("剩余库存不足");
stringRedisTemplate.delete(lockKey);
return "fail";
}
}
这次我们看来基本可以了,使用我们的setnx命令来做一次唯一的限制,万一报错了呢?解锁怎么办?再来改造一下。
@GetMapping(value = "/getLock")
public String getLock() {
String lockKey = "lock";
Boolean bool = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "Ange", 10, TimeUnit.SECONDS);//相当于我们的setnx命令
try {
if (!bool) {
return "error";
}
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
if (stock > 0) {
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + "");
System.out.println("售卖成功,剩余" + realStock + "");
return "success";
} else {
System.out.println("剩余库存不足");
return "fail";
}
} finally {
if (bool) {
stringRedisTemplate.delete(lockKey);
}
}
}
这次貌似真的可以了,可以加锁,最后在finally解锁,如果解锁还是不成功,我们还设置了我们的超时时间,貌似完美了,我们再来提出一个场景。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w6wjnlr7-1575470824568)(D:\TinkingCat\Redis\assets\1506597-20191026222151964-1074019708.png)]
就是什么意思呢?我们的线程来争抢锁,拿到锁的线程开始执行,但是我们并不知道何时执行完成,我们只是设定了10秒自动释放掉锁,如果说我们的线程10秒还没有结束,其它线程会拿到锁资源,开始执行代码,但是过了一段时间(蓝色线程还未执行完成),这时我们的绿色线程执行完毕了,开始释放锁资源,他释放的其实已经不是他自己的锁了,他自己的锁超时了,自动释放了,实则绿色线程释放的蓝色的资源,这也就造成了释放其它的锁,其它的线程又会重复的拿到锁,重复执行该操作。明显有点乱了,这不合理,我们来改善一下。
@GetMapping(value = "/getLock")
public String getLock() {
String lockKey = "lock";
String lockValue = UUID.randomUUID().toString();
Boolean bool = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 10, TimeUnit.SECONDS);//相当于我们的setnx命令
try {
if (!bool) {
return "error";
}
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
if (stock > 0) {
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + "");
System.out.println("售卖成功,剩余" + realStock + "");
return "success";
} else {
System.out.println("剩余库存不足");
return "fail";
}
} finally {
if (lockValue.equals(stringRedisTemplate.opsForValue().get(lockKey))) {
stringRedisTemplate.delete(lockKey);
}
}
}
这次再来看一下流程,我们设置一个UUID,设置为锁的值,也就是说,每次上锁的UUID都是不一致的,我们的线程A的锁这次只能由我们的线程A来释放掉,不会造成释放其它锁的问题了,还是上次的图,我们回过头来看一下,10秒?真的合理吗?万一10秒还没有执行完成呢?有的人还会问,那设置100秒?万一执行到delete操作的时候,服务宕机了呢?是不是还要等待100秒才可以释放锁。别说那只是万一,我们的代码希望达到我们能力范围之内的最严谨。这次来说一下我们本节的其中一个重点,Lua脚本,后面会去说,我们来先用我们这次的Redisson吧
Redisson
刚才我们提到了我们锁的时间设置,多长才是合理的,100秒?可能宕机,造成等待100秒自动释放,1秒?线程可能执行不完,我们可不可以这样来做呢?我们设置一个30秒,或者说设置10秒,然后我们给予一个固定时间来检查我们的主线程是否执行完成,执行完成再释放我们的锁,思路有了,但是代码实现起来并不简单,别着急,我们已经有了现成的包供我们使用的,就是我们的Redisson,首先我们来引入我们的依赖,修改一下pom文件。
<!-- https://mvnrepository.com/artifact/org.redisson/redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.6.5</version>
</dependency>
然后通过@Bean的方式注入容器,三种方式我都写在上面了。
@Bean
public Redisson redisson(){
Config config = new Config();
//主从(单机)
config.useSingleServer().setAddress("redis://127.0.0.1:6379").setDatabase(0);
//哨兵
// config.useSentinelServers().setMasterName("mymaster");
// config.useSentinelServers().addSentinelAddress("redis://192.168.1.1:26379");
// config.useSentinelServers().addSentinelAddress("redis://192.168.1.2:26379");
// config.useSentinelServers().addSentinelAddress("redis://192.168.1..3:26379");
// config.useSentinelServers().setDatabase(0);
// //集群
// config.useClusterServers()
// .addNodeAddress("redis://192.168.0.1:8001")
// .addNodeAddress("redis://192.168.0.2:8002")
// .addNodeAddress("redis://192.168.0.3:8003")
// .addNodeAddress("redis://192.168.0.4:8004")
// .addNodeAddress("redis://192.168.0.5:8005")
// .addNodeAddress("redis://192.168.0.6:8006");
// config.useSentinelServers().setPassword("xiaocai");//密码设置
return (Redisson) Redisson.create(config);
}
如果我们的是springboot也可以通过配置来实现的。
application.properties
## 因为springboot-data-redis 是用到了jedis,所已这里得配置
spring.redis.database=10
spring.redis.pool.max-idle=8
spring.redis.pool.min-idle=0
spring.redis.pool.max-active=8
spring.redis.pool.max-wait=-1
## jedis 哨兵配置
spring.redis.sentinel.master=mymaster
spring.redis.sentinel.nodes=192.168.1.241:26379,192.168.1.241:36379,192.168.1.241:46379
spring.redis.password=admin
## 关键地方 redisson
spring.redis.redisson.config=classpath:redisson.json
redisson.json
## redisson.json 文件
{
"sentinelServersConfig":{
"sentinelAddresses": ["redis://192.168.1.241:26379","redis://192.168.1.241:36379","redis://192.168.1.241:46379"],
"masterName": "mymaster",
"database": 0,
"password":"admin"
}
}
这样我们就建立了我们的Redisson的连接了,我们来看一下如何使用吧。
package com.redisclient.cluster;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class RedisCluster {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private Redisson redisson;
@GetMapping(value = "/getLock")
public String getLock() {
String lockKey = "lock";
RLock redissonLock = redisson.getLock(lockKey);
try {
redissonLock.lock();
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
if (stock > 0) {
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + "");
System.out.println("售卖成功,剩余" + realStock + "");
return "success";
} else {
System.out.println("剩余库存不足");
return "fail";
}
} finally {
redissonLock.unlock();
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tfjCn6Gw-1575470824570)(D:\TinkingCat\Redis\assets\1506597-20191026233134462-247555401.png)]
使用也是超级简单的,Redisson还有重入锁功能等等,有兴趣的可以去Redisson查看,地址:https://redisson.org/ 国外的地址打开可能会慢一些。
虚拟机中构建Docker
1.更新yum
yum update
2.安装docker
yum install docker
3.查看docker版本
docker -v
4.配置国内仓库地址
vi /etc/docker/daemon.json
将以下配置写入到文件中,保存并退出(不会操作的百度下vi命令吧):
{
"registry-mirrors": ["http://f1361db2.m.daocloud.io"]
}
重启docker:systemctl restart docker.service
5.启动和开机启动docker
systemctl start docker
systemctl enable docker
6.查询镜像
docker search 镜像名称:tag(例如:docker search mysql:tag)
tag代表版本号,可以省略,默认是最新的latest
7.拉取镜像
docker pull 镜像名称:tag(例如:docker pull mysql)
8.查看镜像
docker images
9.删除镜像
docker rmi 镜像名称:tag
10.制作容器
docker run --name redis -p 6379:6379 -d redis
安装Nginx所需插件
1、安装gcc
gcc是linux下的编译器在此不多做解释,感兴趣的小伙伴可以去查一下相关资料,它可以编译 C,C++,Ada,Object C和Java等语言
命令:查看gcc版本
gcc -v
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2n4prF5E-1575470824571)(D:\TinkingCat\nginx\assets\20190509145141495.png)]
一般阿里云的centOS7里面是都有的,没有安装的话会提示命令找不到,
安装命令:
yum -y install gcc
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8zZpwvmg-1575470824572)(D:\TinkingCat\nginx\assets\20190509145218698.png)]
2、pcre、pcre-devel安装
pcre是一个perl库,包括perl兼容的正则表达式库,nginx的http模块使用pcre来解析正则表达式,所以需要安装pcre库。
安装命令:
yum install -y pcre pcre-devel
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BMCTAqC2-1575470824572)(D:\TinkingCat\nginx\assets\2019050914525680.png)]
3、zlib安装
zlib库提供了很多种压缩和解压缩方式nginx使用zlib对http包的内容进行gzip,所以需要安装
安装命令:
yum install -y zlib zlib-devel
4、安装openssl
openssl是web安全通信的基石,没有openssl,可以说我们的信息都是在裸奔。。。。。。
安装命令:
yum install -y openssl openssl-devel
安装nginx
1、下载nginx安装包
wget http://nginx.org/download/nginx-1.9.9.tar.gz
如果命令无法进行
yum -y install wget
这里需要到/usr/local/ 新建java的文件夹
2、把压缩包解压到usr/local/java
tar -zxvf nginx-1.9.9.tar.gz
3、切换到cd /usr/local/java/nginx-1.9.9/下面
执行三个命令:
./configure
make
make install
4、切换到/usr/local/nginx安装目录
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NHF2OJc6-1575470824573)(D:\TinkingCat\nginx\assets\20190509145506224.png)]
5、配置nginx的配置文件nginx.conf文件,主要也就是端口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wyuScR4p-1575470824574)(D:\TinkingCat\nginx\assets\20190509145516758.png)]
可以按照自己服务器的端口使用情况来进行配置
ESC键,wq!强制保存并退出
6、启动nginx服务
切换目录到/usr/local/nginx/sbin下面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DeoRy1ON-1575470824574)(D:\TinkingCat\nginx\assets\20190509145534295.png)]
启动nginx命令:
./nginx
./nginx -s reload
7、查看nginx服务是否启动成功
ps -ef | grep nginx
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bNj4lY5M-1575470824575)(D:\TinkingCat\nginx\assets\20190509145628305.png)]
8、访问你的服务器IP
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nMn0eA4N-1575470824576)(D:\TinkingCat\nginx\assets\1573111082676.png)]
如果访问不了,关闭防火墙
查看防火墙状态:systemctl status firewalld
关闭防火墙:systemctl stop firewalld
开启防火墙:systemctl start firewalld
nginx.conf说明
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream redislock{
server 192.168.1.62:8080 weight=1;
server 192.168.1.62:8090 weight=1;
}
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
proxy_pass http://redislock;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
#user nobody;
worker_processes 1; #工作进程:数目。根据硬件调整,通常等于cpu数量或者2倍cpu数量。
#错误日志存放路径
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid; # nginx进程pid存放路径
events {
worker_connections 1024; # 工作进程的最大连接数量
}
http {
include mime.types; #指定mime类型,由mime.type来定义
default_type application/octet-stream;
# 日志格式设置
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main; #用log_format指令设置日志格式后,需要用access_log来指定日志文件存放路径
sendfile on; #指定nginx是否调用sendfile函数来输出文件,对于普通应用,必须设置on。
如果用来进行下载等应用磁盘io重负载应用,可设着off,以平衡磁盘与网络io处理速度,降低系统uptime。
#tcp_nopush on; #此选项允许或禁止使用socket的TCP_CORK的选项,此选项仅在sendfile的时候使用
#keepalive_timeout 0; #keepalive超时时间
keepalive_timeout 65;
#gzip on; #开启gzip压缩服务
#虚拟主机
server {
listen 80; #配置监听端口号
server_name localhost; #配置访问域名,域名可以有多个,用空格隔开
#charset koi8-r; #字符集设置
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
}
#错误跳转页
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ { #请求的url过滤,正则匹配,~为区分大小写,~*为不区分大小写。
# root html; #根目录
# fastcgi_pass 127.0.0.1:9000; #请求转向定义的服务器列表
# fastcgi_index index.php; # 如果请求的Fastcgi_index URI是以 / 结束的, 该指令设置的文件会被附加到URI的后面并保存在变量$fastcig_script_name中
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl; #监听端口
# server_name localhost; #域名
# ssl_certificate cert.pem; #证书位置
# ssl_certificate_key cert.key; #私钥位置
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5; #密码加密方式
# ssl_prefer_server_ciphers on; # ssl_prefer_server_ciphers on; #
# location / {
# root html;
# index index.html index.htm;
# }
#}
}