欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 前端技术 > javascript >内容正文

javascript

Spring Cloud【Finchley】实战-03订单微服务与商品微服务之间的调用

发布时间:2025/3/21 javascript 55 豆豆
生活随笔 收集整理的这篇文章主要介绍了 Spring Cloud【Finchley】实战-03订单微服务与商品微服务之间的调用 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

文章目录

  • Spring Cloud【Finchley】专栏
  • 概述
  • HTTP方式之RestTemplate
    • 方式一 (直接使用restTemplate访问URL,url写死)
    • 方式二 (使用LoadBalancerClient通过应用名获取url,拼装请求地址,然后再使用restTemplate)
    • 方式三 (使用@LoadBalanced注解)
  • Fegin 的使用
  • 商品微服务获取商品列表功能开发
    • Product微服务查询商品列表功能开发
      • DAO层
      • Service层
      • Controller层
    • Order微服务调用接口查询商品列表
      • 增加接口方法
  • 调用商品微服务扣库存功能开发
    • Product微服务减库存功能开发
      • 减库存的参数 DTO封装
      • Service
      • Controller层
    • Order微服务调用接口扣减库存
      • 增加接口方法
  • 整合
    • 测试
  • 知识点小结
    • 点1
    • 点2
  • Github

Spring Cloud【Finchley】专栏

如果还没有系统的学过Spring Cloud ,先到我的专栏去逛逛吧

Spring Cloud 【Finchley】手札


概述

还记得上篇博文的TODO吧

这里我们先循序渐进的了解下,微服务之间调用的几种方式

先了解下应用之间的通行的主要两种方式

  • RPC – 代表 Dubbo (可以基于TCP协议,也可以基于HTTP协议)
  • HTTP —代表 Spring Cloud (基于HTTP协议)

HTTP方式之RestTemplate

我们在order微服务调用product微服务。

product作为服务端,先对外暴露个测试接口

order作为客户端调用该接口

方式一 (直接使用restTemplate访问URL,url写死)

访问 http://localhost:8081/order/getServerInfoFromClient

写死的地址,并且只能请求一个,如果有多个地址就比较麻烦了。而且还是IP地址。


方式二 (使用LoadBalancerClient通过应用名获取url,拼装请求地址,然后再使用restTemplate)

package com.artisan.order.controller;import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate;@RestController @Slf4j @RequestMapping("/order") public class ClientController {@Autowiredprivate LoadBalancerClient loadBalancerClient;@GetMapping("/getServerInfoFromClient")public String requestServer(){// 方式二 (使用LoadBalancerClient通过应用名获取url,拼装请求地址,然后再使用restTemplate)RestTemplate restTemplate2 = new RestTemplate();ServiceInstance serviceInstance = loadBalancerClient.choose("ARTISAN-PRODUCT");// 获取ip port 组装urlString url = String.format("http://%s:%s",serviceInstance.getHost(),serviceInstance.getPort() + "/product/serverMsg");log.info("url:{}",url);String msg = restTemplate2.getForObject(url,String.class);log.info("msg from server : {}", msg);return msg;} }

loadBalancerClient.choose("ARTISAN-PRODUCT"); 通过loadBalancerClient 选择 注册到Eurek Server上的ip . 需要填写注册到注册中心的名字ARTISAN-PRODUCT。

访问 http://localhost:8081/order/getServerInfoFromClient


方式三 (使用@LoadBalanced注解)

先初始化RestTemplate , 标注 @LoadBalanced 注解

package com.artisan.order.config;import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate;@Configuration public class RestTemplateConfig {@Bean@LoadBalancedpublic RestTemplate restTemplate(){return new RestTemplate();} } package com.artisan.order.controller;import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate;@RestController @Slf4j @RequestMapping("/order") public class ClientController {@Autowiredprivate RestTemplate restTemplate;@GetMapping("/getServerInfoFromClient")public String requestServer(){// 方式三 (使用@LoadBalanced注解)String msg = restTemplate.getForObject("http://ARTISAN-PRODUCT/product/serverMsg",String.class);log.info("msg from server : {}", msg);return msg;} }

请求的地址 http://ARTISAN-PRODUCT/product/serverMsg 注册到服务中心上的服务

访问 http://localhost:8081/order/getServerInfoFromClient


Fegin 的使用

Spring Cloud【Finchley】-06服务消费者整合Feign

总体来说,在作为客户端的order微服务中, 步骤如下

  • 添加依赖
  • 添加注解@EnableFeignClients
  • 开发接口
  • 使用
  • pom.xml 添加依赖


    添加注解@EnableFeignClients


    编写client接口

    package com.artisan.order.client;import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping;// name为注册在注册中心上的名称 @FeignClient(name="ARTISAN-PRODUCT") public interface ProductClient {// ARTISAN-PRODUCT微服务接口的访问路径@GetMapping("/product/serverMsg")String getServerInfo(); }

    调用

    package com.artisan.order.controller;import com.artisan.order.client.ProductClient; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;@RestController @Slf4j @RequestMapping("/order") public class FeginClientController {@Autowiredprivate ProductClient productClient;@GetMapping("/getServerInfoByFeign")public String requestServer(){String msg = productClient.getServerInfo();log.info("msg from server : {}", msg);return msg;} }

    访问 http://localhost:8081/order/getServerInfoByFeign

    OK。


    商品微服务获取商品列表功能开发

    熟悉了基本使用后,刚开始说了,我们有几个TODO要做,那开始吧


    Product微服务查询商品列表功能开发

    我们看下前台会传递什么给我们

    关于商品的信息,productId是个集合,那么我们就需要提供一个根据传入的productId列表来返回product集合的功能。

    DAO层

    老规矩,DAO层先


    单元测试下,

    @Testpublic void findByProductIdIn() {List<Product> list = productRepository.findByProductIdIn(Arrays.asList("1","2"));Assert.assertEquals(2,list.size());}

    结合库表中的数据


    单元测试通过


    Service层

    紧接着Service层

    实现类

    单元测试


    单元测试通过


    Controller层

    /*** 根据productIdList 查询商品列表* 提供给Order微服务用* @param productIdList* @return*/@PostMapping("/productListForOrder")private List<Product> getProductForOrder(@RequestBody List<String> productIdList){return productService.getProductList(productIdList);}

    Order微服务调用接口查询商品列表

    增加接口方法

    返回的类型是个Product集合,我们先从product微服务那边将Product copy一份过来。 后续会优化这些地方

    我们写个方法来测试下这个功能, 那就在刚才的用作测试的FeginClientController类中写个方法吧

    当参数中标注了@RequestBody , 则必须使用POST方法

    启动服务,测试下 http://localhost:8081/order/getProductList


    可见功能是OK的。


    调用商品微服务扣库存功能开发

    Product微服务减库存功能开发

    减库存的参数 DTO封装

    我们看下前台会传递什么给我们

    肯定是 某个产品 扣除多少个数量。 []可以传递多个,对于后台来讲是个集合 。

    Product微服务需要两个参数 productId 和 productQuantity


    Service

    分析下,扣减库存,直接使用JPA内置的方法即可,DAO层可以省略了。

    直接来Service吧 ,直接写实现类中的方法你把

    @Override// 因为是对List操作,所以加个事务控制@Transactionalpublic void decreaseProduct(List<CartDTO> cartDTOList) {// 遍历CartDTOfor (CartDTO cartDTO : cartDTOList) {// 根据productId查询ProductOptional<Product> productOptional = productRepository.findById(cartDTO.getProductId());// 商品是否存在if (!productOptional.isPresent()) {throw new ProductException(ResultEnum.PRODUCT_NOT_EXIST);}// 是否库存充足Product product = productOptional.get();int leftStock = product.getProductStock() - cartDTO.getProductQuantity();if (leftStock < 0 ){throw new ProductException(ResultEnum.PRODUCT_STOCK_ERROR);}// 将剩余库存设置到product,并更新数据库product.setProductStock(leftStock);productRepository.save(product);}}

    因为是对List操作,所以加个事务控制 @Transactional

    单元测试

    @Testpublic void decreaseProductTest() {CartDTO cartDTO = new CartDTO();cartDTO.setProductId("3");cartDTO.setProductQuantity(2);productService.decreaseProduct(Arrays.asList(cartDTO));}

    测试前数据


    Controller层


    Order微服务调用接口扣减库存

    增加接口方法

    ProductClient接口新增方法

    测试下 ,在 FeginClientController 新增个方法 (这个Controller和工程无关哈,仅仅是用来测试用的)

    访问 http://localhost:8081/order/decreseProduct


    整合

    Product微服务要提供的功能及Order微服务调用都开发完了,那整合到业务逻辑中吧

    @Overridepublic OrderDTO createOrder(OrderDTO orderDTO) {String orderId = KeyUtil.genUniqueKey();// 查询商品信息(调用商品微服务)List<String> productIdList = orderDTO.getOrderDetailList().stream().map(OrderDetail::getProductId).collect(Collectors.toList());List<Product> productList = productClient.getProductForOrder(productIdList);// 计算订单总价BigDecimal orderAmout = new BigDecimal(BigInteger.ZERO);for (OrderDetail orderDetail: orderDTO.getOrderDetailList()) {for (Product product: productList) {if (product.getProductId().equals(orderDetail.getProductId())) {//单价*数量orderAmout = product.getProductPrice().multiply(new BigDecimal(orderDetail.getProductQuantity())).add(orderAmout);BeanUtils.copyProperties(product, orderDetail);orderDetail.setOrderId(orderId);orderDetail.setDetailId(KeyUtil.genUniqueKey());//订单详情入库orderDetailRepository.save(orderDetail);}}}// 扣减库存(调用商品微服务)List<CartDTO> cartDTOList = orderDTO.getOrderDetailList().stream().map(e -> new CartDTO(e.getProductId(), e.getProductQuantity())).collect(Collectors.toList());productClient.decreseProduct(cartDTOList);//订单入库Order order = new Order();orderDTO.setOrderId(orderId);// 复制属性BeanUtils.copyProperties(orderDTO, order);// 设置其他属性order.setOrderAmount(orderAmout);order.setOrderStatus(OrderStatusEnum.NEW.getCode());order.setPayStatus(PayStatusEnum.WAIT.getCode());orderRepository.save(order);return orderDTO;}

    测试

    [{"productId": "1","productQuantity": 2 }, {"productId": "2","productQuantity": 5 }, {"productId": "3","productQuantity": 10 }]

    买 1号商品 2个 ,金额 20.99乘以2 = 41.98
    买 2号商品 5个 ,金额 7.5乘以5 = 37.5
    买 3号商品 10个 ,金额 15乘以10 = 150
    总金额 229.48

    原始库存:

    使用POSTMAN测试一把

    检查下总金额,库存扣减,及order_detail中的数据

    artisan_order

    order_detail 3条记录

    库存:

    OK


    知识点小结

    点1

    Spring MVC在接收集合请求参数时,需要在Controller方法的集合参数里前添加@RequestBody

    List<Product> getProductForOrder(@RequestBody List<String> productIdList)

    点2

    当参数中标注了@RequestBody , 则必须使用POST方法


    Github

    artisan-product: https://github.com/yangshangwei/springcloud-o2o/tree/master/artisan_order

    artisan_order: https://github.com/yangshangwei/springcloud-o2o/tree/master/artisan-product

    总结

    以上是生活随笔为你收集整理的Spring Cloud【Finchley】实战-03订单微服务与商品微服务之间的调用的全部内容,希望文章能够帮你解决所遇到的问题。

    如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。