远程调用

在前面,我们拆分出了商品服务(hm-goods-service)和购物车服务(hm-cart-service)。但是,现在我们遇到了一个问题,那就是购物车服务需要查询商品的信息。在单体架构中,我们可以在购物车中直接使用 GoodsService,但是随着服务被拆分,我们没办法这样做了,因为商品和购物车是两个独立的工程。那有没有什么办法可以实现我们的需求呢?其实,最简单的办法就是发送 Http 请求。在购物车中,我们可以调用商品相关的接口查询商品信息。

单体架构

  • CartServiceImpl
@Service
@RequiredArgsConstructor
public class CartServiceImpl extends ServiceImpl<CartMapper, Cart> implements CartService {

    /**
     * 商品服务的 service
     */
    private final GoodsService goodsService;

    /**
     * 查询购物车
     */
    @Override
    public List<CartVO> queryMyCarts() {
        // 1.查询我的购物车列表
        List<Cart> carts = lambdaQuery().eq(Cart::getUserId, UserContext.getUserId()).list();
        if (CollUtils.isEmpty(carts)) {
            return CollUtils.emptyList();
        }

        // 2.转换 VO
        List<CartVO> vos = BeanUtils.copyList(carts, CartVO.class);

        // 3.处理 VO 中的商品信息
        handleCartGoods(vos);

        // 4.返回
        return vos;
    }

    private void handleCartGoods(List<CartVO> vos) {
        // 1.获取商品 id
        Set<Long> goodsIds = vos.stream().map(CartVO::getGoodsId).collect(Collectors.toSet());
        // 2.查询商品
        List<GoodsDTO> goodsList = goodsService.queryGoodsByIds(goodsIds);
        if (CollUtils.isEmpty(goodsList)) {
            return;
        }
        // 3.转为 id 到 goods 的 map
        Map<Long, GoodsDTO> goodsMap = goodsList.stream().collect(Collectors.toMap(GoodsDTO::getId, Function.identity()));
        // 4.写入 vo
        for (CartVO v : vos) {
            GoodsDTO goodsDTO = goodsMap.get(v.getGoodsId());
            if (goodsDTO == null) {
                continue;
            }
            v.setNewPrice(goodsDTO.getPrice());
            v.setStatus(goodsDTO.getStatus());
            v.setStock(goodsDTO.getStock());
        }
    }
}

Http 请求

  • RestTemplateConfig
@Configuration(proxyBeanMethods = false)
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
  • CartServiceImpl
@Service
@RequiredArgsConstructor
public class CartServiceImpl extends ServiceImpl<CartMapper, Cart> implements CartService {

    /**
     * 注入 RestTemplate
     */
    private final RestTemplate restTemplate;

    /**
     * 查询购物车
     */
    @Override
    public List<CartVO> queryMyCarts() {
        // 1. 查询我的购物车列表
        List<Cart> carts = lambdaQuery().eq(Cart::getUserId, UserContext.getUserId()).list();
        if (CollUtils.isEmpty(carts)) {
            return CollUtils.emptyList();
        }

        // 2. 转换 VO
        List<CartVO> vos = BeanUtils.copyList(carts, CartVO.class);

        // 3. 处理 VO 中的商品信息
        handleCartGoods(vos);

        // 4. 返回
        return vos;
    }

    private void handleCartGoods(List<CartVO> vos) {
        // 1. 获取商品 id
        Set<Long> goodsIds = vos.stream().map(CartVO::getGoodsId).collect(Collectors.toSet());

        // 发送 Http 请求
        ResponseEntity<List<GoodsDTO>> response = restTemplate.exchange(
                "http://localhost:8081/goods?ids={ids}",
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<>() {
                },
                Map.of("ids", CollUtils.join(goodsIds, ","))
        );

        if (!response.getStatusCode().is2xxSuccessful()) {
            return;
        }

        // 2. 查询商品
        List<GoodsDTO> goodsList = response.getBody();

        if (CollUtils.isEmpty(goodsList)) {
            return;
        }
        // 3. 转为 id 到 goods 的 map
        Map<Long, GoodsDTO> goodsMap = goodsList.stream().collect(Collectors.toMap(GoodsDTO::getId, Function.identity()));
        // 4. 写入 vo
        for (CartVO v : vos) {
            GoodsDTO goodsDTO = goodsMap.get(v.getGoodsId());
            if (goodsDTO == null) {
                continue;
            }
            v.setNewPrice(goodsDTO.getPrice());
            v.setStatus(goodsDTO.getStatus());
            v.setStock(goodsDTO.getStock());
        }
    }
}







 



























 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


















上面这种方式虽然可以实现功能,但是,还存在一个非常明显的问题。那就是,购物车服务调用商品服务时,需要知道商品服务的 IP 和 端口。但是,在微服务中,一般情况下,某个服务的 IP 和端口不是固定的,随时都可能发生变动。针对这种情况,我们可以使用负载均衡技术,把商品服务使用 Nginx 进行代理,这样,我们只需要知道 Nginx 的地址就行了,这也是一种解决办法。当然,这种方式也存在弊端,就是无法感知到商品服务的变化,例如商品服务某个实例挂掉了或者新增了实例。这就是微服务架构中的服务治理问题,需要使用注册中心