你好,我是Qiuner. 为帮助别人少走弯路和记录自己编程学习过程而写博客
这是我的 github https://github.com/Qiuner ⭐️
gitee https://gitee.com/Qiuner 🌹
如果本篇文章帮到了你 不妨点个赞吧~ 我会很高兴的 😄 (^ ~ ^)
想看更多 那就点个关注吧 我会尽力带来有趣的内容 😎
- 这篇中规中举,有不少bug记录与方便您复制的代码,相信一定能节省学习时间同时达到更好的效果
- 本博客要与原文档搭配使用 day03-微服务01 - 飞书云文档 (feishu.cn)
- 本来这个系列博客应该五月份出完,但有各种事情。所幸都安然度过 也不敢再标题写全网最快了 只写全网最全 😎
本篇文章的重点是 作业 trade客户端拆分与相应OpenFeign编写思路,您一看就能明白 我比老师讲的更详细 看完不会找我 就是这么自信 😎
封面颜色是 槐花黄绿
2024最新SpringCloud微服务开发与实战,java黑马商城项目微服务实战开发(涵盖MybatisPlus、Docker、MQ、ES、Redis高级等)Day 3
文章目录
- 2024最新SpringCloud微服务开发与实战,java黑马商城项目微服务实战开发(涵盖MybatisPlus、Docker、MQ、ES、Redis高级等)Day 3
- win 11 VMware workstations b不可恢复的错误(vcpu-2)
- CentOS7显示异常 显示为bash-4.2#
- 数据库连接异常处理
- 项目实际生产环境与使用环境的不同设计
- 用户登录报错
- 单体架构认识
- 拆分案例 拆分商品服务
- 在微服务项目中新建一个模块时 你需要做些什么
- 拆分案例 拆分购物车服务 与IDEA自动导包设置
- 远程调用
- Spring不推荐使用@Autowired注解
- Nacos注册中心
- 服务注册
- 依赖导入报错问题 [ERROR] 'dependencies.dependency.groupId' for com.heima':hm-service:jar with value 'com.heima'' does not match a valid id pattern. @ line 33, column 22
- OpenFeign
- OpenFeign引入四步走
- OpenFeign优化
- OpenFeign使用优化
- OpenFeign日志
- 作业:trade客户端拆分思路
win 11 VMware workstations b不可恢复的错误(vcpu-2)
- 找一个新版的VM进行下载这个问题就能解决,博主之前是16pro
alias rm='rm -i' alias cp='cp -i' alias mv='mv -i' alias dps='docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"' alias dis='docker images' # Source global definitions if [ -f /etc/bashrc ]; then . /etc/bashrc fi
- 官方文档的这个是错的,写错了一部分的内容
CentOS7显示异常 显示为bash-4.2#
- 输入一行
source /root/.bashrc
- 就能实现命令符的变化
数据库连接异常处理
- 在这里 可能会看见以下报错
- 注意 您可在这一步前确认自己的mysql服务是否已启动(是否能连上本地的数据库)
解决方式
- 先看能不能ping通
- 如果能ping通而连接不了,那可能是docker服务没有启动
- 先查看全部容器,然后使用docker start mysql即可
- 再次连接发现成功连接上
项目实际生产环境与使用环境的不同设计
- 通过这种方式就可以设置启动项目的方式
- 一个未登入导致权限不足的bug,这个是正常的
- 这里老师使用黑框框启动 但其实可以不用 点击也行 以下命令会关闭所有的nginx进程
taskkill /f /t /im nginx.exe
用户登录报错
- 将JDK改为11即可
单体架构认识
项目中使用到的jmeter
- 这里要用到jmeter进行测试 推荐看博主的另一篇文章
软件测试之 性能测试 性能测试基础指标 Loadrunner、Jmeter等工具-CSDN博客
- 如果不想看 那我简单地做个介绍
- https://spring.io/projects/spring-cloud 这是SpringCloud的官网 可以在这里看到很多组件
- P40这里做的非常好 从一个商城项目的视角来切入项目 教你怎么读别人的代码
微服务拆分原则
拆分案例 拆分商品服务
下面是修改的配置文件
- 作者名我改成了Qiuner,你可以改成你自己喜欢的名称
server: port: 8081 spring: application: name: item-service profiles: active: dev datasource: url: jdbc:mysql://${hm.db.host}:3306/hm-item?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai driver-class-name: com.mysql.cj.jdbc.Driver username: root password: ${hm.db.pw} mybatis-plus: configuration: default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler global-config: db-config: update-strategy: not_null id-type: auto logging: level: com.hmall: debug pattern: dateformat: HH:mm:ss:SSS file: path: "logs/${spring.application.name}" knife4j: enable: true openapi: title: 黑马商城商品管理接口文档 description: "黑马商城商品管理接口文档" email: zhanghuyi@itcast.cn concat: Qiuner url: https://www.itcast.cn version: v1.0.0 group: default: group-name: default api-rule: package api-rule-resources: - com.hmall.item.controller
- 这里的拷贝案例直接看视频吧,用文字表达出来比较长
- 微服务01-08.微服务拆分-拆分商品服务_哔哩哔哩_bilibili
- 视频里的 服务拆分思路我认为是值得学习的,先复制domain层的东西(vo、dto)什么的
- 然后复制mapper、Service、control
- 视频里的 服务拆分思路我认为是值得学习的,先复制domain层的东西(vo、dto)什么的
- 这里服务配置完后就可以运行了 运行起来时记得访问下8081(拆分出来的子模块)端口号后面加上/doc.html 来访问接口文档
在微服务项目中新建一个模块时 你需要做些什么
启动类方面
- 修改成这样 那么启动类就配置完毕了
配置文件方面
- 这里配置文件可以看我的另一篇文章: 微服务配置文件详解 对配置文件做了个大致的介绍,通俗易懂(还没写 之后写了会放链接的
拆分案例 拆分购物车服务 与IDEA自动导包设置
- 和之前一样的 就是视频中的idea配置了自动导入包
- 可以尝试自己进行服务拆分
- 如果此处运行起来报 500 的错 先检查自己docker服务有没有启动起来 然后看自己是不是写hm-cart 像博主就写card了
远程调用
@Bean public RestTemplate restTemplate() { return new RestTemplate(); }
private void handleCartItems(List<CartVO> vos) { // TODO 1.获取商品id Set<Long> itemIds = vos.stream().map(CartVO::getItemId).collect(Collectors.toSet()); // 2.查询商品 原代码 // List<ItemDTO> items = itemService.queryItemByIds(itemIds); // 这里查询商品不再从本地数据库中查询 而是发送请求 让远程服务器接受来查询 // 使用RestTemplate发送请求 ResponseEntity<List<ItemDTO>> response= restTemplate.exchange( "http://localhost:8081/items?ids={ids}", HttpMethod.GET, null, new ParameterizedTypeReference<List<ItemDTO>>() { }, Map.of("ids",CollUtils.join(itemIds,",")) ); // 解析响应 if (!response.getStatusCode().is2xxSuccessful()){ // 查询失败 return; } //这里做转换 List<ItemDTO> items =response.getBody(); if (CollUtils.isEmpty(items)) { return; } // 3.转为 id 到 item的map Map<Long, ItemDTO> itemMap = items.stream().collect(Collectors.toMap(ItemDTO::getId, Function.identity())); // 4.写入vo for (CartVO v : vos) { ItemDTO item = itemMap.get(v.getItemId()); if (item == null) { continue; } v.setNewPrice(item.getPrice()); v.setStatus(item.getStatus()); v.setStock(item.getStock()); } }
- 要点就是看服务有没有启动、使用了什么配置文件的数据库进行运行
Spring不推荐使用@Autowired注解
- 这里有很多原因,比如依赖注入顺序,使用构造函数能保证在对象创建时已经被注入,而使用字段注入未必
- 还有对象不可变性、依赖倒装原则,减少反射等等
- 使用构造函数会出现成员变量很多构造函数需要写很多的情况
- 使用RequiredArgsConstructor 注解 加上 final来进行保证初始化
Nacos注册中心
- 面临可以有多台服务器提供服务,这个时候服务地址不知道要填什么。填一个定死,出故障了那整个业务就崩溃了
- 图中 当 8083没有进行心跳续约的时候,注册中心就会剔除掉
- 原本有三个服务提供者,8081,8082,8083
报错NMI watchdog: BUG: soft lockup - CPU#1 stuck for 22s! [containerd:1062]
- 看看你虚拟机分配的配置是不是不够,博主就是虚拟机配置不够,然后跑不起来(老师弄得8G是够的,博主没开那么大而已)
http://192.168.197.130:8848/nacos
- 将项目ip地址换成你自己的
- 看到这个页面就是部署成功了
服务注册
这里的discoveryClient是一个顶级接口,所有的服务注册中心都实现了这个接口
instances.get这里是选择负载均衡算法
依赖导入报错问题 [ERROR] ‘dependencies.dependency.groupId’ for com.heima’:hm-service:jar with value ‘com.heima’’ does not match a valid id pattern. @ line 33, column 22
- 解决方案很简单,这是没有在本地找到项目依赖,将要关联的弄到本地就可以了
- 运行 成功解决
mvn clean install
OpenFeign
- 原本想要在两个服务中调取请求需要做上面这么多事
- 现在只需要这几行代码
OpenFeign引入四步走
第一步:在pom文件中新增这些东西
<!--openFeign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!--负载均衡器--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> </dependency>
第二步:添加开关
第三步:写客户端与实际代码
第四步:使用
OpenFeign优化
- 原本没有连接池 现在使用链接池,毫无疑问,使用链接池对性能的优化是巨大的
day03-微服务01 - 飞书云文档 (feishu.cn)
OpenFeign使用优化
优化方式一:
优化方式二:
- 需要在启动类的@Endble注解上加上扫描地址
OpenFeign日志
第一步:写一个返回日志级别方法
第二步:使用这个日志级别
- 上面标志表示 成功了
作业:trade客户端拆分思路
一:分析
- 原本cart-service需要item-service的购物车中数据,所以创建了item的openfeign客户端,而trade需要商品id(item),清理购物车库存(cart)这两个的数据,所以需要这两个的客户端
二:定位代码
- 经过如上图以后的分析,就能明确到底要做什么
三:开始创建client
1 商品id获取
@FeignClient("item-service") public interface ItemClient { @GetMapping("/items") List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids); }
类部分
@FeignClient("item-service")
:定义一个 Feign 客户端,服务名称为 item-service
,这个名称应该与服务注册中心(如 Nacos)中的服务名称一致。
public interface ItemClient
:声明一个接口,表示这个接口将包含远程服务的 API 方法。
方法部分
@GetMapping("/items")
:定义一个 GET 请求,映射到 item-service
服务的 /items
端点。
List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
:
- 方法名称
queryItemByIds
表示查询多个商品 ID 的信息。 - 参数
@RequestParam("ids") Collection<Long> ids
:使用@RequestParam
注解表示这是一个查询参数,参数名称为ids
,类型是Collection<Long>
。 - 返回类型
List<ItemDTO>
:方法返回一个ItemDTO
对象的列表。
- 这里可以看出,需求是一样的,因此直接用
2 购物车清空
一:阅读原本购物车代码
发现有一个批量删除的代码,那我们就用这个了
本方法没有返回值,所以得出的client代码如下
package com.hmall.api.client; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import java.util.Collection; @FeignClient("cart-service") public interface CartClient { @GetMapping("/carts") void deleteCartItemByIds(@RequestParam("ids") Collection<Long> ids); }
cartClient.deleteCartItemByIds(itemIds);
3 扣减缓存
- 发现扣减缓存代码
3.1 最终结果
CartClient
package com.hmall.api.client; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import java.util.Collection; @FeignClient("cart-service") public interface CartClient { @GetMapping("/carts") void deleteCartItemByIds(@RequestParam("ids") Collection<Long> ids); }
ItemClient
package com.hmall.api.client; import com.hmall.api.dto.ItemDTO; import com.hmall.api.dto.OrderDetailDTO; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import java.util.Collection; import java.util.List; @FeignClient("item-service") public interface ItemClient { @GetMapping("/items") List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids); @PutMapping("/items/stock/deduct") void deductStock(@RequestBody List<OrderDetailDTO> items); }
四:非拆分配置
- 分别是扫描和,和依赖使用
- 其他都一样了,相信你能知道怎么使用