多线程异步任务的问题

例如a,b,c三个异步任务,不是随机运行就可以,它们还有一定的关系,c需要等待a的返回结果,b不需要等待谁的结果。

当异步任务产生一些关系和顺序之后,我们要编排好它们的关系进行调用,又是一个复杂的过程。

我们可以用CompletableFuture来解决这个问题。

业务场景

查询商品详情页的逻辑比较复杂,有些数据还需要远程调用,必然需要花费更多的时间。

假如商品详情页的每个查询,需要如下标注的时间才能完成。

那么,用户需要 5.5s 后才能看到商品详情页的内容。很显然是不能接受的。

如果有多个线程同时完成这 6 步操作,也许只需要 1.5s 即可完成响应。

sku的所有信息异步查询是可以的,但是sku对应的spu的信息是需要获取到sku的基本信息之后才能获取。

CompletableFuture 异步编排

静态方法创建

// runAsync函数都是没返回值的
public static CompletableFuture<Void> runAsync(Runnable runnable)// executor 指定线程池
public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor)// supplyAsync 函数都是有返回值的
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor)
package com.atlinxi.gulimall.search.thread;import java.util.concurrent.*;public class ThreadTest {// 用Executors或new ThreadPoolExecutor()创建线程池实际上都是实现ExecutorService// newFixedThreadPool 一个固定数量的线程池// 这个线程池不应该是每一个异步任务都创建一个线程池// 而是应该整个系统一个线程池,大家都把自己的任务交给这个池里执行// 当前系统中池只有一两个,可能有核心业务的或者非核心业务的// 每个异步任务,直接提交给线程池,让它自己去执行public static ExecutorService executor = Executors.newFixedThreadPool(10);public static void main(String[] args) throws ExecutionException, InterruptedException {System.out.println("main...start...");//        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {//
//            System.out.println("当前线程:" + Thread.currentThread().getId());
//
//            int i = 10 / 2;
//
//            System.out.println("运行结果:" + i);
//
//        }, executor);CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {System.out.println("当前线程:" + Thread.currentThread().getId());int i = 10 / 2;System.out.println("运行结果:" + i);return i;}, executor);System.out.println("main...end..." + future.get());}

以下方法只对CompletableFuture启动的异步任务有效

回调感知与异常处理 whenComplete()

whenComplete只能处理异常,不能处理(修改)返回值

// 与上一个函数,
// 也就是任务完成之后调用回调函数的那个函数用同一个线程
public CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action)// 回调函数依然是异步的
// 从线程池中再取一个线程
// BiConsumer param1 结果,param2 异常
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action)public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor)public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn)
package com.atlinxi.gulimall.search.thread;import java.util.concurrent.*;public class ThreadTest {// 用Executors或new ThreadPoolExecutor()创建线程池实际上都是实现ExecutorService// newFixedThreadPool 一个固定数量的线程池// 这个线程池不应该是每一个异步任务都创建一个线程池// 而是应该整个系统一个线程池,大家都把自己的任务交给这个池里执行// 当前系统中池只有一两个,可能有核心业务的或者非核心业务的// 每个异步任务,直接提交给线程池,让它自己去执行public static ExecutorService executor = Executors.newFixedThreadPool(10);public static void main(String[] args) throws ExecutionException, InterruptedException {System.out.println("main...start...");//        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {//
//            System.out.println("当前线程:" + Thread.currentThread().getId());
//
//            int i = 10 / 2;
//
//            System.out.println("运行结果:" + i);
//
//        }, executor);CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {System.out.println("当前线程:" + Thread.currentThread().getId());int i = 10 / 0;System.out.println("运行结果:" + i);return i;}, executor).whenComplete((res,exception)->{// 虽然能得到异常信息,但是没法修改返回数据System.out.println("异步任务完成了。。。结果是:" + res + ";异常是:" + exception);// 感知异常,同时返回默认值}).exceptionally(throwable -> 10);// 10System.out.println("main...end..." + future.get());}

回调处理与异常处理 handle()

handle()可以处理返回值,也可以处理异常

public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn)public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn)public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor)
/*** 方法执行完成后的处理*/CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {System.out.println("当前线程:" + Thread.currentThread().getId());int i = 10 / 2;System.out.println("运行结果:" + i);return i;}, executor).handle((res,exception)->{if (res!=null){return res * 2;}if (exception!=null){return 0;}return 0;});

线程串行化

// 启动线程后的异步任务完成后再启动一个线程任务,
// 可以感知到上一个线程的结果和这个线程任务的结果
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)// 启动线程后的异步任务完成后再启动一个线程任务,可以感知到上一个线程的结果
public CompletableFuture<Void> thenAccept(Consumer<? super T> action)public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action)public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor)// 启动线程后的异步任务完成后再启动一个线程任务,不能感知上一个线程的结果
public CompletableFuture<Void> thenRun(Runnable action)public CompletableFuture<Void> thenRunAsync(Runnable action)public CompletableFuture<Void> thenRunAsync(Runnable action,Executor executor)
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {System.out.println("当前线程:" + Thread.currentThread().getId());int i = 10 / 2;System.out.println("运行结果:" + i);return i;}, executor).thenApplyAsync((res)->{System.out.println("任务2启动了" + res);return res * 10;},executor);System.out.println("main...end..." + future1.get());

两任务组合 - 都要完成

两个任务都完成以后,再触发这个任务

// CompletableFuture 实现了 CompletionStage
// 就相当于传一个CompletableFuture// 异步任务启动后再执行一个线程任务,两个任务都完成之后再执行other
// 三个任务均有返回值
public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn)public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn)public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn, Executor executor)// 异步任务启动后再执行一个线程任务,两个任务都完成之后再执行other
// 前两个任务均有返回值,该任务没有返回值
public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action)public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action)public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action, Executor executor)// 异步任务启动后再执行一个线程任务,两个任务都完成之后再执行action
// 三个任务均无返回值
public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other,Runnable action)public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action)public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action,Executor executor)
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {System.out.println("当前线程:" + Thread.currentThread().getId());int i = 10 / 2;System.out.println("运行结果:" + i);return i;}, executor);CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {return "hello";}, executor);CompletableFuture<String> future3 = future1.thenCombineAsync(future2, (res1, res2) -> {return res1 + res2;},executor);System.out.println("main...end..." + future3.get());

两任务组合 - 一个完成

// 和上面的所有函数一样,有异步的,有可以指定线程池的
// 都是执行完其中任何一个任务就可以执行action
// 等待的任务不阻塞// 两个任务均无返回值
public CompletableFuture<Void> runAfterEither(CompletionStage<?> other,Runnable action)// 上一个任务有返回值,这个任务无返回值
public CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action)// 两个任务都有返回值
public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn,Executor executor)

多任务组合

// 等待所有任务完成
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)// 只要有一个任务完成
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {System.out.println("future1:" + Thread.currentThread().getId());int i = 10 / 2;System.out.println("运行结果:" + i);return i;}, executor);CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {System.out.println("future2");return "hello";}, executor);CompletableFuture<String> future3 = future1.thenCombineAsync(future2, (res1, res2) -> {System.out.println("future3");return res1 + res2;},executor);CompletableFuture<Void> future = CompletableFuture.allOf(future1, future2, future3);//        main...start...
//        future1:22
//        运行结果:5
//        future2
//        future3
//        main...end...future.get();   // 等待所有结果完成System.out.println("main...end...");

商品详情

可以讲的点,确定sku销售属性


如上图,需要根据颜色和内存这两个销售属性的值确定sku,

思路是可以求出各销售属性的值分别被哪些sku所拥有,然后所有销售属性都拥有的那个sku就是被选中的sku

 # pms_sku_info 求出spu所对应的sku# 然后通过连接pms_sku_sale_attr_value,分组得到各属性值的所有skuSELECT pss.attr_id  attrId,pss.attr_name    attrName,pss.attr_value   attrValue,GROUP_CONCAT(DISTINCT ps.sku_id) skuIdsFROM pms_sku_info psLEFT JOIN pms_sku_sale_attr_value pss ON ps.sku_id = pss.sku_idWHERE spu_id = 6GROUP BY pss.attr_id, pss.attr_name, pss.attr_value;SELECT sku_id,spu_id FROM pms_sku_info;
SELECT attr_id,attr_name,attr_value,sku_id FROM pms_sku_sale_attr_value;

修改hots和gateway

修改windows本地hosts

192.168.56.10    gulimall.com
192.168.56.10   search.gulimall.com
192.168.56.10   item.gulimall.com

修改gateway路由

- id: gulimall_host_routeuri: lb://gulimall-productpredicates:- Host=gulimall.com,item.gulimall.com

在linux宿主机/mydata/nginx/html/static/item上传item的静态文件,修改item.html文件的静态资源路径。

修改search模块的list.html

<p class="da"><a th:href="|http://item.gulimall.com/${product.skuId}.html|"><img th:src="${product.skuImg}" class="dim"></a>
</p>

controller

package com.atlinxi.gulimall.product.web;import com.atlinxi.gulimall.product.service.SkuInfoService;
import com.atlinxi.gulimall.product.vo.SkuItemVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;import java.util.concurrent.ExecutionException;@Controller
public class ItemController {@AutowiredSkuInfoService skuInfoService;/*** 展示当前sku的详情* @param skuId* @return*/@GetMapping("/{skuId}.html")public String skuItem(@PathVariable("skuId") Long skuId, Model model) throws ExecutionException, InterruptedException {SkuItemVo skuItemVo = skuInfoService.item(skuId);model.addAttribute("item",skuItemVo);return "item";}
}

service

SkuInfoServiceImpl@Overridepublic SkuItemVo item(Long skuId) throws ExecutionException, InterruptedException {SkuItemVo skuItemVo = new SkuItemVo();CompletableFuture<SkuInfoEntity> infoFuture = CompletableFuture.supplyAsync(() -> {// 1. sku基本信息获取    pms_sku_infoSkuInfoEntity info = getById(skuId);skuItemVo.setInfo(info);return info;}, executor);// 不后面直接.thenAcceptAsync的原因是// thenAcceptAsync 是需要等待上一个任务完成后才启动下一个,就失去了异步的意义CompletableFuture<Void> saleAttrFuture = infoFuture.thenAcceptAsync((res) -> {//            Long catalogId = res.getCatalogId();
//            Long spuId = res.getSpuId();// 3. 获取spu的销售属性组合List<SkuItemSaleAttrVo> saleAttrVos = skuSaleAttrValueService.getSaleAttrsBySpuId(res.getSpuId());skuItemVo.setSaleAttr(saleAttrVos);}, executor);CompletableFuture<Void> descFuture = infoFuture.thenAcceptAsync((res) -> {//            // 4. 获取spu的介绍  pms_spu_info_descSpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(res.getSpuId());skuItemVo.setDesp(spuInfoDescEntity);}, executor);CompletableFuture<Void> baseAttrFuture = infoFuture.thenAcceptAsync((res) -> {//           // 5. 获取spu的规格参数信息List<SpuItemAttrGroupVo> attrGroupVos = attrGroupService.getAttrGroupWithAttrsBySpuId(res.getSpuId(), res.getCatalogId());skuItemVo.setGroupAttrs(attrGroupVos);}, executor);// 2. sku图片信息       pms_sku_images// 这个和上面的没什么关系,也没有返回值,所以再创建一个异步任务就可以了CompletableFuture<Void> imageFuture = CompletableFuture.runAsync(() -> {List<SkuImagesEntity> images = imagesService.getImagesBySkuId(skuId);skuItemVo.setImages(images);}, executor);// 等到所有任务都完成// infoFuture 不写的原因是,其他异步任务需要关联它,其他的异步任务完了,它肯定就完了CompletableFuture.allOf(saleAttrFuture, descFuture, baseAttrFuture, imageFuture).get();return skuItemVo;}@Overridepublic List<SkuItemSaleAttrVo> getSaleAttrsBySpuId(Long spuId) {SkuSaleAttrValueDao dao = this.baseMapper;List<SkuItemSaleAttrVo> saleAttrVos = dao.getSaleAttrsBySpuId(spuId);return saleAttrVos;}@Overridepublic List<SpuItemAttrGroupVo> getAttrGroupWithAttrsBySpuId(Long spuId, Long catalogId) {// 1. 查出当前spu对应的所有属性的分组信息以及当前分组下的所有属性对应的值AttrGroupDao baseMapper = this.getBaseMapper();List<SpuItemAttrGroupVo> vos = baseMapper.getAttrGroupWithAttrsBySpuId(spuId,catalogId);return vos;}@Overridepublic List<SkuImagesEntity> getImagesBySkuId(Long skuId) {SkuImagesDao baseMapper = this.baseMapper;List<SkuImagesEntity> imagesEntities = baseMapper.selectList(new QueryWrapper<SkuImagesEntity>().eq("sku_id", skuId));return imagesEntities;}

dao

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.atlinxi.gulimall.product.dao.SkuSaleAttrValueDao"><!-- 可根据自己的需求,是否要使用 --><resultMap type="com.atlinxi.gulimall.product.entity.SkuSaleAttrValueEntity" id="skuSaleAttrValueMap"><result property="id" column="id"/><result property="skuId" column="sku_id"/><result property="attrId" column="attr_id"/><result property="attrName" column="attr_name"/><result property="attrValue" column="attr_value"/><result property="attrSort" column="attr_sort"/></resultMap><resultMap id="SkuItemSaleAttrVo" type="com.atlinxi.gulimall.product.vo.SkuItemSaleAttrVo"><result property="attrId" column="attrId"></result><result property="attrName" column="attrName"></result><collection property="attrValues" ofType="com.atlinxi.gulimall.product.vo.AttrValueWithSkuIdVo"><result property="skuIds" column="skuIds"></result><result property="attrValue" column="attrValue"></result></collection></resultMap><select id="getSaleAttrsBySpuId" resultMap="SkuItemSaleAttrVo"><!--         SELECT pss.attr_name attrName,pss.attr_id attrId,GROUP_CONCAT(DISTINCT pss.attr_value) attrValuesFROM pms_sku_info ps LEFT JOIN pms_sku_sale_attr_value pss ON ps.sku_id=pss.sku_idWHERE spu_id = #{spuId} GROUP BY pss.attr_name,pss.attr_id -->SELECT pss.attr_id  attrId,pss.attr_name    attrName,pss.attr_value   attrValue,GROUP_CONCAT(DISTINCT ps.sku_id) skuIdsFROM pms_sku_info psLEFT JOIN pms_sku_sale_attr_value pss ON ps.sku_id = pss.sku_idWHERE spu_id = #{spuId}GROUP BY pss.attr_id, pss.attr_name, pss.attr_value</select></mapper><?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.atlinxi.gulimall.product.dao.AttrGroupDao"><!-- 可根据自己的需求,是否要使用 --><resultMap type="com.atlinxi.gulimall.product.entity.AttrGroupEntity" id="attrGroupMap"><result property="attrGroupId" column="attr_group_id"/><result property="attrGroupName" column="attr_group_name"/><result property="sort" column="sort"/><result property="descript" column="descript"/><result property="icon" column="icon"/><result property="catelogId" column="catelog_id"/></resultMap><!--    自定义结果集,因为SkuItemVo.SpuItemAttrGroupVo里还有个对象,所以resultType不够用了只要有嵌套属性就要封装自定义结果
--><resultMap id="SpuItemAttrGroupVo" type="com.atlinxi.gulimall.product.vo.SpuItemAttrGroupVo"><result property="groupName" column="attr_group_name"></result><collection property="attrs" ofType="com.atlinxi.gulimall.product.vo.Attr"><result column="attr_name" property="attrName"></result><result column="attr_value" property="attrValue"></result></collection></resultMap><select id="getAttrGroupWithAttrsBySpuId" resultMap="SpuItemAttrGroupVo">select pav.spu_id,ag.attr_group_name,ag.attr_group_id,aar.attr_id,attr.attr_name,pav.attr_valuefrom pms_attr_group agleft join pms_attr_attrgroup_relation aar on aar.attr_group_id = ag.attr_group_idleft join pms_attr attr on attr.attr_id = aar.attr_idleft join pms_product_attr_value pav on pav.attr_id=attr.attr_idwhere ag.catelog_id=#{catalogId} and pav.spu_id = #{spuId}</select></mapper>

实体类

package com.atlinxi.gulimall.product.vo;import com.atlinxi.gulimall.product.entity.SkuImagesEntity;
import com.atlinxi.gulimall.product.entity.SkuInfoEntity;
import com.atlinxi.gulimall.product.entity.SpuInfoDescEntity;
import lombok.Data;
import java.util.List;@Data
public class SkuItemVo {// 1. sku基本信息获取    pms_sku_infoprivate SkuInfoEntity info;private boolean hasStock = true;// 2. sku图片信息       pms_sku_imagesprivate List<SkuImagesEntity> images;// 3. 获取spu的销售属性组合private List<SkuItemSaleAttrVo> saleAttr;// 4. 获取spu的介绍private SpuInfoDescEntity desp;// 5. 获取spu的规格参数信息private List<SpuItemAttrGroupVo> groupAttrs;}package com.atlinxi.gulimall.product.vo;import lombok.Data;
import lombok.ToString;import java.util.List;@Data
@ToString
public class SkuItemSaleAttrVo {private Long attrId;private String attrName;private List<AttrValueWithSkuIdVo> attrValues;
}package com.atlinxi.gulimall.product.vo;import lombok.Data;@Data
public class AttrValueWithSkuIdVo {private String attrValue;private String skuIds;
}package com.atlinxi.gulimall.product.vo;import lombok.Data;
import lombok.ToString;import java.util.List;@Data
@ToString
public class SpuItemAttrGroupVo {private String groupName;private List<Attr> attrs;
}

maven

<?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 https://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.5.5</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.atlinxi.gulimall</groupId><artifactId>gulimall-product</artifactId><version>0.0.1-SNAPSHOT</version><name>gulimall-product</name><description>谷粒商城-商品服务</description><properties><java.version>1.8</java.version><spring-cloud.version>2020.0.4</spring-cloud.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency><dependency><groupId>com.atlinxi.gulimall</groupId><artifactId>gulimall-common</artifactId><version>0.0.1-SNAPSHOT</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!--        由于SpringCloud Feign高版本不使用Ribbon而是使用spring-cloud-loadbalancer,所以需要引用spring-cloud-loadbalancer或者降版本--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-loadbalancer</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
<!--        页面渲染我们使用thymeleaf,这应该和freemarker是差不多的,都是模板引擎-->
<!--        优点:它是一个自然化语言,编写的语言前端可以直接使用,方便前后人员的分工合作-->
<!--        缺点:性能比其他模板引擎要低一点,但是我们在生产环境开启了它的缓存功能,性能也是很高的--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><!--spring元数据处理器,加了就有提示了,不加也没任何问题optional 依赖不会被传递
--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><!--        解决thymeleaf页面修改必须得重启服务器的问题,页面修改之后,需要重新build修改的页面-->
<!--        前提是需要关闭thymeleaf的缓存--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency><!--        以后使用redisson作为所有分布式锁,分布式对象等功能框架--><dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.12.0</version></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

线程池配置

package com.atlinxi.gulimall.product.config;import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;// ThreadPoolConfigProperties 已经被注入容器了,就不需要这样配置了
//@EnableConfigurationProperties(ThreadPoolConfigProperties.class)
@Configuration
public class MyThreadConfig {@Beanpublic ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties pool){return new ThreadPoolExecutor(pool.getCoreSize(),pool.getMaxSize(),pool.getKeepAliveTime(), TimeUnit.SECONDS,new LinkedBlockingQueue<>(100000),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());}}package com.atlinxi.gulimall.product.config;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;@ConfigurationProperties("gulimall.thread")
@Component
@Data
public class ThreadPoolConfigProperties {private Integer coreSize;private Integer maxSize;private Integer keepAliveTime;
}// application.properties
#�Զ���������Щ
#CacheAutoConfiguration�ᵼ��RedisCacheConfiguration
#�Զ�����˻�������� RedisCacheManager
spring.cache.type=redis
# redis key ttl msΪ��λ
spring.cache.redis.time-to-live=3600000
# �����ǰ׺�����ָ����ǰ׺��������ָ����ǰ׺�����û�У���Ĭ��ʹ�û����������Ϊǰ׺
# @Cacheable(value = {"category"},key = "#root.method.name")
#   category::getLevel1Categorys
# ��������ָ���ˣ�����
# CACHE_getLevel1Categorys
#spring.cache.redis.key-prefix=CACHE_
# �����ʹ��ǰ׺����ôע���к������ļ��еĶ�������Ч
# getLevel1Categorys
spring.cache.redis.use-key-prefix=true
# �Ƿ												

谷粒商城十五商品详情CompletableFuture异步编排相关推荐

  1. 谷粒商城高级篇(38)——异步编排之商品详情查询

    异步编排之商品详情查询 异步编排 CompletableFuture介绍 创建异步对象 计算完成时回调方法 handle 方法 线程串行化方法 两任务组合 全部完成 一个完成即可 多任务组合 业务描述 ...

  2. 尚硅谷谷粒商城第十二天 商品详情页及异步编排

    1. 商品详情 当用户搜索到商品,肯定会点击查看,就会进入商品详情页,接下来我们完成商品详情页的展示. 商品详情浏览量比较大,并发高,我们会独立开启一个微服务,用来展示商品详情. 1.1. 创建mod ...

  3. 谷粒商城笔记+踩坑(15)——商品详情搭建+异步编排

    导航: 谷粒商城笔记+踩坑汇总篇 目录 1.搭建页面环境 1.1.配置 Nginx 和 网关 1.2.动静资源配置 1.3.搜索页到详情页跳转 2.模型类抽取和controller 2.1.分析首页需 ...

  4. Day140-142.尚品汇:AOP+Redis缓存+redssion分布式锁、CompletableFuture异步编排、首页三级分类展示、Nginx静态代理

    目录 Day08 一.获取商品详情 加入缓存 二.全局缓存:分布式锁与aop 整合 三.布隆过滤器 四.CompletableFuture 异步编排 jdk1.8 Day09 1. 将item 改为多 ...

  5. 谷粒商城 高级篇 (十四) ---------- 商品详情

    目录 一.详情数据 二.查询详情 三.sku 组合切换 四.关键 SQL 一.详情数据 封装成 vo 如下: SkuItemVo: @Data public class SkuItemVo {//1. ...

  6. 乐优商城学习笔记十九-商品详情(二)

    2.页面静态化 2.1.简介 2.1.1.问题分析 现在,我们的页面是通过Thymeleaf模板引擎渲染后返回到客户端.在后台需要大量的数据查询,而后渲染得到HTML页面.会对数据库造成压力,并且请求 ...

  7. 商城-3 查询商品详情页信息

    查询商品详情页信息 上面章节完成了查询spu列表 在商品列表中选中商品后,会显示这个商品的详情信息 商品详情页我们需要显示的信息包括 根据spuId查询spu信息 根据spuId查询spuDetail ...

  8. 谷粒商城项目8——商品上架 上架商品sku保存到es nginx配置

    文章目录 一.商城业务 1.商品上架 1.1 ES 的存储结构分析 1.2 PUT product 1.3 一些细节 2.商品上架-构造基本数据 3.商品上架-业务代码: 4.商品上架-search模 ...

  9. 微信小程序商城系列之商品详情页

    wxml: <!--商品幻灯片--> <swiper indicator-dots="{{indicatorDots}}" autoplay="{{au ...

最新文章

  1. hadoop系统 hdfs 命令行操作
  2. CCF201604-3 路径解析(100分)
  3. Linux下安装JDK,Tomcat,Mysql详细教程
  4. MySQL root密码重置 报错:mysqladmin: connect to server at 'localhost' failed的解决方案
  5. 一键发布部署vs插件[AntDeploy]开源了
  6. .toString(c) 将数字值 渲染成 货币形式
  7. WebAPI Get
  8. ansible基本模块-server
  9. Giroro制造武器
  10. 网络便签与网络通讯录
  11. java application.doevents_Application.DoEvents()笔记
  12. P1179 数字统计
  13. 谈谈时间管理--陶哲轩
  14. 对话管理DST:Deep Neural Network Approach for the Dialog State Tracking Challenge
  15. 7-4 打印九九口诀表 (15 分)
  16. js将阿拉伯数字翻译成中文的大写数字
  17. 手机邮箱怎么注册申请,手机邮箱在哪里找到,电子邮箱怎么弄?
  18. html left属性,CSS属性参考 | left
  19. 有关计算机的课外阅读书籍,关于2020级本科生《认识实习》课外阅读书目的通知...
  20. go每日新闻(2021-02-25)——悄悄告诉你:很可能Go 1.17就能尝试泛型

热门文章

  1. 【caffe】caffe之反卷积层
  2. 股票历史走势的比较及显示
  3. Python编程|手把手教植物大战僵尸,代码开源
  4. DACE- a matlab Kriging toolbox
  5. C++/C策略打怪小游戏 (修复并优化了的版本)
  6. win7 启动速度慢 IDE模式 解决办法
  7. 阿里布局大数据 盈利模式还需探索
  8. 【Eureka】【08】EurekaClient优雅下线,先把服务状态修改成OUT_OF_SERVICE,睡眠10s把服务EurekaClient kill
  9. 关于jfm捕获声音的问题
  10. AutoSAR Adaptive platform 初探