# 微服务 **Repository Path**: irving2020/microservices ## Basic Information - **Project Name**: 微服务 - **Description**: 第五阶段微服务学习用 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2023-12-29 - **Last Updated**: 2024-11-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # **_微服务商城项目_** ## **父项目** 每次新建一个子项目都要进行父子相认 ![img.png](img.png) 修改pom文件 注意parent标签中 version版本要更改(idea2023并没有java8的选项 要更改服务器为阿里云的) ![img_1.png](img_1.png) ### 子项目 ## stock模块 创建子项目模块,在模块的pom中修改parent标签为父项目的这一部分 ```xml cn.tedu csmall 0.0.1-SNAPSHOT ``` ![img_2.png](img_2.png) 到这里父子相认就完成了 ## 2、父项目添加依赖 ### 又称锁版本 定义依赖版本的变量,父pom中指定版本,子pom中就不需要指定版本了 除非有额外的版本需要,才在子项目中添加版本信息 ```xml 1.8 2.2.2 ``` 添加完毕之后开始添加依赖,在version标签中添加之前定义的变量${*}的格式来使用变量 ```xml org.mybatis.spring.boot mybatis-spring-boot-starter ${mybats.version} ``` 子项目中不需要添加版本信息,直接添加所需要的依赖就可以自动匹配父项目的版本 ![img_3.png](img_3.png) 添加依赖之后可能会出现找不到依赖包的报错,这种时候可以尝试使用下面的方法 就是把报错的dependency标签赋值到dependencyManagement外面的dependencies(就是跟我的lombok同级,并不是在原来的依赖管理中) 然后刷新maven就可以下载了 ![img.png](img5.png) # commons模块 maven搞定之后就新建一个模块为commons ```xml com.github.xiaoymin knife4j-spring-boot-starter org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-json org.springframework.boot spring-boot-starter-tomcat ``` 改完pom之后删除启动类 resources包和test包 exclusions标签的作用是保留注解,排除启动的依赖 因为commons包不需要启动,是被其他模块调用的 ```xml org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-json org.springframework.boot spring-boot-starter-tomcat ``` 创建一个包 在commons包下,创建一个pojo包,在pojo包下创建一个cart,在cart下创建一个dto包 ,dto包下创建一个CartAddDto实体类 **Dto**是前端传给后端的参数,**Vo**是前端返回给后端的参数,**entity/model/domain/bean/pojo/其他**的实体类为真正的实体类 持久层**mapper/dao/repository** 业务逻辑层较为统一**service/biz**(非常老) 控制层**controller/servlet** 与数据库字段一一对应 ## 购物车实体类 ```java @Data @ApiModel("购物车添加DTO") public class CartAddDto implements Serializable { @ApiModelProperty(value = "商品编号",name = "commodityCode",example = "PC100") private String commodityCode; @ApiModelProperty(value = "商品价格",name = "price",example = "20") private Integer price; @ApiModelProperty(value = "商品数量",name = "count",example = "2") private Integer count; @ApiModelProperty(value = "用户id",name = "userId",example = "UU100") private String userId; } ``` ```java @Data @ApiModel("购物车实体类") public class Cart implements Serializable { @ApiModelProperty(value = "购物车id",name = "id",example = "1") private Integer id; @ApiModelProperty(value = "商品编号",name = "commodityCode",example = "PC100") private String commodityCode; @ApiModelProperty(value = "商品价格",name = "price",example = "20") private Integer price; @ApiModelProperty(value = "商品数量",name = "count",example = "2") private Integer count; @ApiModelProperty(value = "用户id",name = "userId",example = "UU100") private String userId; } ``` ## 订单类 ```java @ApiModel(value = "新增订单的DTO") @Data public class OrderAddDTO implements Serializable { @ApiModelProperty(value = "用户id",name="userId",example = "UU100") private String userId; @ApiModelProperty(value = "商品编号",name="commodityCode",example = "PC100") private String commodityCode; @ApiModelProperty(value = "购买数量",name="count",example = "5") private Integer count; @ApiModelProperty(value = "总金额",name="money",example = "500") private Integer money; } ``` ```java @Data @ApiModel(value = "订单实体类") public class Order implements Serializable { private Integer id; @ApiModelProperty(value = "用户id",name="userId",example = "UU100") private String userId; @ApiModelProperty(value = "商品编号",name="commodityCode",example = "PC100") private String commodityCode; @ApiModelProperty(value = "购买数量",name="count",example = "5") private Integer count; @ApiModelProperty(value = "总金额",name="money",example = "500") private Integer money; } ``` ## 库存类 ```java @ApiModel(value = "商品减库存DTO") @Data public class StockReduceCountDTO implements Serializable { @ApiModelProperty(value = "商品编号",name="commodityCode",example = "PC100") private String commodityCode; @ApiModelProperty(value = "减库存数",name="reduceCount",example = "5") private Integer reduceCount; } ``` ```java @Data @ApiModel(value = "商品库存") public class Stock implements Serializable { @ApiModelProperty(value = "库存Id",name="commodityCode",example = "11") private Integer id; @ApiModelProperty(value = "商品编号",name="commodityCode",example = "PC100") private String commodityCode; @ApiModelProperty(value = "减库存数",name="reduceCount",example = "5") private Integer reduceCount; } ``` ## 异常处理类 ```java /** * 错误代码枚举类型 */ public enum ResponseCode { OK(200), BAD_REQUEST(400), UNAUTHORIZED(401), FORBIDDEN(403), NOT_FOUND(404), NOT_ACCEPTABLE(406), CONFLICT(409), INTERNAL_SERVER_ERROR(500); private Integer value; ResponseCode(Integer value) { this.value = value; } public Integer getValue() { return value; } } ``` ## 关于枚举 异常处理类使用的就是enum枚举, 这是一个相对特殊的类, 一般情况下用来表示一组常量, 对于一些固定值并且数量有限的情况, 可以使用枚举。 ## 业务异常类 ```java /** * 业务异常 */ @Data @EqualsAndHashCode(callSuper = false) public class CoolSharkServiceException extends RuntimeException { private ResponseCode responseCode; public CoolSharkServiceException(ResponseCode responseCode, String message) { super(message); setResponseCode(responseCode); } } ``` super用法,子调父的私有属性不可直接调用,可以通过super来调用父级的私有属性 # business模块 还是删除test测试类 添加配置[application.yml](csmall-business%2Fsrc%2Fmain%2Fresources%2Fapplication.yml) ```yaml server: port: 20000 #公共配置 mybatis: configuration: cache-enabled: false # 不启用mybatis缓存 map-underscore-to-camel-case: true # 映射支持驼峰命名法 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 将运行的sql输出到控制台 knife4j: # 开启增强配置 enable: true # 生产环境屏蔽,开启将禁止访问在线API文档 production: false # Basic认证功能,即是否需要通过用户名、密码验证后才可以访问在线API文档 basic: # 是否开启Basic认证 enable: false # 用户名,如果开启Basic认证却未配置用户名与密码,默认是:admin/123321 username: root # 密码 password: root spring: profiles: active: dev ``` server.port:端口号20000,mybatis关闭二级缓存,开启驼峰命名法,把运行的sql输出到控制台 knife4j: 在线api测试文档 spring.profiles.active:表示在这个yml文件加载之后还要在加载一个后缀为dev的yml文件 ## 配置类 ```java //当前类是配置Spring扫描环境的配置类,必须添加此注解 @Configuration //扫描全局异常处理类的包,使其生效 @ComponentScan("cn.tedu.csmall.commons.exception") public class CommonsConfiguration { } ``` ```java /** * Knife4j(Swagger2)的配置 */ @Configuration @EnableSwagger2WebMvc public class Knife4jConfiguration { /** * 【重要】指定Controller包路径 */ private String basePackage = "cn.tedu.csmall.business.controller"; /** * 分组名称 */ private String groupName = "base-business"; /** * 主机名 */ private String host = "http://java.tedu.cn"; /** * 标题 */ private String title = "酷鲨商城项目案例在线API文档--基础businessr-web实例"; /** * 简介 */ private String description = "构建基础business-web项目,实现购买"; /** * 服务条款URL */ private String termsOfServiceUrl = "http://www.apache.org/licenses/LICENSE-2.0"; /** * 联系人 */ private String contactName = "项目研发部"; /** * 联系网址 */ private String contactUrl = "http://java.tedu.cn"; /** * 联系邮箱 */ private String contactEmail = "java@tedu.cn"; /** * 版本号 */ private String version = "1.0-SNAPSHOT"; @Autowired private OpenApiExtensionResolver openApiExtensionResolver; @Bean public Docket docket() { String groupName = "1.0-SNAPSHOT"; Docket docket = new Docket(DocumentationType.SWAGGER_2) .host(host) .apiInfo(apiInfo()) .groupName(groupName) .select() .apis(RequestHandlerSelectors.basePackage(basePackage)) .paths(PathSelectors.any()) .build() .extensions(openApiExtensionResolver.buildExtensions(groupName)); return docket; } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title(title) .description(description) .termsOfServiceUrl(termsOfServiceUrl) .contact(new Contact(contactName, contactUrl, contactEmail)) .version(version) .build(); } } ``` ## service 在business模块下新建service层和impl层 ```java public interface IBusinessService { void buy(); } ``` ## impl 因为是测试模块功能,所以不连数据库没有mapper层,直接在impl层写业务逻辑 ```java @Service @Slf4j //控制套输出使用到的注解 public class BusinessServiceImpl implements IBusinessService{ @Override public void buy() { //编写新增订单雏形 // 先实例化一个新增的订单对象 OrderAddDTO orderAddDTO = new OrderAddDTO(); orderAddDTO.setUserId("UU100"); orderAddDTO.setCount(10); orderAddDTO.setMoney(199); orderAddDTO.setCommodityCode("PC100"); //这个实例化的DTO对象,需要传递给订单order模块,让订单模块去新增订单 log.info("订单模块新增订单,传递的参数是:"+orderAddDTO); } } ``` ## controller ```java @RestController @RequestMapping("/base/business") @Api(tags = "业务触发模块") public class BusinessController { @Resource private IBusinessService businessService; @PostMapping("/buy") @ApiOperation("执行业务触发的方法") public JsonResult buy(){ businessService.buy(); return JsonResult.ok("完美成功"); } } ``` 通过Knife4j测试(正确返回如下) ![img_4.png](img_4.png) 现在要开始将项目注册到nacos注册中心,现在business模块中增加依赖 ```yaml com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ``` 然后修改[application-dev.yml](csmall-business%2Fsrc%2Fmain%2Fresources%2Fapplication-dev.yml)文件 ```yaml spring: application: name: csmall-business #设置当前项目的名称,注册到nacos的名称与这个相同 cloud: nacos: discovery: server-addr: localhost:8849 #nacos服务地址 ``` 然后启动nacos再启动项目(顺序很关键) ## 心跳机制 nacos有一个心跳机制, 服务每五秒会向nacos发送一次信息交互,当我们把服务注册到nacos会发送一个心跳包, nacos会根据心跳包检查当前服务是否在nacos中存在, 如果不存在就按照新业务进行注册,否则代表当前服务是健康状态 如果一个服务连续三次心跳(默认是15秒)没有和Nacos进行信息的交互,就会把当前服务标记为不健康状态 如果连续六次心跳(默认30秒)没有和Nacos进行信息的交互,就会把当前服务从Nacos中删除 这些时间是可以通过配置修改的 ## 实例类型分类 实际上nacos的服务类型是有分类的 * 临时实例 * 持久化实例(永久实例) 持久化实例启动时像nacos注册,心跳包的规则和临时实例是一样的, 只是不会将该服务从列表中剔除,一边情况下默认是临时实例, 只有项目的主干业务才会设置为永久实例 # cart模块 新建cart模块,导入pom依赖 ```xml org.springframework.boot spring-boot-starter-web org.mybatis.spring.boot mybatis-spring-boot-starter com.alibaba druid mysql mysql-connector-java cn.tedu csmall-commons 0.0.1-SNAPSHOT com.github.xiaoymin knife4j-spring-boot-starter com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ``` 把busniess模块的配置文件复制过来 ```yaml server: port: 20001 #公共配置 mybatis: configuration: cache-enabled: false # 不启用mybatis缓存 map-underscore-to-camel-case: true # 映射支持驼峰命名法 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 将运行的sql输出到控制台 knife4j: # 开启增强配置 enable: true # 生产环境屏蔽,开启将禁止访问在线API文档 production: false # Basic认证功能,即是否需要通过用户名、密码验证后才可以访问在线API文档 basic: # 是否开启Basic认证 enable: false # 用户名,如果开启Basic认证却未配置用户名与密码,默认是:admin/123321 username: root # 密码 password: root spring: profiles: active: dev ``` ```yaml spring: application: name: csmall-cart #设置当前项目的名称,注册到nacos的名称与这个相同 cloud: nacos: discovery: server-addr: localhost:8849 #nacos服务地址 #ephemeral: false ``` 配置好了开始连接数据库,导入准备好的sql文件[csmall_db.sql](sql%2Fcsmall_db.sql),我使用的是MySQL8 ,依然是导入两个配置 ![img_5.png](img_5.png) 需要注意一点 knife4j中的扫描包名等要改为cart(如果是从busniess CV来的话) 写好配置之后就可以开始写业务逻辑了 ## Mapper ```java package cn.tedu.csmall.cart.mapper; import cn.tedu.csmall.commons.pojo.cart.entity.Cart; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; @Repository public interface CartMapper { //向购物车表中新增商品数据 @Insert("insert into cart_tbl(commodity_code,price,count,user_id) " + "values (#{commodityCode},#{price},#{count},#{userId})") int insertCart(Cart cart); //删除购物车中的商品信息 @Delete("delete from cart_tbl where user_id=#{userId} and commodity_code=#{commodityCode}") int deleteCartByUserIdAndCommodityCode(@Param("userId") String userId, @Param("commodityCode") String commodityCode); } ``` ## service ```java package cn.tedu.csmall.cart.service; import cn.tedu.csmall.commons.pojo.cart.dto.CartAddDto; import cn.tedu.csmall.commons.pojo.cart.entity.Cart; /** * @author 刘浩男 */ public interface ICartService { void cartAdd(CartAddDto cartAddDto); void deleteUserCart(String userId, String commodityCode); } ``` ## impl ```java package cn.tedu.csmall.cart.service.impl; import cn.tedu.csmall.cart.mapper.CartMapper; import cn.tedu.csmall.cart.service.ICartService; import cn.tedu.csmall.commons.pojo.cart.dto.CartAddDto; import cn.tedu.csmall.commons.pojo.cart.entity.Cart; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @author 刘浩男 */ @Service @Slf4j public class CartServiceImpl implements ICartService { @Autowired private CartMapper cartMapper; //新增购物车方法 @Override public void cartAdd(CartAddDto cartAddDto) { //当前方法是CartAddDto对象 //mapper接受的是cart对象,所以要进行转换 Cart cart = new Cart(); //利用BeanUtils。copyProperties方法进行转换 BeanUtils.copyProperties(cartAddDto, cart); // 调用mapper的方法,将cart对象插入到数据库中 int rows = cartMapper.insertCart(cart); //打印日志,可以通过rows的值来判断是否成功 log.info("新增购物车商品成功,受影响的行数为:{}", rows); } //删除购物车的方法 @Override public void deleteUserCart(String userId, String commodityCode) { // 根据用户id和商品编号删除购物车中的商品信息 int i = cartMapper.deleteCartByUserIdAndCommodityCode(userId, commodityCode); log.info("删除购物车商品成功,受影响的行数为:{}", i); } } ``` ## Controller ```java package cn.tedu.csmall.cart.controller; import cn.tedu.csmall.cart.service.ICartService; import cn.tedu.csmall.commons.pojo.cart.dto.CartAddDto; import cn.tedu.csmall.commons.restful.JsonResult; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author 刘浩男 */ @RestController @RequestMapping("/cart") public class CartController { @Autowired private ICartService cartService; @PostMapping("/add") @ApiOperation("新增商品到购物车") public JsonResult add(CartAddDto cartAddDto) { cartService.cartAdd(cartAddDto); return JsonResult.ok("新增商品到购物车成功!"); } @PostMapping("delete") @ApiOperation("删除购物车中的商品") public JsonResult delete(String userId, String commodityCode) { cartService.deleteUserCart(userId, commodityCode); return JsonResult.ok("删除购物车中的商品成功!"); } } ``` # Order模块 新建Order模块,导入依赖,父子相认,与Cart模块一样 ```xml org.springframework.boot spring-boot-starter-web org.mybatis.spring.boot mybatis-spring-boot-starter com.alibaba druid mysql mysql-connector-java cn.tedu csmall-commons 0.0.1-SNAPSHOT com.github.xiaoymin knife4j-spring-boot-starter com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ``` 编写代码,实现新增订单功能,因为订单不需要删除所以只需要写新增方法就可以 ## Mapper ```java package cn.tedu.csmall.order.mapper; import cn.tedu.csmall.commons.pojo.order.entity.Order; import org.apache.ibatis.annotations.Insert; import org.springframework.stereotype.Repository; @Repository public interface OrderMapper { @Insert("insert into order_tbl(user_id,commodity_code,count,money) " + "values(#{userId},#{commodityCode},#{count},#{money})") int insertOrder(Order order); } ``` ## Service ```java package cn.tedu.csmall.order.service; import cn.tedu.csmall.commons.pojo.order.dto.OrderAddDTO; public interface IOrderService { void OrderAdd(OrderAddDTO orderAddDTO); } ``` ## Impl ```java package cn.tedu.csmall.order.service.impl; import cn.tedu.csmall.commons.pojo.order.dto.OrderAddDTO; import cn.tedu.csmall.commons.pojo.order.entity.Order; import cn.tedu.csmall.order.mapper.OrderMapper; import cn.tedu.csmall.order.service.IOrderService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service @Slf4j public class OrderServiceImpl implements IOrderService { @Autowired private OrderMapper orderMapper; @Override public void OrderAdd(OrderAddDTO orderAddDTO) { //1.这里要先进行减少数据库中库存的操作(stock模块) //2.还要从购物车中删除用户选中的商品(cart模块) Order order = new Order(); BeanUtils.copyProperties(orderAddDTO,order); int i = orderMapper.insertOrder(order); log.info("新增订单成功,插入了:{}条",i); } } ``` ## Controller ```java package cn.tedu.csmall.order.controller; import cn.tedu.csmall.commons.pojo.order.dto.OrderAddDTO; import cn.tedu.csmall.commons.restful.JsonResult; import cn.tedu.csmall.order.service.IOrderService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/order") @Api(tags = "订单业务触发模块") public class OrderController { @Autowired private IOrderService orderService; @PostMapping("/add") @ApiOperation("新增订单") public JsonResult OrderAdd(OrderAddDTO orderAddDTO){ orderService.OrderAdd(orderAddDTO); return JsonResult.ok("新增订单成功!"); } } ``` # Stock模块 编写Stock模块,第一步添加依赖 ```xml 4.0.0 cn.tedu csmall 0.0.1-SNAPSHOT cn.tedu csmall-stock 0.0.1-SNAPSHOT csmall-stock csmall-stock org.springframework.boot spring-boot-starter-web org.mybatis.spring.boot mybatis-spring-boot-starter com.alibaba druid mysql mysql-connector-java cn.tedu csmall-commons 0.0.1-SNAPSHOT com.github.xiaoymin knife4j-spring-boot-starter com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ``` 第二步,修改配置文件 ```yaml server: port: 20003 #公共配置 mybatis: configuration: cache-enabled: false # 不启用mybatis缓存 map-underscore-to-camel-case: true # 映射支持驼峰命名法 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 将运行的sql输出到控制台 knife4j: # 开启增强配置 enable: true # 生产环境屏蔽,开启将禁止访问在线API文档 production: false # Basic认证功能,即是否需要通过用户名、密码验证后才可以访问在线API文档 basic: # 是否开启Basic认证 enable: false # 用户名,如果开启Basic认证却未配置用户名与密码,默认是:admin/123321 username: root # 密码 password: root spring: profiles: active: dev ``` ```yaml spring: application: name: nacos-stock #设置当前项目的名称,注册到nacos的名称与这个相同 cloud: nacos: discovery: server-addr: localhost:8849 #nacos服务地址 #ephemeral: false datasource: url: jdbc:mysql://localhost:3306/csmall_db?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true username: root password: root ``` ## 配置类 和Order模块与Cart模块一样 CV就可以 ## Mapper 需要注意一些问题,首先@Param在多参数时必须添加,否则会报错,减库存的逻辑与订单和购物车有所区别,首先是需要一些逻辑运算 要判断库存是否足够 ```java package cn.tedu.csmall.stock.mapper; import cn.tedu.csmall.commons.pojo.stock.entity.Stock; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import org.springframework.stereotype.Repository; @Repository public interface StockMapper { @Update("update stock_tbl set count = count - #{countNum} " + " where commodity_code = #{commodityCode} AND " + " count>=#{countNum}") int stockUpdate(@Param("commodityCode") String commodityCode, @Param("countNum") int countNum); } ``` ## Service service依旧接参为DTO ```java package cn.tedu.csmall.stock.service; import cn.tedu.csmall.commons.pojo.stock.dto.StockReduceCountDTO; public interface IStockService { void reduceStock(StockReduceCountDTO stockReduceCountDTO); } ``` ## Impl 这里要判断他是否成功,如果失败要抛出异常,如果不抛异常可能会造成程序服务停机 ```java package cn.tedu.csmall.stock.service.impl; import cn.tedu.csmall.commons.exception.CoolSharkServiceException; import cn.tedu.csmall.commons.pojo.stock.dto.StockReduceCountDTO; import cn.tedu.csmall.commons.pojo.stock.entity.Stock; import cn.tedu.csmall.commons.restful.ResponseCode; import cn.tedu.csmall.stock.mapper.StockMapper; import cn.tedu.csmall.stock.service.IStockService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service @Slf4j public class StockServiceImpl implements IStockService { @Autowired private StockMapper stockMapper; @Override public void reduceStock(StockReduceCountDTO stockReduceCountDTO) { int rows = stockMapper.stockUpdate(stockReduceCountDTO.getCommodityCode(), stockReduceCountDTO.getReduceCount()); if (rows==0){ throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST,"库存不足或商品不存在"); } log.info("库存扣减成功"); } } ``` ## Controller ```java package cn.tedu.csmall.stock.controller; import cn.tedu.csmall.commons.pojo.stock.dto.StockReduceCountDTO; import cn.tedu.csmall.stock.service.IStockService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/stock") @Api(tags = "库存业务模块") public class StockController { @Autowired private IStockService stockService; @PostMapping("/reduce") @ApiOperation(value = "扣减库存") public void reduceStock(StockReduceCountDTO stockReduceCountDTO) { stockService.reduceStock(stockReduceCountDTO); } } ``` # Dubbo ![img_6.png](img_6.png) 默认使用Dubbo协议,支持很多序列化协议,默认使用Hessian2,默认情况下支持的协议有如下特征: * 采用NIO单一长连接 * 优秀的并发性能,但是处理大型文件的能力差 Dubbo方便支持高并发和高性能 ![img_7.png](img_7.png) consumer服务的消费者,指的是服务的调用者(使用者),也是需要注册到注册中心,provider启动之后把服务都给到注册中心 consumer启动之后能够看到注册中心所有的服务,发现之后就可以根据RPC远程过程调用的方式调用服务,Dubbo中,远程调用 依据是服务的提供者在Nacos中的注册服务名称