搜索词>>spring boot 2.0 redis 耗时0.0160
  • spring boot 2.0 Redis整合_spring boot 2.0 集成Redis实现缓存框架(一)

    Spring Boot 2.0 Redis整合,通过spring boot 2.0整合Redis作为spring缓存框架的实现。spring boot 2.0 Redis整合_spring boot 2.0 集成Redis实现缓存框架(一)
  • spring boot 整合redis实现spring的缓存框架

    spring boot 1.5整合redis实现spring的缓存框架,spring boot,redisspring boot 整合redis实现spring的缓存框架<br /> <br /> 1.创建一个spring boot项目<br /> <img alt="项目" class="img-thumbnail" src="/resources/assist/images/blog/87917c2a16564cc08bf8e7670ec943ea.jpg" /><br /> 2.配置pom.xml文件 <pre> <code class="language-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> <groupId>com.leftso</groupId> <artifactId>demo-springboot-redis</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>demo-springboot-redis</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <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> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> </code></pre> <br /> 3.在spring boot的application配置文件中配置redis的相关信息 <pre> <code>####################Redis 配置信息 ########################## # Redis数据库分片索引(默认为0) spring.redis.database=0 # Redis服务器地址 spring.redis.host=10.1.1.134 # Redis服务器连接端口 spring.redis.port=6379 # Redis服务器连接密码(默认为空) spring.redis.password= # 连接池最大连接数(使用负值表示没有限制) spring.redis.pool.max-active=8 # 连接池最大阻塞等待时间(使用负值表示没有限制) spring.redis.pool.max-wait=-1 # 连接池中的最大空闲连接 spring.redis.pool.max-idle=8 # 连接池中的最小空闲连接 spring.redis.pool.min-idle=0 # 连接超时时间(毫秒) spring.redis.timeout=0</code></pre> <br /> 4.配置redis整合入spring的缓存框架 <pre> <code class="language-java">package com.leftso.config; import java.lang.reflect.Method; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; @Configuration @EnableCaching // 继承CachingConfigurerSupport并重写方法,配合该注解实现spring缓存框架的启用 public class RedisConfig extends CachingConfigurerSupport { /** 载入通过配置文件配置的连接工场 **/ @Autowired RedisConnectionFactory redisConnectionFactory; @SuppressWarnings("rawtypes") @Autowired RedisTemplate redisTemplate; @Bean RedisTemplate<String, Object> objRedisTemplate() { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); return redisTemplate; } /* * (non-Javadoc) * * @see org.springframework.cache.annotation.CachingConfigurerSupport# * cacheManager() */ @Bean // 必须添加此注解 @Override public CacheManager cacheManager() { RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate); // 设置缓存过期时间 // redisCacheManager.setDefaultExpiration(60);//秒 return redisCacheManager; } /** * 重写缓存的key生成策略,可根据自身业务需要进行自己的配置生成条件 * * @see org.springframework.cache.annotation.CachingConfigurerSupport# * keyGenerator() */ @Bean // 必须项 @Override public KeyGenerator keyGenerator() { return new KeyGenerator() { @Override public Object generate(Object target, Method method, Object... params) { StringBuilder sb = new StringBuilder(); sb.append(target.getClass().getName()); sb.append(method.getName()); for (Object obj : params) { sb.append(obj.toString()); } return sb.toString(); } }; } } </code></pre> <br /> 5.创建一个测试的pojo和一个测试的controller <pre> <code class="language-java">package com.leftso.pojo; import java.io.Serializable; /** * * <pre> * [Summary] * 测试用简单类型 * [Detail] * TODO * [Author] * XQLEE * [Version] * v1.0 * 2017年5月8日上午9:58:42 * </pre> */ public class Address implements Serializable{ /** * */ private static final long serialVersionUID = 1L; private String id; private String city; private String detail; public Address() { } public Address(String id, String city, String detail) { super(); this.id = id; this.city = city; this.detail = detail; } /** * @return the id */ public String getId() { return id; } /** * @param id * the id to set */ public void setId(String id) { this.id = id; } /** * @return the city */ public String getCity() { return city; } /** * @param city * the city to set */ public void setCity(String city) { this.city = city; } /** * @return the detail */ public String getDetail() { return detail; } /** * @param detail * the detail to set */ public void setDetail(String detail) { this.detail = detail; } } </code></pre>   <pre> <code class="language-java">package com.leftso.controller; import org.springframework.cache.annotation.Cacheable; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import com.leftso.pojo.Address; @RestController public class CacheTestCtrl { @GetMapping("/getAddr") @Cacheable(value = "address-cache") // 设置缓存的名称,也可以通过key属性指定缓存的key,keyGenerator指定key生成策略器(keyGenerator一般推荐在重写CachingConfigurerSupport类里面的方法适合全局指定) public Address getAddress() { Address addr = new Address("001", "重庆市", "渝北区"); System.out.println("==========你看见这句话表示没有缓存时候打印出来的========"); return addr; } } </code></pre> <br /> 6.启动spring boot项目,访问地址localhost:8080/getAddr<br /> 第一次访问将会在控制台中看到以下输出:<br /> <img alt="1" class="img-thumbnail" src="/resources/assist/images/blog/8273e3645def48cfb209a482857ff0ab.jpg" /><br /> <br /> 后面多次刷新都不会进入到该方法中去读取信息,而是通过缓存直接读取了缓存信息。<br /> <br /> <br /> 至此spring boot整合redis 实现基本的缓存已经完成。
  • spring boot 2.0 security 5.0 整合

    spring boot 2.0 security 5.0 整合,实现自定义表单登录。spring boot 2.0框架使用。spring boot 2.0 security 5.0 整合入门,实现自定义表单登录。 <h2>1.spring boot 2.0 security 5.0 整合需要引入的maven配置</h2> <pre> <code class="language-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> <groupId>com.example</groupId> <artifactId>demo-security</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>demo-security</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</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> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> </code></pre> <h2>2.spring boot 2.0 security 5.0 整合核心配置文件</h2> <pre> <code class="language-java">package com.example.demosecurity; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfiguration extends WebSecurityConfigurerAdapter { /** * 配置忽略安全管理的路径,一般为资源文件例如css,js,IMG等 * * @param web * @throws Exception */ @Override public void configure(WebSecurity web) throws Exception { web.ignoring() .antMatchers("/webjars/**", "/resources/**"); } @Override protected void configure(HttpSecurity http) throws Exception { // super.configure(http); //注意!注意!注意!这个必须注释或者删除掉否则以下配置回受到默认您spring security规则影响 http .authorizeRequests() .antMatchers("/account/**").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/account/login.html")//自定义登录页面的地址 .loginProcessingUrl("/account/login")//自定义登录表单提交地址(默认:/login) .passwordParameter("pwd")//自定义登录用密码的表单名称(默认password) .usernameParameter("username")//自定义登录用户名的表单名称(默认username) .defaultSuccessUrl("/admin")//自定义登录成功后跳转的页面 .failureForwardUrl("/account/login.html?error")//自定义登录失败跳转的页面 .and() .logout() .invalidateHttpSession(true)//登出时候清除sessionion .clearAuthentication(true)//登出时候清除认证信息 .logoutUrl("/account/logout")//登出表单的地址 .logoutSuccessUrl("/account/login.html")//登出成功后跳转页面 .and() // .csrf().disable()//配置是否启用csrf,默认启用 .cors().disable().headers().frameOptions().sameOrigin();//解决iframe无法访问 } } </code></pre> <h2>3.spring boot 2.0 security 5.0 整合配置csrf安全登录</h2> 如果在上面配置中没有禁用csrf则在登录或者登出的表单中都必须添加以下隐藏字段: <pre> <code class="language-html"><input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" /></code></pre> <br />  
  • Spring Boot 2.0 Websocket Angular整合

    Spring Boot 2.0 Websocket Angular整合Spring Boot 2.0 Websocket Angular整合
  • Redis 秒杀整合Spring Boot 2.x实现例子

    前言继续上一篇Spring Boot Redis 秒杀实现 的一个修改版本,主要实现用ab工具进行网页正式访问的一个版本,其主要目的还是介绍Redis实现秒杀活动的一种方式前言继续上一篇Spring Boot Redis 秒杀实现 的一个修改版本,主要实现用ab工具进行网页正式访问的一个版本,其主要目的还是介绍Redis实现秒杀活动的一种方式。Redis 秒杀活动项目结构图​与上一篇文章中的区别在于多了一个GoodService服务。该服务主要提供秒杀业务。Redis秒杀活动业务层$title(GoodsService.java) package com.leftso.demo.demoredisseckill.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.Transaction; import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; /** * 商品服务 */ @Service public class GoodsService { @Autowired JedisPool jedisPool; public static String productKey="GOODS_001";//某产品的ID int goodsStock=10;//某产品用于秒杀的库存 String successKey="Success_User_List";//成功秒杀用户的集合 /** * 初始化一些默认数据(正常情况这些数据来源于数据库) */ @PostConstruct public void init(){ Jedis jedis=jedisPool.getResource(); jedis.set(productKey,String.valueOf(goodsStock));//设置产品默认库存数量 while (jedis.lpop(successKey)!=null){ }//清空秒杀成功人用户列表 //end new Thread(()->{ long size=jedis.llen(successKey); while (true){ if (size==goodsStock){ break; }else{ size=jedis.llen(successKey); } } List<String> successUsers=new ArrayList<>(); String user=jedis.lpop(successKey); while (user!=null){ successUsers.add(user); user=jedis.lpop(successKey); } System.out.println("活动结束>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>活动结束"); System.out.println("抢购名单:"+successUsers); //可以在名单拿到后生成订单等其他业务操作。 String num=jedis.get(productKey); System.out.println("剩余库存:"+num); }).start(); } /** * 获取库存 * @param productKey * @return */ public int getStock(String productKey){ try (Jedis jedis=jedisPool.getResource();){ String val=jedis.get(productKey); if (val!=null){ return Integer.valueOf(val); } return -1; } } /** * 秒杀商品 * * @param productKey * @return */ public boolean seckill(String productKey, String userName) { if (getStock(productKey)<=0){ System.out.println("商品已抢购完毕>>>>>>>>>>>>>>>>>>>>>>>>>>欢迎下次再来。"); return false; } try(Jedis jedis = jedisPool.getResource();) { jedis.watch(productKey); String val = jedis.get(productKey); int valInt = Integer.valueOf(val); if (valInt >= 1) { Transaction tx = jedis.multi(); tx.incrBy(productKey, -1);//原子操作 List<Object> list = tx.exec(); if (list == null || list.isEmpty()) { //System.out.println("用户:" + userName + " 抢购失败。"); this.seckill(productKey, userName);//再抢 } else { System.out.println("用户:" + userName + " 抢购成功!!!"); // jedis.setnx(productKey,) jedis.rpush(successKey, userName);//成功用户添加入队列 //处理成功后的业务逻辑 return true; } } else { System.out.println("商品已抢购完毕>>>>>>>>>>>>>>>>>>>>>>>>>>欢迎下次再来。"); return false; } return false; } catch (Exception e) { e.printStackTrace(); return false; } } } 主要模拟环境与之前的文章一样。作为秒杀模拟场景。其他相关文件清单:pom.xml依赖$title(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.1.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.leftso.demo</groupId> <artifactId>demo-redis-seckill</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo-redis-seckill</name> <description>Redis 实现产品秒杀</description> <properties> <java.version>1.8</java.version> </properties> <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> <!-- https://mvnrepository.com/artifact/redis.clients/jedis --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.0.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> redis配置$title(JedisConfig.java) package com.leftso.demo.demoredisseckill; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisCluster; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; import java.util.HashSet; import java.util.Set; @Configuration public class JedisConfig { /*** * 单机连接池 * @return */ @Bean public JedisPool jedisPool() { JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); // 设置配置 jedisPoolConfig.setMaxTotal(2048); jedisPoolConfig.setMaxIdle(400); jedisPoolConfig.setMaxWaitMillis(100); jedisPoolConfig.setTestOnBorrow(false);//jedis 第一次启动时,会报错 jedisPoolConfig.setTestOnReturn(true); JedisPool pool = new JedisPool(jedisPoolConfig, "127.0.0.1", 6379); return pool; } /*** * redis集群用(这里暂时没用) * @return */ // @Bean public JedisCluster jedisCluster() { //创建jedisCluster对象,有一个参数 nodes是Set类型,Set包含若干个HostAndPort对象 Set<HostAndPort> nodes = new HashSet<>(); nodes.add(new HostAndPort("127.0.0.1", 7001)); nodes.add(new HostAndPort("127.0.0.1", 7002)); nodes.add(new HostAndPort("127.0.0.1", 7003)); nodes.add(new HostAndPort("127.0.0.1", 7004)); nodes.add(new HostAndPort("127.0.0.1", 7005)); nodes.add(new HostAndPort("127.0.0.1", 7006)); JedisCluster jedisCluster = new JedisCluster(nodes); //使用jedisCluster操作redis // jedisCluster.set("test", "my forst jedis"); // String str = jedisCluster.get("test"); // System.out.println(str); // //关闭连接池 // jedisCluster.close(); //注意由于集群式单例,不要再其他地方关闭连接池!!!!由系统关闭时统一关闭。 return jedisCluster; } } web 接口$title(SecKillController.java) package com.leftso.demo.demoredisseckill.controller; import com.leftso.demo.demoredisseckill.service.GoodsService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.UUID; /** * 秒杀接口 */ @RestController public class SecKillController { @Autowired GoodsService goodsService; @GetMapping("/seckill") public Object seckill() { //创建随机用户名 String userName="用户+"+ UUID.randomUUID().toString().replace("-","").toUpperCase(); boolean suc=goodsService.seckill(GoodsService.productKey,userName); return suc?"Succes":"Fail"; } } Redis 秒杀活动测试 这里的测试主要使用ab工具进行测试。首先启动秒杀应用(注意启动一次只能测一次。再次测试请重启服务,例子简单就这么处理的。哈哈)ab测试命令:ab -c 1000 -n 3000 http://localhost:8080/seckill 执行结果:​通过测试的访问来看,错误请求有2994.好吧我目前也不知道啥情况。反正后台的输出是正常的。来看看后台的日志吧 2019-07-10 11:49:10.592 INFO 2656 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 9 ms 用户:用户+28C21F33CBD3452EB5C67B14A3939356 抢购成功!!! 用户:用户+126334102697433C8BB5B5D6E4061E38 抢购成功!!! 用户:用户+9ED7D0B472E9490E9C780B61B881E41B 抢购成功!!! 用户:用户+5AD862F2A389473AB2B4FB5EDC187617 抢购成功!!! 用户:用户+A264C1B9CB69498D8402B2E6C0B47589 抢购成功!!! 用户:用户+FC3D45232C73446E8032FDC36B0B8430 抢购成功!!! 用户:用户+D0F11B01EAB24CB385C23580CFC4E671 抢购成功!!! 用户:用户+73FD6DAED8564FEF8315CC4D6181ED77 抢购成功!!! 用户:用户+6967C042ADCA4FC9B42B70A51EE10752 抢购成功!!! 用户:用户+BBB5FF4878844B79BD11DC8C15EA9500 抢购成功!!! 商品已抢购完毕>>>>>>>>>>>>>>>>>>>>>>>>>>欢迎下次再来。 商品已抢购完毕>>>>>>>>>>>>>>>>>>>>>>>>>>欢迎下次再来。 商品已抢购完毕>>>>>>>>>>>>>>>>>>>>>>>>>>欢迎下次再来。 商品已抢购完毕>>>>>>>>>>>>>>>>>>>>>>>>>>欢迎下次再来。 商品已抢购完毕>>>>>>>>>>>>>>>>>>>>>>>>>>欢迎下次再来。 活动结束>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>活动结束 抢购名单:[用户+28C21F33CBD3452EB5C67B14A3939356, 用户+126334102697433C8BB5B5D6E4061E38, 用户+9ED7D0B472E9490E9C780B61B881E41B, 用户+5AD862F2A389473AB2B4FB5EDC187617, 用户+A264C1B9CB69498D8402B2E6C0B47589, 用户+FC3D45232C73446E8032FDC36B0B8430, 用户+D0F11B01EAB24CB385C23580CFC4E671, 用户+73FD6DAED8564FEF8315CC4D6181ED77, 用户+6967C042ADCA4FC9B42B70A51EE10752, 用户+BBB5FF4878844B79BD11DC8C15EA9500] 剩余库存:0 商品已抢购完毕>>>>>>>>>>>>>>>>>>>>>>>>>>欢迎下次再来。 商品已抢购完毕>>>>>>>>>>>>>>>>>>>>>>>>>>欢迎下次再来。 商品已抢购完毕>>>>>>>>>>>>>>>>>>>>>>>>>>欢迎下次再来。 细数了下获奖名单还是没错的。哈哈谁知道ab为啥那么多错误请求?评论告诉我一下谢谢。
  • Spring Boot Redis 秒杀实现

    简述本文主要通过一个简单的例子模拟实现秒杀情景,其中主要使用Redis事物进行实现spring boot为提供方便的环境简述本文主要通过一个简单的例子模拟实现秒杀情景,其中主要使用Redis事物进行实现spring boot为提供方便的环境。首先导入redis依赖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.1.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.leftso.demo</groupId> <artifactId>demo-redis-seckill</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo-redis-seckill</name> <description>Redis 实现产品秒杀</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/redis.clients/jedis --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.0.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>Redispool配置配置redis的连接池,这个根据自己需求改。这里测试用。package com.leftso.demo.demoredisseckill; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; @Configuration public class JedisConfig { @Bean public JedisPool jedisPool(){ JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); // 设置配置 jedisPoolConfig.setMaxTotal(1024); jedisPoolConfig.setMaxIdle(100); jedisPoolConfig.setMaxWaitMillis(100); jedisPoolConfig.setTestOnBorrow(false);//jedis 第一次启动时,会报错 jedisPoolConfig.setTestOnReturn(true); JedisPool pool=new JedisPool(jedisPoolConfig,"127.0.0.1",6379); return pool; } } 写一个秒杀的业务处理实现package com.leftso.demo.demoredisseckill.service; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.Transaction; import java.util.List; public class Seckill implements Runnable { private JedisPool jedisPool; private String userName; private String productKey; public Seckill(JedisPool jedisPool, String userName, String productKey) { this.jedisPool = jedisPool; this.userName = userName; this.productKey = productKey; } @Override public void run() { Jedis jedis=jedisPool.getResource(); try { jedis.watch(productKey); String val=jedis.get(productKey); int valInt=Integer.valueOf(val); if (valInt>=1){ Transaction tx=jedis.multi(); tx.incrBy(productKey,-1);//原子操作 List<Object> list=tx.exec(); if (list==null||list.isEmpty()){ System.out.println("用户:"+userName+" 抢购失败。"); this.run();//再抢 }else{ System.out.println("用户:"+userName+ " 抢购成功!!!"); // jedis.setnx(productKey,) jedis.rpush(productKey+"user",userName);//成功用户添加入队列 } }else{ System.out.println("商品已抢购完毕-------"); } }catch (Exception e){ e.printStackTrace(); } } } 最后模拟访问package com.leftso.demo.demoredisseckill; import com.leftso.demo.demoredisseckill.service.Seckill; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import redis.clients.jedis.JedisPool; import java.util.ArrayList; import java.util.List; @RunWith(SpringRunner.class) @SpringBootTest public class RedisSeckillTest { private final static Logger logger = LoggerFactory.getLogger(RedisSeckillTest.class); @Autowired JedisPool jedisPool; String productKey="SSSSSSKEY"; int productNum=10; @Before public void before(){ jedisPool.getResource().set(productKey,10+"");//设置产品默认库存数量 while (jedisPool.getResource().lpop(productKey+"user")!=null){ }//清空秒杀成功人用户列表 //end } @After public void after(){ String num=jedisPool.getResource().get(productKey); System.out.println("剩余库存:"+num); } @Test public void contextLoads() { try { for (int i = 0; i < 100; i++) { //每个用户件数 Thread t = new Thread(new Seckill(jedisPool,"用户"+i,productKey)); t.start(); } long size=jedisPool.getResource().llen(productKey+"user"); while (true){ if (size==productNum){ break; }else{ size=jedisPool.getResource().llen(productKey+"user"); } } List<String> successUsers=new ArrayList<>(); String user=jedisPool.getResource().lpop(productKey+"user"); while (user!=null){ successUsers.add(user); user=jedisPool.getResource().lpop(productKey+"user"); } System.out.println("活动结束>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>活动结束"); System.out.println("获奖名单:"+successUsers); Thread.currentThread().sleep(2000); } catch (Exception e) { e.printStackTrace(); } } } 执行单元测试,查看结果:.............................. 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 活动结束>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>活动结束 获奖名单:[用户66, 用户2, 用户8, 用户10, 用户56, 用户78, 用户33, 用户58, 用户16, 用户87] 剩余库存:0 Process finished with exit code 0您有任何想法欢迎评论区留言讨论,谢谢
  • Spring Boot 2.0 有哪些新特性_Spring Boot 2.0新功能

    Spring Boot 2.0 有哪些新特性_Spring Boot 2.0新功能,在本文中,我们将探讨为Spring Boot 2.0计划的一些更改和功能。我们还会描述这些变化如何帮助我们提高生产力。Spring Boot 2.0 有哪些新特性_Spring Boot 2.0新功能
  • Spring Boot 2.0 中的Spring Boot Actuator变化

    Spring Boot 2.0,Spring框架的Spring Boot 中的Spring Boot Actuator变化讲解。并且了解如何在Spring Boot 2.0中使用Actuator端点,包括创建自定义端点和使用健康指示器。Spring Boot 2.0 中的Spring Boot Actuator变化
  • Spring Boot 2.0 绑定属性资源文件 Spring Boot 2.0 读取配置文件值 Spring Boot 2.0获取配置文件值

    Spring Boot 2.0 绑定properties属性资源文件 Spring Boot 2.0 读取properties配置文件值 Spring Boot 2.0获取properties配置文件值Spring Boot 2.0 绑定properties属性资源文件 Spring Boot 2.0 读取配置文件值 Spring Boot 2.0获取配置文件值<br /> 自Spring Boot首次发布以来,就可以使用<code>@ConfigurationProperties</code>注释将属性绑定到类。也可以用不同的形式指定属性名称。 例如,person.first-name,person.firstName和PERSON_FIRSTNAME可以互换使用。 我们称这个功能为“宽松绑定”。<br /> <br /> 不幸的是,在Spring Boot 1.x中,“宽松绑定”实际上有点过于宽松。 确切地定义绑定规则是什么以及什么时候可以使用特定格式是相当困难的。 我们也开始获得有关我们的1.x实施难以解决的问题的报告。 例如,在Spring Boot 1.x中,不可能将项目绑定到java.util.Set。<br /> 所以,在Spring Boot 2.0中,我们已经着手重新设计绑定发生的方式。 我们添加了几个新的抽象,并且我们开发了一个全新的绑定API。 在这篇博文中,我们介绍了一些新的类和接口,并描述了为什么添加了它们,它们做了什么,以及如何在自己的代码中使用它们。 <h2>Property Sources</h2> 如果你一直在使用Spring,你可能很熟悉<code>Environment</code>抽象类。这个接口是一个<code>PropertyResolver</code>,它可以让你从一些底层的<code>PropertySource</code>实现中解析属性。<br /> Spring框架为常见的东西提供<code>PropertySource</code>实现,例如系统属性,命令行标志和属性文件。 Spring Boot 会以对大多数应用程序有意义的方式自动配置这些实现(例如,加载application.properties)。 <h2>Configuration Property Sources</h2> Spring Boot 2.0不是直接使用现有的<code>PropertySource</code>接口进行绑定,而是引入了一个新的<code>ConfigurationPropertySource</code>接口。 我们引入了一个新的接口,为我们提供了一个合理的地方来实施放松绑定规则,这些规则以前是活页夹的一部分<br /> 接口的主要API非常简单: code:>>ConfigurationProperty getConfigurationProperty(ConfigurationPropertyName name);<br /> 还有一个<code>IterableConfigurationPropertySource</code>变相的实现了<code>Iterable </code>接口,以便您可以发现源包含的所有名称。<br /> <br /> 通过使用以下代码,可以将Spring <code>Environment</code>调整为<code>ConfigurationPropertySources</code><br /> Iterable sources = ConfigurationPropertySources.get(environment);<br /> 如果您需要它,我们还提供一个简单的<code>MapConfigurationPropertySource</code>实现。 <h2>Configuration Property Names</h2> 事实证明,如果将其限制为一个方向,放宽属性名称的实现更容易实现。 您应该始终使用规范形式访问代码中的属性,而不管它们在基础源中的表示方式如何。<br /> ConfigurationPropertyName类强制执行这些规范的命名规则,这些规则基本归结为“使用小写的kebab-case名称”。<br /> 因此,例如,即使在基础源中使用person.firstName或PERSON_FIRSTNAME,也应该将代码中的属性称为person.first-name。<br /> Origin Support起源支持<br /> 一个Origin是Spring Boot 2.0中引入的一个新接口,可以让您精确定位从某个值加载的确切位置。 有许多Origin实现,可能最有用的是TextResourceOrigin。 这提供了加载的资源的详细信息,以及值的行号和列号。 <pre> <code class="language-html">*************************** APPLICATION FAILED TO START *************************** Description: Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'person' to scratch.PersonProperties failed: Property: person.name Value: Joe Origin: class path resource [application.properties]:1:13 Reason: length must be between 4 and 2147483647 Action: Update your application's configuration </code></pre>   <h2>Binder API</h2> Binder类(在org.springframework.boot.context.properties.bind中)可以让你获取一个或多个ConfigurationPropertySource并从它们中绑定一些东西。 更准确地说,一个Binder采用一个Bindable并返回一个BindResult。<br /> Bindable<br /> Bindable可能是现有的Java bean,类类型或复杂的ResolvableType(如List )。 这里有些例子 <pre> <code class="language-java">Bindable.ofInstance(existingBean); Bindable.of(Integer.class); Bindable.listOf(Person.class); Bindable.of(resovableType);</code></pre> <br /> Bindable也用于携带注释信息,但通常不需要关心这一点。 <h2>BindResult</h2> <br /> 绑定器不是直接返回一个绑定对象,而是返回一个名为BindResult的东西。 类似于Java 8 Streams返回Optional的方式,BinderResult表示可能绑定或可能未绑定的内容。<br /> <br /> 如果您尝试获取未绑定对象的实际结果,则会引发异常。 我们还提供了一些方法,可以让您在没有任何约束或映射到不同类型时提供替代值 <pre> <code class="language-java">var bound = binder.bind("person.date-of-birth",Bindable.of(LocalDate.class)); // Return LocalDate or throws if not bound bound.get(); // Return a formatted date or "No DOB" bound.map(dateFormatter::format).orElse("No DOB"); // Return LocalDate or throws a custom exception bound.orElseThrow(NoDateOfBirthException::new);</code></pre> <h2>Formatting and Conversion </h2> 大多数ConfigurationPropertySource实现将其基础值作为字符串公开。 当Binder需要将源值转换为其他类型时,它将委托给Spring的ConversionService API。<br /> 如果您需要调整值的转换方式,则可以自由使用格式化程序注释(如@NumberFormat或@DateFormat)。<br /> <br /> Spring Boot 2.0还引入了一些对绑定特别有用的新注释和转换器。 例如,您现在可以将诸如4s之类的值转换为持续时间。 有关详细信息,请参阅org.springframework.boot.convert包。 <h2>BindHandler</h2> 有时候,绑定时可能需要实现额外的逻辑,而BindHandler接口提供了一个很好的方法来实现这一点。 每个BindHandler都可以实现onStart,onSuccess,onFailure和onFinish方法来覆盖行为。<br /> <br /> Spring Boot提供了一些处理程序,主要用于支持现有的@ConfigurationProperties绑定。 例如,ValidationBindHandler可用于对绑定对象应用Validator验证。 <h2>@ConfigurationProperties</h2> 正如本文开始时提到的,@ConfigurationProperties从一开始就一直是Spring Boot的特色。 @ConfigurationProperties很可能仍然是大多数人执行绑定的方式。<br /> <br /> 尽管我们重写了整个绑定过程,但大多数人在升级Spring Boot 1.5应用程序时似乎并没有太多问题。 只要您遵循迁移指南中的建议,您应该会发现事情继续良好。<br /> 如果您在升级应用程序时发现问题,请在GitHub问题跟踪器上以小样本向他们报告,以便重现问题。 <h2>Future Work</h2> 我们计划在Spring Boot 2.1中继续开发Binder,我们希望支持的第一个特性是不可变的配置属性。 如果当前需要getter和setter的配置属性可以使用基于构造函数的绑定,那将是非常好的: <pre> <code class="language-java">public class Person { private final String firstName; private final String lastName; private final LocalDateTime dateOfBirth; public Person(String firstName, String lastName, LocalDateTime dateOfBirth) { this.firstName = firstName; this.lastName = lastName; this.dateOfBirth = dateOfBirth; } // getters }</code></pre> 我们认为构造函数绑定也可以很好地处理Kotlin数据类。<br /> <br /> 小结<br /> 我们希望您能够在Spring Boot 2.0中发现新的绑定功能,并且您会考虑升级现有的Spring Boot应用程序。
  • Spring Boot 2.0 支持的Apache Camel 版本发布了_Apache Camel 2.22发布支持Spring Boot 2.0

    Spring Boot 2.0 支持的Apache Camel 版本发布了_Apache Camel 2.22发布支持Spring Boot 2.0Spring Boot 2.0 支持的Apache Camel 版本发布了_Apache Camel 2.22发布支持Spring Boot 2.0