Redis分布式锁实战

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;
    #    }
    #}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值