项目细节 2

配置search.gulimall.com
在这里插入图片描述
网关处

        - id: gulimall_search_route
          uri: lb://gulimall-search
          predicates:
            - Host=search.gulimall.com

CompletableFuture 异步编排

在这里插入图片描述
1、runXxxx 都是没有返回结果的,supplyXxx 都是可以获取返回结果的

完成时回调
在这里插入图片描述

        CompletableFuture.supplyAsync(() -> {
            return 1;
            // 处理正常和异常的结果
        }, executor).whenComplete((res, ex) -> {
            System.out.println(res + "----》" + ex);
            //感知异常,同时返回默认值
        }).exceptionally((ex) -> {
            return 10;
        });

handle 方法

在这里插入图片描述
可对结果做最后的处理(可处理异常),可改变返回值。

        CompletableFuture.supplyAsync(() -> {
            return 1;
            // 处理正常和异常的结果
        }, executor).handle((res,ex)->{
            if(res!=null) {
                return res;
            }
            if(ex!=null){
                return 0;
            }
            return res+1;
        });

线程串行化
在这里插入图片描述

CompletableFuture.supplyAsync(() -> {
            return 1;
            // 无法获取到上一步的返回结果,只能执行自己的内容
        }, executor).thenRunAsync(()->{
            System.out.println("aaa");
        },executor)

CompletableFuture.supplyAsync(() -> {
            return 1;
            // 可以接收上一布的返回结果,但是无返回值
        }, executor).thenAcceptAsync((x)->{
            System.out.println(x+1);;
        },executor);

CompletableFuture.supplyAsync(() -> {
            return 1;
            // 可以接收上一步的返回结果,并且可以也可以返回结果
        }, executor).thenApplyAsync((x)->{
            return  x+1;
        },executor);

两任务组合 - 都要完成
在这里插入图片描述
runAfterBoth:组合两个 future,不需要获取 future 的结果
在这里插入图片描述
thenCombine:组合两个 future,获取两个 future 的返回结果,并返回当前任务的返回值
thenAcceptBoth:组合两个 future,获取两个 future 任务的返回结果,然后处理任务,没有 返回值

        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1开始");
            return 1;
        });
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2开始");
            return "2";
        });
        // 不能感知前面的结果
//        future1.runAfterBothAsync(future2,()->{
//            System.out.println("任务三开始");
//        },executor);

        // 可以感知前面的结果
        future1.thenAcceptBothAsync(future2,(f1,f2)->{
            System.out.println(f1+"====>"+f2);
        },executor);

        // 可以感知结果 并且可以返回结果
        future1.thenCombineAsync(future2,(f1,f2)->{
            return f1+f2;
        },executor);

两任务组合 - 一个完成
在这里插入图片描述
在这里插入图片描述

        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2开始");
            return 2;
        });
        //有一个执行完就行了,不接受参数 并且无返回值
        future1.runAfterEitherAsync(future2,()->{
            System.out.println("任务三开始");
        },executor);

        // 可以接受返回参数,但是需要相同的请求类型
        future1.acceptEitherAsync(future2,(res)->{
            System.out.println(res);
        },executor);

        // 可以感知结果 并且可以返回结果
        future1.applyToEitherAsync(future2,(f1)->{
            return f1;
        },executor);

多任务组合
在这里插入图片描述
allOf:等待所有任务完成
anyOf:只要有一个任务完成

        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1开始");
            return 1;
        });
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2开始");
            return 2;
        });

        CompletableFuture<Integer> future3 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务3开始");
            return 1;
        });
        CompletableFuture<Void> allOf = CompletableFuture.allOf(future1, future2, future3);
        Void unused = allOf.get();

        CompletableFuture<Object> anyOf = CompletableFuture.anyOf(future1, future2, future3);
        Object o = anyOf.get();

购物车

用户可以在登录状态下将商品添加到购物车【用户购物车/在线购物车】
放入数据库
放入 redis(采用)

用户可以在未登录状态下将商品添加到购物车【游客购物车/离线购物车/临时购物车】
放入 localstorage(客户端存储,后台不存)
放入 redis(采用)

数据结构

{ 	skuId: 2131241, 
	check: true,
	 title: "Apple iphone.....",
	  defaultImage: "...",
   price: 4999, count: 1, 
   totalPrice: 4999, 
   skuSaleVO: {...} }

在这里插入图片描述

redis保存的结构为 hash ,Map<String,Map<String,String>>
cat:1 # 1为用户Id
1: {“skuid”:13,“name”:“华为”} #1: 商品id
2: {“skuid”:14,“name”:“苹果”}

  • 登录的话 session 有user-key
  • 没登录: cookie的user-key
  • 第一次:创建一个临时用户
@Data
public class UserInfoTo {
    private Long userId;
    private String userKey;
    private Boolean tempUser=false;
}


@Configuration
public class GulimallWebConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/cart.html").setViewName("cartList");
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    	// 拦截所有请求
        registry.addInterceptor(new CartInterceptor()).addPathPatterns("/**");
    }
}

@Configuration
public class CartInterceptor  implements HandlerInterceptor {
	// 同一个线程共享数据 
    public static ThreadLocal<UserInfoTo> threadLocal = new ThreadLocal<>();
    /**
     * 目标方法执行之前拦截
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        UserInfoTo userInfoTo = new UserInfoTo();
        HttpSession session = request.getSession();
        // 获取当前登录用户
        Object object = session.getAttribute(AuthConstant.LOGIN_USER);

        String s = JSON.toJSONString(object);
        //将json转成需要的对象
        MemberRespTo member= JSONObject.parseObject(s,MemberRespTo.class);


        if(member!=null){
            //用户已登录  要用户Id
            userInfoTo.setUserId(member.getId());
        }

        Cookie[] cookies = request.getCookies();
        if(cookies!=null&&cookies.length>0){
            for (Cookie cookie : cookies) {
                String name = cookie.getName();
                if(CartConstant.TEMP_USER_COOKIE_NAME.equals(name)){
                // 没登录有用户Key
                   userInfoTo.setUserKey(cookie.getValue());
                    userInfoTo.setTempUser(true);
                }
            }
        }

        //如果没有临时用户,一定分配一个临时用户
        if(StringUtils.isEmpty(userInfoTo.getUserKey())){
            String uuid = UUID.randomUUID().toString();
            userInfoTo.setUserKey(uuid);
        }
        //目标方法执行之前  
        threadLocal.set(userInfoTo);
        return true;
    }

    /**
     * 业务执行之后
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        UserInfoTo userInfoTo = threadLocal.get();
        if (!userInfoTo.getTempUser()){
        	// 浏览器 保存用户user-key
            Cookie cookie = new Cookie(CartConstant.TEMP_USER_COOKIE_NAME, userInfoTo.getUserKey());
            cookie.setMaxAge(CartConstant.TEMP_USER_COOKIE_TIMEOUT);
            response.addCookie(cookie);

        }
    }
}


CartInterceptor.threadLocal.get()

添加购物车

    /**
     * 根据当前的登录状态
     * 获取到我们要操作的购物车
     *
     * @return
     */
    private BoundHashOperations<String, Object, Object> getCartOps() {
        UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
        String cartKey = "";
        if (userInfoTo.getUserId() != null) {
            //例:gulimall:cart:1
            cartKey = CART_PREFIX + userInfoTo.getUserId();
        } else {
            //临时购物车
            cartKey = CART_PREFIX + userInfoTo.getUserKey();
        }
        BoundHashOperations<String, Object, Object> operations = stringRedisTemplate.boundHashOps(cartKey);

        return operations;
    }


    @Override
    public CartItem addToCart(Long skuId, Integer number) throws ExecutionException, InterruptedException {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        //添加新商品D到购物车

        String s = (String) cartOps.get(skuId.toString());
        if (StringUtils.isEmpty(s)) {
            //购物车无此商品
            CartItem cartItem = new CartItem();
            CompletableFuture<Void> getSkuInfo = CompletableFuture.runAsync(() -> {
                //1.查询当前skuId的商品信息
                R info = productFeignService.info(skuId);
                SkuInfoVo data = info.getData("skuInfo", new TypeReference<SkuInfoVo>() {
                });
                cartItem.setCheck(true);
                cartItem.setCount(number);
                cartItem.setImage(data.getSkuDefaultImg());
                cartItem.setTitle(data.getSkuTitle());
                cartItem.setSkuId(skuId);
                cartItem.setPrice(data.getPrice());
            }, executor);


            //2.远程调用查询sku的组合信息
            CompletableFuture<Void> getSkuAttr = CompletableFuture.runAsync(() -> {
                List<String> skuSaleAttrValues = productFeignService.getSkuSaleAttrValues(skuId);
                cartItem.setSkuAttr(skuSaleAttrValues);
            }, executor);


            CompletableFuture.allOf(getSkuAttr, getSkuInfo).get();
            cartOps.put(skuId.toString(), JSON.toJSONString(cartItem));

            return cartItem;

        } else {
            //购物车有商品
            CartItem cartItem;
            cartItem = JSON.parseObject(s, CartItem.class);
            cartItem.setCount(cartItem.getCount() + number);
            cartOps.put(skuId.toString(), JSON.toJSONString(cartItem));
            return cartItem;
        }

    }
    /**
     * 查询商品  跳转到success.html
     *
     * @return
     */
	//为了防止表单重复提交, 添加成功就重定向到其他页面
    @GetMapping("/addToCartSuccess.html")
    public String addToCartSuccess(@RequestParam("skuId") Long skuId,Model model) {
        CartItem cartItem = cartService.getCartItem(skuId);
        model.addAttribute("item",cartItem);
        return "success";
    }

    /**
     * 根据skuId查出单个CartItem
     * @param skuId
     * @return
     */
    @Override
    public CartItem getCartItem(Long skuId) {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        String s = (String) cartOps.get(skuId.toString());
        CartItem cartItem;
        cartItem = JSON.parseObject(s, CartItem.class);
        return cartItem;
    }

rabbitMQ

  • 如果消息发送的是对象,就一定要实现序列化
// 开启mq
@EnableRabbit

	//配置MQ自定义消息转换器
    @Bean
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }
	//监听的队列
    @RabbitListener(queues = "{hello-java-queue}")
    // Object=>  org.springframework.amqp.core.Message
    //Channel 传输数据的通道
    // 服务启动多个:同一个消息 只有一个客户端能收到
    // 当一个消息处理完才能接受下一个消息
    public void receiveMessage(Object message, OrderReturnReasonEntity reasonEntity,Channel channel){
        System.out.println("接收到消息"+message+"内容=>"+reasonEntity);
    }

@RabbitListener:类+方法上
@RabbitHandler:方法上(重载不同的消息)

// 发送端 发送了A 和 B 往 hello-java-queue队列 两种类型的消息
// 接收端  这样就能自动获取不同类型的消息
@Service
@RabbitListener(queues = "hello-java-queue")
public class OrderItemServiceImpl{
    @RabbitHandler
    public void receiveMessage(A a){
        System.out.println("接收到消息"+a);
    }
    @RabbitHandler
    public void receiveMessage(B b){
        System.out.println("接收到消息"+b);
    }
}

可靠消息

在这里插入图片描述
可靠发送

// 发送端确认
spring.rabbitmq.publisher-confirms=true

//消息抵达队列
spring.rabbitmq.publisher-returns=true
// 消息抵达队列就异步回调returnconfirm
spring.rabbitmq.template.mandatory=true


@Configuration
public class MyRabbitConfig {
    @Autowired
    RabbitTemplate rabbitTemplate;

 @PostConstruct  //MyRabbitConfig 创建完成以后 执行这个方法
 public void initRabbitTemplate(){
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             *
             * @param correlationData 当前消息的唯一关联数据(唯一ID)
             * @param ack
             * @param cause 失败的原因
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {

            }
        });

        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
             * 只要消息没有投递和指定的队列,就会触发失败回调
             * @param message 投递失败的消息的详细信息
             * @param replyCode 回复的状态码
             * @param replyText 回复的文本内容
             * @param exchange
             * @param routingKey
             */
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {

            }
        });
    }

}

消费端确认

在这里插入图片描述

/**消费端确认(保证每个消息被正确消费,此时broker删除这个消息)
*	1.默认自动确认,只要消息接受到,服务端就会移除这个消息
*		问题:收到很多消息,只处理了一个消息 就宕机了。发生消息丢失:需要手动确认
*		spring.rabbitmq.listener.simple.acknowledge-mode=manual
*		auto:自动模式,manual:手动ack
*	2.  channel.basicAck(deliveryTag,false);签收
*		channel.basicNack(deliveryTag,false,false); 拒绝签收,业务失败
*	    @RabbitListener(queues = "hello-java-queue")
    public void receiveMessage(Message message, OrderReturnReasonEntity reasonEntity, Channel channel)  {
        System.out.println("接收到消息"+message+"内容=>"+reasonEntity);
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        // 签收货物,非批量模式
        try {
            channel.basicAck(deliveryTag,false);
        } catch (IOException e) {
            // 网络中断
            // long deliveryTag, boolean multiple, boolean requeue
            //                          批量删除      requeue:true 返回服务器,false直接丢弃
            try {
                channel.basicNack(deliveryTag,false,false);
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
            e.printStackTrace();
        }
    }
*/

Spring Session

session不同服务器不共享
同域名下 不共享的问题

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

//获取redis过期时间,时间如果大于指定的值,就提示错误
//可以防止短时间内多次发送短信
stringRedisTemplate.opsForValue().getOperations().getExpire("a");

使用spring session做session共享

  <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
@Configuration
public class GulimallSessionConfig {
    @Bean
    public CookieSerializer cookieSerializer(){
        DefaultCookieSerializer cookieSerialize = new DefaultCookieSerializer();
        cookieSerialize.setDomainName("gulimall.com");
        cookieSerialize.setCookieName("GULISESSION");

        return cookieSerialize;
    }

    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer(){
        return new  Jackson2JsonRedisSerializer<>(Object.class);
    }
}
spring.session.store-type=redis
server.servlet.session.timeout=30m

@EnableRedisHttpSession

feign丢失请求头

在这里插入图片描述

@Configuration
public class GuliFeignConfig {

    @Bean("requestInterceptor")
    public RequestInterceptor requestInterceptor() {
        return new RequestInterceptor() {
            @Override
            public void apply(RequestTemplate requestTemplate) {
                ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                    HttpServletRequest request = attributes.getRequest();
                    if (request != null) {
                        String cookie = request.getHeader("Cookie");
                        requestTemplate.header("Cookie", cookie);
                    }
            }
        };
    }
}

在这里插入图片描述

        //取出当前线程的数据
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

        CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {
            //1.
            //在其他线程中放入自己的数据
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<MemberAddressVo> address = memberFeignService.getAddress(memberRespTo.getId());
            confirmVo.setAddress(address);
        }, executor);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值