Springboot+redis+Vue实现秒杀的项目实践

目录
  • 1、Redis简介
  • 2、实现代码
  • 3、启动步骤
  • 4、使用ab进行并发测试
  • 5、线程安全
  • 6、总结
  • 7、参考资料

1、Redis简介

Redis是一个开源的key-value存储系统。

Redis的五种基本类型:String(字符串),list(链表),set(集合),zset(有序集合),hash,stream(Redis5.0后的新数据结构)

这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。

Redis的应用场景为配合关系型数据库做高速缓存,降低数据库IO

需要注意的是,Redis是单线程的,如果一次批量处理命令过多,会造成Redis阻塞或网络拥塞(传输数据量大)

2、实现代码

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>org.example</groupId>
    <artifactId>seckill</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>2.2.1.RELEASE</version>
            <scope>test</scope>
        </dependency>

        <!-- redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <!-- spring2.X集成redis所需common-pool2-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.6.0</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.24</version>
        </dependency>

    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <fork>true</fork>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

application.properties.xml

#Redis服务器地址
spring.redis.host=192.168.1.2
#Redis服务器连接端口
spring.redis.port=6379
#Redis数据库索引(默认为0)
spring.redis.database=0
#连接超时时间(毫秒)
spring.redis.timeout=1800000
#连接池最大连接数(使用负值表示没有限制)
spring.redis.lettuce.pool.max-active=20
#最大阻塞等待时间(负数表示没限制)
spring.redis.lettuce.pool.max-wait=-1
#连接池中的最大空闲连接
spring.redis.lettuce.pool.max-idle=5
#连接池中的最小空闲连接
spring.redis.lettuce.pool.min-idle=0
# 关闭超时时间
#pring.redis.lettuce.shutdown-timeout=100
#配置spring启动端口号
server.port=8080

前端界面

seckillpage.html

<!DOCTYPE html>
<html lang="en" xmlns:v-on="http://www.w3.org/1999/xhtml" xmlns:v-bind="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>redis秒杀</title>
</head>
<body>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- 官网提供的 axios 在线地址 -->
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.20.0-0/axios.min.js"></script>
<div id="app">
    <h1>商品1元秒杀</h1>
    <!-- 左箭头 -->
    <input type="button" value="<" v-on:click="prov" v-show="this.index>-1"/>
    <img v-bind:src="imgArr[index]" width="200px" />
    <!-- 右箭头 -->
    <input type="button" value=">" v-on:click="next" v-show="this.index<2"/><br>
    <input type="button" v-on:click="seckill" value="秒 杀"/>
</div>
<script>
    var app = new Vue({
        el: "#app",
        data: {
            productId: "01234",
            imgArr:[
                "/image/phone1.png",
                "/image/phone2.png",
                "/image/phone3.png",
            ],
            index:0
        },
        methods: {
            seckill: function () {
                let param = new URLSearchParams()
                param.append('productId', this.productId)
                param.append('index', this.index)
                axios({
                    method: 'post',
                    url: 'http://192.168.1.6:8080/index/doSeckill',
                    data: param
                }).then(function (response) {

                    if (response.data == true) {
                        alert("秒杀成功");
                    } else {
                        alert("抢光了");
                    }

                },
                    function(error){
                    alert("发生错误");
                });

            },

            prov:function(){
                this.index--;
            },
            next:function(){
                this.index++;
            }
        }
    });

</script>
</body>
</html>

相关配置类

Redis配置类

RedisConfig.java

package com.springboot_redis_seckill.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

/**
 * @author WuL2
 * @create 2021-05-27 14:26
 * @desc
 **/
@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setConnectionFactory(factory);
        template.setKeySerializer(redisSerializer); //key序列化方式
        template.setValueSerializer(jackson2JsonRedisSerializer); //value序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer); //value hashmap序列化
        return template;
    }

    @Bean(name = "cacheManager")
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        //解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 配置序列化(解决乱码的问题),过期时间600秒
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();
        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }
}

配置Vue获取后端接口数据时出现的跨域请求问题。

CorsConfig.java

package com.springboot_redis_seckill.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

/**
 * @author: wu linchun
 * @time: 2021/5/28 22:22
 * @description: 解决跨域问题(接口是http,而axios一般请求的是https。从https到http是跨域,因此要配置跨域请求)
 */

@Configuration
public class CorsConfig {
    private CorsConfiguration buildConfig() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*"); //允许任何域名
        corsConfiguration.addAllowedHeader("*"); //允许任何头
        corsConfiguration.addAllowedMethod("*"); //允许任何方法
        return corsConfiguration;
    }

    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", buildConfig()); //注册
        return new CorsFilter(source);
    }

}

服务层

SecKillService.java

package com.springboot_redis_seckill.service;

public interface SecKillService {
    public boolean doSecKill(String uid,String productId);
}

SecKillServiceImpl.java

package com.springboot_redis_seckill.service.impl;

import com.springboot_redis_seckill.service.SecKillService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

/**
 * @author WuL2
 * @create 2021-05-27 14:53
 * @desc
 **/
@Service
public class SecKillServiceImpl implements SecKillService {
    @Autowired
    @Qualifier("redisTemplate")
    private RedisTemplate redisTemplate;

    @Override
    public synchronized boolean doSecKill(String uid, String productId) {
        //1、uid和productId非空判断
        if (uid == null || productId == null) {
            return false;
        }

        //2、拼接key
        String kcKey = "sk:" + productId + ":qt";   //库存
        String userKey = "sk:" + productId + ":user";  //秒杀成功的用户

        //3、获取库存
        String kc = String.valueOf(redisTemplate.opsForValue().get(kcKey)) ;
        if (kc == null) {
            System.out.println("秒杀还没有开始,请等待");
            return false;
        }

        //4、判断用户是否已经秒杀成功过了
        if (redisTemplate.opsForSet().isMember(userKey, uid)) {
            System.out.println("已秒杀成功,不能重复秒杀");
            return false;
        }
        //5、如果库存数量小于1,秒杀结束
        if (Integer.parseInt(kc) <=0) {
            System.out.println("秒杀结束");
            return false;
        }

        //6、秒杀过程
        redisTemplate.opsForValue().decrement(kcKey);  //库存数量减1
        redisTemplate.opsForSet().add(userKey, uid);
        System.out.println("秒杀成功。。。");
        return true;
    }
}

控制层

package com.springboot_redis_seckill.controller;

import com.alibaba.fastjson.JSONObject;
import com.springboot_redis_seckill.service.SecKillService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Random;

/**
 * @author WuL2
 * @create 2021-05-27 14:28
 * @desc
 **/
@Controller
@RequestMapping("/index")
public class MyController {
    @Autowired
    @Qualifier("secKillServiceImpl")
    private SecKillService secKillService;

    @RequestMapping(value = {"/seckillpage"}, method = RequestMethod.GET)
    public String seckillpage() {
        return "/html/seckillpage.html";
    }

    @RequestMapping(value = {"/doSeckill"}, method = RequestMethod.POST)
    @ResponseBody  //自动返回json格式的数据
    public Object doSeckill(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("doSeckill");
        String productId = request.getParameter("productId");
        String index = request.getParameter("index");
        System.out.println(productId+index);  //拼接成为商品ID
        int id = new Random().nextInt(50000);  //使用随机数生成用户ID
        String uid = String.valueOf(id) + " ";
        boolean flag = secKillService.doSecKill(uid, productId+index);
        System.out.println(flag);
        return flag;
    }
}

启动类

RunApplication.java

package com.springboot_redis_seckill;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author WuL2
 * @create 2021-05-27 14:32
 * @desc
 **/
@SpringBootApplication
public class RunApplication {
    public static void main(String[] args) {
        SpringApplication.run(RunApplication.class, args);
    }
}

3、启动步骤

因为一共有三件商品要秒杀,所以在redis里面设置三个商品的库存数量。这里数量都设置为10。

127.0.0.1:6379> set sk:012340:qt 10
OK
127.0.0.1:6379> set sk:012341:qt 10
OK
127.0.0.1:6379> set sk:012342:qt 10
OK

要确保redis能够被访问,要确保关闭linux的防火墙,以及关闭redis的保护模式。

vim redis.conf   --打开redis配置

service iptables stop   --关闭防火墙

//关闭redis保护模式
redis-cli    --进入redis客户端
config set protected-mode "no"   --配置里面关闭redis保护模式(只是进入redis.conf把protected-mode变为no是不行的,还要加一句config set protected-mode "no"

启动springboot项目

秒杀成功后,该商品在redis中的数量就减1。

当数量减为0时,则提示“抢光了”。

4、使用ab进行并发测试

如果是centOS 6版本的linux都是默认按照了ab工具的。

如果没有安装ab工具,可在linux终端用命令联网下载安装。

yum install httpd-tools

安装完成后,就可以使用ab工具进行并发测试了。

在linux终端输入如下命令:

ab -n 2000  -c 200 -p '/root/Desktop/post.txt' -T 'application/x-www-form-urlencoded' 'http://192.168.1.6:8080/index/doSeckill/'

012341这个商品库存变为0了

5、线程安全

为了防止出现“超买”的现象,需要让操作redis的方法是线程安全的(即在方法上加上一个“悲观锁”synchronized)。

如果不加synchronized就会出现“超买”现象,即redis库存会出现负数

之所以产生这种现象是由于并发导致多个用户同时调用了doSecKill()方法,多个用户同时修改了redis中的sk:012342:qt的值,但暂时都没有提交存入到redis中去。等到后来一起提交,导致了sk:012342:qt的值被修改了多次,因此会出现负数。

因此在doSecKill()方法加上悲观锁,用户调用该方法就对该方法加锁,修改了sk:012342:qt的值后并提交存入redis中之后,才会释放锁。其他用户才能得到锁并操作该方法。

6、总结

redis作为一种Nosql的非关系型数据库,因为其单实例,简单高效的特性通常会被作为其他关系型数据库的高速缓存。尤其是在秒杀这样的高并发操作。先将要秒杀的商品信息从数据库读入到redis中,秒杀的过程实际上是在与redis进行交互。等秒杀完成后再将秒杀的结果存入数据库。可以有效降低降低数据库IO,防止数据库宕机。

7、参考资料

https://www.bilibili.com/video/BV1Rv41177Af?p=27

https://www.cnblogs.com/taiyonghai/p/5810150.html

到此这篇关于Springboot+redis+Vue实现秒杀的项目实践的文章就介绍到这了,更多相关Springboot+redis+Vue 秒杀内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBoot之使用Redis实现分布式锁(秒杀系统)

    一.Redis分布式锁概念篇 建议直接采用Redis的官方推荐的Redisson作为redis的分布式锁 1.1.为什么要使用分布式锁 我们在开发应用的时候,如果需要对某一个共享变量进行多线程同步访问的时候,可以使用我们学到的Java多线程的18般武艺进行处理,并且可以完美的运行,毫无Bug! 注意这是单机应用,也就是所有的请求都会分配到当前服务器的JVM内部,然后映射为操作系统的线程进行处理!而这个共享变量只是在这个JVM内部的一块内存空间! 后来业务发展,需要做集群,一个应用需要部署到几台机

  • springboot +rabbitmq+redis实现秒杀示例

    目录 实现说明 1.工具准备 2.数据表 3.pom 4.代码结构 5.配置config 6.订单业务层 7.redis实现层 8.mq实现层 9.redis模拟初始化库存量 10.controller控制层 11.测试 12.测试结果 实现说明 这里的核心在于如何在大并发的情况下保证数据库能扛得住压力,因为大并发的瓶颈在于数据库.如果用户的请求直接从前端传到数据库,显然,数据库是无法承受几十万上百万甚至上千万的并发量的.因此,我们能做的只能是减少对数据库的访问.例如,前端发出了100万个请求,

  • SpringBoot使用Redisson实现分布式锁(秒杀系统)

    前面讲完了Redis的分布式锁的实现,接下来讲Redisson的分布式锁的实现,一般提及到Redis的分布式锁我们更多的使用的是Redisson的分布式锁,Redis的官方也是建议我们这样去做的.Redisson点我可以直接跳转到Redisson的官方文档. 1.1.引入Maven依赖 <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter&l

  • SpringBoot+RabbitMQ+Redis实现商品秒杀的示例代码

    目录 业务分析 创建表 功能实现 1.用户校验 2.下单 3.减少库存 4.支付 总结 业务分析 一般而言,商品秒杀大概可以拆分成以下几步: 用户校验 校验是否多次抢单,保证每个商品每个用户只能秒杀一次 下单 订单信息进入消息队列,等待消费 减少库存 消费订单消息,减少商品库存,增加订单记录 付款 十五分钟内完成支付,修改支付状态 创建表 goods_info 商品库存表 列 说明 id 主键(uuid) goods_name 商品名称 goods_stock 商品库存 package com.

  • springboot集成redis实现简单秒杀系统

    本文实例为大家分享了springboot集成redis实现简单秒杀系统的具体代码,供大家参考,具体内容如下 项目是有地址的,我会放到文章的最后面 1. 直接service,我们会介绍两种秒杀模式 public interface GoodsService { /** * 通过lua脚本实现的秒杀 * @param skuCode 商品编码 * @param buyNum 购买数量 * @return 购买数量 */ Long flashSellByLuaScript(String skuCode

  • 基于Redis结合SpringBoot的秒杀案例详解

    目录 1.构建SpringBoot项目 2.启动类 3.在Controller层里定义秒杀接口 4.在Service层里通过lua脚本实现秒杀效果 5.配置redis连接参数 6.演示秒杀效果 6.1 准备redis环境 6.2 启动项目 6.3 多线程形式发起秒杀请求 1.构建SpringBoot项目 搭建名为quickbuy的springboot项目,相关的依赖包如下所示: <?xml version="1.0" encoding="UTF-8"?>

  • vue.js+boostrap项目实践(案例详解)

    一.为什么要写这篇文章 最近忙里偷闲学了一下vue.js,同时也复习了一下boostrap,发现这两种东西如果同时运用到一起,可以发挥很强大的作用,boostrap优雅的样式和丰富的组件使得页面开发变得更美观和更容易,同时vue.js又是可以绑定model和view(这个相当于MVC中的,M和V之间的关系),使得对数据变换的操作变得更加的简易,简化了很多的逻辑代码. 二.学习这篇文章需要具备的知识 1.需要有vue.js的知识 2.需要有一定的HTML.CSS.JavaScript的基础知识 3

  • Vue+Vux项目实践完整代码

    提供完整的路由,services````````````` ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

  • Vue.js系列之项目结构说明(2)

    说明: 我们项目现在用的是:vue2.0 + vue-cli + webpack + vue-router2.0 + vue-resource1.0.3 如果大家在实践的过程中与本文所说的内容有较大区别的话看看是不是版本问题. 本文是一系列文章,在我对Vue有了更深刻的理解认识之后会对文章及时进行修改或更正.欢迎大家批评指出错误.以下是已完成的文章列表. 1.Vue.js系列之项目搭建(1) 简单介绍目录结构 build目录是一些webpack的文件,配置参数什么的,一般不用动 config是v

  • 基于SpringBoot与Mybatis实现SpringMVC Web项目

    一.热身 一个现实的场景是:当我们开发一个Web工程时,架构师和开发工程师可能更关心项目技术结构上的设计.而几乎所有结构良好的软件(项目)都使用了分层设计.分层设计是将项目按技术职能分为几个内聚的部分,从而将技术或接口的实现细节隐藏起来. 从另一个角度上来看,结构上的分层往往也能促进了技术人员的分工,可以使开发人员更专注于某一层业务与功能的实现,比如前端工程师只关心页面的展示与交互效果(例如专注于HTML,JS等),而后端工程师只关心数据和业务逻辑的处理(专注于Java,Mysql等).两者之间

  • php+redis实现商城秒杀功能

    好久没来整理文章了,闲了没事写篇文章记录下php+redis实现商城秒杀功能. 1.安装redis,根据自己的php版本安装对应的redis扩展(此步骤简单的描述一下) 1.1.安装php_igbinary.dll,php_redis.dll扩展此处需要注意你的php版本如图: 1.2.php.ini文件新增extension=php_igbinary.dll;extension=php_redis.dll两处扩展 ok此处已经完成第一步redis环境搭建完成看看phpinfo 2.项目中实际使

  • Vue CLI3搭建的项目中路径相关问题的解决

    这是开头 最近在试水 Vue CLI 3,并且尝试配置一个多页面(多应用)项目出来,期间又遇到各种路径问题,于是...于是有了下面的唠叨. 以下都是基于 Vue CLI 3 来举例说明的,使用 2.x 版本的其实也类似 首先,参考 官方文档对静态资源处理的说明,并通过自己的实践,可以总结出以下内容 静态资源可以通过两种方式进行处理: 1.以下情况下,资源不会被 webpack 处理,而是被直接拷贝: 放置在 public 目录下,即使未被使用. 通过绝对路径被引用,即以 / 开头的路径. 2.以

  • Springboot+redis+Interceptor+自定义annotation实现接口自动幂等

    前言: 在实际的开发项目中,一个对外暴露的接口往往会面临很多次请求,我们来解释一下幂等的概念:任意多次执行所产生的影响均与一次执行的影响相同.按照这个含义,最终的含义就是对数据库的影响只能是一次性的,不能重复处理.如何保证其幂等性,通常有以下手段: 1:数据库建立唯一性索引,可以保证最终插入数据库的只有一条数据 2:token机制,每次接口请求前先获取一个token,然后再下次请求的时候在请求的header体中加上这个token,后台进行验证,如果验证通过删除token,下次请求再次判断toke

  • Vue.js 图标选择组件实践详解

    本文介绍了Vue.js 图标选择组件实践详解,分享给大家,具体如下: 背景 最近项目中在做一个自定义菜单需求,其中有一个为菜单设置小图标的功能,就是大家常见的左侧菜单 设置图标不难,方案就是字体图标,可供使用的图标库也有很多,比如阿里巴巴的 Iconfont,以及 Fontaswsome 等,问题在于如何优雅的提供几百个图标供用户选择,而不需要开发去一个一个的写标签,也不需要一个个的去找图标. 字体图标库 Fontawesome 方案 我们使用字体图标的方式,一般是一个 <i class="

  • springboot redis分布式锁代码实例

    这篇文章主要介绍了springboot redis分布式锁代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 随着微服务等分布式架构的快速发展及应用,在很多情况下,我们都会遇到在并发情况下多个线程竞争资源的情况,比如我们耳熟能详的秒杀活动,多平台多用户对同一个资源进行操作等场景等.分布式锁的实现方式有很多种,比如基于数据库.Zookeeper.Redis等,本文我们主要介绍Spring Boot整合Redis实现分布式锁. 工具类如下: i

随机推荐

其他