Typora
typora11.16有毒,两天没了三次内容,已经写到了前端,不过前端也不想听了,反正cv就完事了
检索服务-页面渲染
吐了,CV
这一块真的是败笔
面包屑导航
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| if (param.getAttrs() != null && param.getAttrs().size() > 0) { List<SearchResultVo.NavVo> navVoList = param.getAttrs().stream().map(attr -> { SearchResultVo.NavVo navVo = new SearchResultVo.NavVo(); String[] s = attr.split("_"); AttrResponseTo attrResponseTo = productFeignService.attrResponseInfoFeign(Long.valueOf(s[0])); if (attrResponseTo != null) { navVo.setNavName(attrResponseTo.getAttrName()); } else { navVo.setNavName(s[0]); } navVo.setNavValue(s[1]); String encode = null; try { encode = URLEncoder.encode(attr, "UTF-8").replace("+", "%20"); } catch (UnsupportedEncodingException ex) { ex.printStackTrace(); } String replace = param.get_queryString().replace("&attrs=" + encode, ""); navVo.setLink("http://search.gulimall.com/list.html?" + replace); return navVo; }).collect(Collectors.toList()); resultVo.setNavs(navVoList); } if (param.getBrandId() != null && param.getBrandId().size() > 0) { List<SearchResultVo.NavVo> navs = resultVo.getNavs(); SearchResultVo.NavVo navVo = new SearchResultVo.NavVo(); navVo.setNavName("品牌"); StringBuilder builder = new StringBuilder(); for (SearchResultVo.BrandVo brand : resultVo.getBrands()) { builder.append(brand.getBrandName()).append(";"); } navVo.setNavValue(String.valueOf(builder)); }
|
异步
初始化线程的 4 种方式
- 继承 Thread
- 实现 Runnable 接口
- 实现 Callable 接口 + FutureTask (可以拿到返回结果, 可以处理异常)
- 线程池
方式 1 和方式 2: 主进程无法获取线程的运算结果。 不适合当前场景。方式 3: 主进程可以获取线程的运算结果, 但是不利于控制服务器中的线程资源。 可以导致服务器资源耗尽。方式 4: 通过如下两种方式初始化线程池Executors.newFiexedThreadPool(3); 或者new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit unit, workQueue, threadFactory, handler);
通过线程池性能稳定, 也可以获取执行结果, 并捕获异常。 但是, 在业务复杂情况下, 一个异步调用可能会依赖于另一个异步调用的执行结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| public class ThreadTest {
public static ExecutorService service = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException { System.out.println("---------- main方法start ----------");
service.submit(new Runnable1());
System.out.println("---------- main方法end ----------"); }
public static class Thread1 extends Thread { @Override public void run() { System.out.println("当前线程ID:" + Thread.currentThread().getId()); int i = 10 / 2; System.out.println("运行结果:" + i); } }
public static class Runnable1 implements Runnable {
@Override public void run() { System.out.println("当前线程ID:" + Thread.currentThread().getId()); int i = 10 / 2; System.out.println("运行结果:" + i); } }
public static class Callable1 implements Callable<Integer> { @Override public Integer call() throws Exception {
System.out.println("当前线程ID:" + Thread.currentThread().getId()); int i = 10 / 2; System.out.println("运行结果:" + i); return i; } } }
|
线程池
线程池种类
- newCachedThreadPool
创建一个可缓存线程池, 如果线程池长度超过处理需要, 可灵活回收空闲线程, 若无可回收, 则新建线程。
core为0,所有线程可回收
- newFixedThreadPool
创建一个定长线程池, 可控制线程最大并发数, 超出的线程会在队列中等
core = max,max-core线程可回收
- newScheduledThreadPool
创建一个定长线程池, 支持定时及周期性任务执行。
- newSingleThreadExecutor
创建一个单线程化的线程池, 它只会用唯一的工作线程来执行任务, 保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public class ThreadTest {
public static ExecutorService service = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException {
ThreadPoolExecutor executor = new ThreadPoolExecutor( 5, 200, 10, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(100000), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
System.out.println("---------- main方法end ----------"); } }
|
为什么使用线程池
CompletableFuture 异步编排
创建异步对象
CompletableFuture 提供了四个静态方法来创建一个异步操作
1、 runXxxx 都是没有返回结果的, supplyXxx 都是可以获取返回结果的
2、 可以传入自定义的线程池, 否则就用默认的线程池;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class CompletableFutureTest {
public static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException { System.out.println("---------- main方法start ----------");
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { System.out.println("当前线程ID:" + Thread.currentThread().getId()); int i = 10 / 2; System.out.println("运行结果:" + i); return i; }, executor); System.out.println("future结果:" + future.get());
System.out.println("---------- main方法end ----------"); }
}
|
计算完成时回调方法、异常处理
whenComplete 可以处理正常和异常的计算结果, exceptionally 处理异常情况。
whenComplete 和 whenCompleteAsync 的区别
- whenComplete: 是执行当前任务的线程执行继续执行 whenComplete 的任务
- whenCompleteAsync: 是执行把 whenCompleteAsync 这个任务继续提交给线程池
来进行执行。
方法不以 Async 结尾, 意味着 Action 使用相同的线程执行, 而 Async 可能会使用其他线程
执行(如果是使用相同的线程池, 也可能会被同一个线程选中执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class CompletableFutureTest {
public static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException { System.out.println("---------- main方法start ----------");
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { System.out.println("当前线程ID:" + Thread.currentThread().getId()); int i = 10 / 2; System.out.println("运行结果:" + i); return i; }, executor).whenComplete((result, exception) -> { System.out.println("异步任务成功完成了...结果是:" + result + ";异常是:" + exception); }); System.out.println("future结果:" + future.get());
System.out.println("---------- main方法end ----------"); }
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class CompletableFutureTest {
public static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException { System.out.println("---------- main方法start ----------");
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { System.out.println("当前线程ID:" + Thread.currentThread().getId()); int i = 10 / 0; System.out.println("运行结果:" + i); return i; }, executor).whenComplete((result, exception) -> { System.out.println("异步任务成功完成了...结果是:" + result + ";异常是:" + exception); }).exceptionally((exception) -> { return 10; }); System.out.println("future结果:" + future.get());
System.out.println("---------- main方法end ----------"); }
}
|
handle方法
和 complete 一样, 可对结果做最后的处理(可处理异常) , 可改变返回值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public class CompletableFutureTest {
public static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException { System.out.println("---------- main方法start ----------");
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { System.out.println("当前线程ID:" + Thread.currentThread().getId()); int i = 10 / 0; System.out.println("运行结果:" + i); return i; }, executor).handle((result, exception) -> { System.out.println("异步任务成功完成了...结果是:" + result + ";异常是:" + exception); if (result != null) { return result; } if (exception != null) { return 10; } return 0; }); System.out.println("future结果:" + future.get());
System.out.println("---------- main方法end ----------"); } }
|
线程串行化方法
thenApply 方法: 当一个线程依赖另一个线程时, 获取上一个任务返回的结果, 并返回当前
任务的返回值。
thenAccept 方法: 消费处理结果。 接收任务的处理结果, 并消费处理, 无返回结果。
thenRun 方法: 只要上面的任务执行完成, 就开始执行 thenRun, 只是处理完任务后, 执行thenRun 的后续操作带有 Async 默认是异步执行的。 同之前。
以上都要前置任务成功完成。
Function<? super T,? extends U>
T: 上一个任务返回结果的类型
U: 当前任务的返回值类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class CompletableFutureTest {
public static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException { System.out.println("---------- main方法start ----------");
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { System.out.println("当前线程ID:" + Thread.currentThread().getId()); int i = 10 / 2; System.out.println("运行结果:" + i); return i; }, executor).thenApplyAsync(result -> { System.out.println("任务二启动,上一次结果+" + result); return result + 1; }, executor); System.out.println("future结果:" + future.get());
System.out.println("---------- main方法end ----------"); }
}
|
两任务组合 - 都要完成
两个任务必须都完成, 触发该任务。
thenCombine: 组合两个 future, 获取两个 future 的返回结果, 并返回当前任务的返回值
thenAcceptBoth: 组合两个 future, 获取两个 future 任务的返回结果, 然后处理任务, 没有
返回值。
runAfterBoth: 组合两个 future, 不需要获取 future 的结果, 只需两个 future 处理完任务后,
处理该任务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| CompletableFuture<Object> future01 = CompletableFuture.supplyAsync(() -> { System.out.println("任务1线程:" + Thread.currentThread().getId()); int i = 10 / 4; System.out.println("任务1结束:" ); return i; }, executor);
CompletableFuture<Object> future02 = CompletableFuture.supplyAsync(() -> { System.out.println("任务2线程:" + Thread.currentThread().getId());
try { Thread.sleep(3000); System.out.println("任务2结束:" ); } catch (InterruptedException e) { e.printStackTrace(); } return "Hello"; }, executor);
future01.runAfterBothAsync(future02,()->{ System.out.println("任务3开始..."); },executor);
future01.thenAcceptBothAsync(future02,(f1,f2)->{ System.out.println("任务3开始...之前的结果:"+f1+"--》"+f2); },executor);
CompletableFuture<String> future = future01.thenCombineAsync(future02, (f1, f2) -> { return f1 + ":" + f2 + " -> Haha"; }, executor);
|
两任务组合 - 一个完成
当两个任务中, 任意一个 future 任务完成的时候, 执行任务。
applyToEither: 两个任务有一个执行完成, 获取它的返回值, 处理任务并有新的返回值。
acceptEither: 两个任务有一个执行完成, 获取它的返回值, 处理任务, 没有新的返回值。
runAfterEither: 两个任务有一个执行完成, 不需要获取 future 的结果, 处理任务, 也没有返
回值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
|
多任务组合
allOf: 等待所有任务完成
anyOf: 只要有一个任务完成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> { System.out.println("查询商品的图片信息"); return "hello.jpg"; },executor);
CompletableFuture<String> futureAttr = CompletableFuture.supplyAsync(() -> { System.out.println("查询商品的属性"); return "黑色+256G"; },executor);
CompletableFuture<String> futureDesc = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(3000); System.out.println("查询商品介绍"); } catch (InterruptedException e) { e.printStackTrace(); } return "华为"; },executor);
CompletableFuture<Object> anyOf = CompletableFuture.anyOf(futureImg, futureAttr, futureDesc); anyOf.get();
System.out.println("main....end...."+anyOf.get());
}
|
前台 - 商品详情
环境搭建
静态资源
存放到nginx -> html -> static 下对应文件夹
html放到对应模块resources -> templates下
host
192.168.128.129 item.gulimall.com
nacos
之前配置的*.gulimall.com即可
gateway
1 2 3 4 5 6 7 8 9
| - id: gulimall_host_route uri: lb://gulimall-product predicates: - Host=gulimall.com,item.gulimall.com - id: gulimall_search_route uri: lb://gulimall-search predicates: - Host=search.gulimall.com
|
页面基本跳转
1 2 3 4
| <a th:href="|http://item.gulimall.com/${product.skuId}.html|"> <img th:src="${product.skuImg}" class="dim"> </a>
|
controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Controller public class ItemController {
@Autowired SkuInfoService skuInfoService;
@GetMapping("/{skuId}.html") public String skuItem(@PathVariable("skuId") Long skuId, Model model) { SkuItemVo skuItemVo = skuInfoService.getItemInfo(skuId); model.addAttribute("item", skuItemVo); return "item"; } }
|
SkuItemVo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| @Data public class SkuItemVo { SkuInfoEntity info; boolean hasStock = true; List<SkuImagesEntity> images; List<SkuItemSaleAttrVo> saleAttr; SpuInfoDescEntity desp; List<SpuItemAttrGroupVo> groupAttrs;
@Data public static class SkuItemSaleAttrVo { private Long attrId; private String attrName; private List<AttrValueWithSkuIdVo> attrValues; }
@Data public static class SpuItemAttrGroupVo { private String groupName; private List<SpuItemBaseAttrVo> attrs; }
@Data public static class SpuItemBaseAttrVo { private String attrName; private String attrValue; }
@Data public static class AttrValueWithSkuIdVo { private String attrValue; private String skuIds; }
}
|
封装基本信息
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| @Service("skuInfoService") public class SkuInfoServiceImpl extends ServiceImpl<SkuInfoDao, SkuInfoEntity> implements SkuInfoService {
@Override public SkuItemVo getItemInfo(Long skuId) { SkuItemVo skuItemVo = new SkuItemVo();
SkuInfoEntity skuInfoEntity = getById(skuId); skuItemVo.setInfo(skuInfoEntity); Long spuId = skuInfoEntity.getSpuId(); Long catalogId = skuInfoEntity.getCatalogId();
List<SkuImagesEntity> skuImagesEntityList = skuImagesService.getBySkuId(skuId); skuItemVo.setImages(skuImagesEntityList);
SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(spuId); skuItemVo.setDesp(spuInfoDescEntity);
return skuItemVo; } }
|
封装spu规格参数
service
1 2 3
| List<SkuItemVo.SpuItemAttrGroupVo> spuItemAttrGroupVoList = attrGroupService.getGroupWithAttrForSkuItem(spuId, catalogId); skuItemVo.setGroupAttrs(spuItemAttrGroupVoList);
|
1 2 3 4 5 6 7 8
| @Service("attrGroupService") public class AttrGroupServiceImpl extends ServiceImpl<AttrGroupDao, AttrGroupEntity> implements AttrGroupService { @Override public List<SkuItemVo.SpuItemAttrGroupVo> getWithAttrForSkuItem(Long spuId, Long catalogId) { return baseMapper.getWithAttrForSkuItem(spuId, catalogId); }
}
|
mapper
联表查询,自定义结果集,内部类使用$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <?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.atguigu.gulimall.product.dao.AttrGroupDao"> <resultMap id="spuItemAttrGroupVo" type="com.atguigu.gulimall.product.vo.front.SkuItemVo$SpuItemAttrGroupVo"> <result property="groupName" column="attr_group_name" /> <collection property="attrs" ofType="com.atguigu.gulimall.product.vo.front.SkuItemVo$SpuItemBaseAttrVo"> <result property="attrName" column="attr_name" /> <result property="attrValue" column="attr_value" /> </collection> </resultMap> <select id="getWithAttrForSkuItem" resultMap="spuItemAttrGroupVo"> select ag.attr_group_id, ag.attr_group_name, aar.attr_id, pav.attr_name, pav.attr_value from gulimall_pms.pms_attr_group ag left join gulimall_pms.pms_attr_attrgroup_relation aar on aar.attr_group_id = ag.attr_group_id left join gulimall_pms.pms_product_attr_value pav on pav.attr_id = aar.attr_id where ag.catelog_id = #{catalogId} and pav.spu_id = #{spuId}; </select> </mapper>
|
封装spu销售属性
service
1 2 3
| List<SkuItemVo.SkuItemSaleAttrVo> skuItemSaleAttrVoList = skuSaleAttrValueService.getAttrBySpuId(skuId); skuItemVo.setSaleAttr(skuItemSaleAttrVoList);
|
1 2 3 4 5 6 7 8
| @Service("skuSaleAttrValueService") public class SkuSaleAttrValueServiceImpl extends ServiceImpl<SkuSaleAttrValueDao, SkuSaleAttrValueEntity> implements SkuSaleAttrValueService { @Override public List<SkuItemVo.SkuItemSaleAttrVo> getAttrBySpuId(Long spuId) { return baseMapper.getAttrBySpuId(spuId); }
}
|
mapper
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <?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.atguigu.gulimall.product.dao.SkuSaleAttrValueDao"> <resultMap id="skuItemSaleAttrVo" type="com.atguigu.gulimall.product.vo.front.SkuItemVo$SkuItemSaleAttrVo"> <result property="attrId" column="attr_id" /> <result property="attrName" column="attr_name" /> <collection property="attrValues" ofType="com.atguigu.gulimall.product.vo.front.SkuItemVo$AttrValueWithSkuIdVo"> <result property="skuIds" column="sku_ids" /> <result property="attrValue" column="attr_value" /> </collection> </resultMap> <select id="getAttrBySpuId" resultMap="skuItemSaleAttrVo"> select sav.attr_id, sav.attr_name, sav.attr_value, group_concat(distinct si.sku_id) sku_ids from gulimall_pms.pms_sku_info si left join gulimall_pms.pms_sku_sale_attr_value sav on sav.sku_id = si.sku_id where spu_id = #{spuId} group by sav.attr_id, sav.attr_name, sav.attr_value </select> </mapper>
|
这里最好起别名,要不然可能会封装不到
前端
自己看
异步编排优化
外部配置
就是可以在配置文件中配置如下的属性
1 2 3 4 5 6 7 8 9
| @ConfigurationProperties(prefix = "gulimall.thread") @Component @Data public class ThreadPoolConfigProperties { private Integer coreSize; private Integer maxSize; private Integer keepAliveTime; }
|
线程池配置
1 2 3 4 5 6 7 8 9 10 11
| @Configuration
public class MyThreadConfig { @Bean public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties pool) { return new ThreadPoolExecutor( pool.getCoreSize(), pool.getMaxSize(), pool.getKeepAliveTime(), TimeUnit.SECONDS, new LinkedBlockingQueue<>(100000), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()); } }
|
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| @Override public SkuItemVo getItemInfo(Long skuId) throws ExecutionException, InterruptedException { SkuItemVo skuItemVo = new SkuItemVo();
CompletableFuture<SkuInfoEntity> infoFuture = CompletableFuture.supplyAsync(() -> { SkuInfoEntity skuInfoEntity = getById(skuId); skuItemVo.setInfo(skuInfoEntity); return skuInfoEntity; }, executor);
CompletableFuture<Void> saleAttrFuture = infoFuture.thenAcceptAsync((skuInfoEntity) -> { List<SkuItemVo.SkuItemSaleAttrVo> skuItemSaleAttrVoList = skuSaleAttrValueService.getAttrBySpuId(skuInfoEntity.getSpuId()); System.out.println(JSON.toJSONString(skuItemSaleAttrVoList)); skuItemVo.setSaleAttr(skuItemSaleAttrVoList); }, executor);
CompletableFuture<Void> descFuture = infoFuture.thenAcceptAsync((skuInfoEntity) -> { SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(skuInfoEntity.getSpuId()); skuItemVo.setDesp(spuInfoDescEntity); }, executor);
CompletableFuture<Void> groupAttrFuture = infoFuture.thenAcceptAsync((skuInfoEntity) -> { List<SkuItemVo.SpuItemAttrGroupVo> spuItemAttrGroupVoList = attrGroupService.getGroupWithAttrForSkuItem(skuInfoEntity.getSpuId(), skuInfoEntity.getCatalogId()); skuItemVo.setGroupAttrs(spuItemAttrGroupVoList); }, executor);
CompletableFuture<Void> imageFuture = CompletableFuture.runAsync(() -> { List<SkuImagesEntity> skuImagesEntityList = skuImagesService.getBySkuId(skuId); skuItemVo.setImages(skuImagesEntityList); }, executor);
CompletableFuture.allOf(saleAttrFuture, descFuture, groupAttrFuture, imageFuture).get(); System.out.println("商品详情结果:" + JSON.toJSONString(skuItemVo)); return skuItemVo; }
|