微服务基础拆分实践(第一篇)
目录
前言
一、认识微服务
1.1 单体架构 VS 微服务架构
1.2 微服务的集大成者:SpringCloud
1.3 微服务拆分原则
1.4 微服务拆分方式
二、微服务拆分入门步骤 :以拆分商品模块为例
三、服务注册订阅与远程调用:以拆分购物车为例
3.1 拆分模块、发现问题
3.2 问题分析、解决方法
3.3 实现远程调用:RestTemplate工具,实现端到端请求发送
四、微服务基础拆分总结
五、微服务基础追问巩固
前言
1. 微服务的基础入门篇,读完本文让你对微服务有一个基本的了解,与此同时通过两个微服务的模块拆分实验,发现微服务拆分面临的难题。并尝试去解决它。
2. 介绍微服务与单体架构的区别、微服务的拆分原则;解决跨微服务请求问题
一、认识微服务
我觉得既然能看到这篇笔记,想必大家对微服务都是有一定的概念的。本篇文章是笔者在学习微服务、拆分微服务、解决拆分微服务中遇到的问题总结而来。是面向实践的。
参考下面这张项目架构演变图。微服务就是随着项目规模不断扩大,为了减轻服务器的压力、维持提高项目团队的高效协作、加快项目部署打包等功能提出的技术。
它将业务代码按照业务模块的不同,划分成多个小项目或是小模块,确保了每一个模块的职责明确,且分别部署到不同的一台或多台服务器上,减少服务器压力。
1.1 单体架构 VS 微服务架构
单体架构:将业务的所有功能集中在一个项目中开发,打成一个包部署。
优点:(小规模下的优势)
- 架构简单
- 部署难度低
- 维护成本低
缺点:(大规模下的缺陷)
- 团队协作难(就好像1个人盖楼100天、100人盖楼1天、100万人盖楼1分钟 可能吗? 人越多,在一个单体中开发、维护越来越困难)
- 发布效率低 (单体打包几个G、几十个G甚至更大,浪费多少时间)
- 系统可用性差(对服务器压力大,别的业务请求量大的时候会影响其他正常的业务)
微服务架构:是服务化思想指导下的一套最佳实践架构方案。服务化,就是把单体架构中的功能模块拆分为多个独立项目。
优点:(大规模下谈、小规模都不用)
- 粒度小、发布效率高
- 团队协调效率高
- 单台服务器压力小
缺点:(大规模下谈、小规模都不用)
- 需要解决不同业务之间的服务调用问题 (涉及到不同服务器之间的通信请求)
- 需要更多的部署成本 (服务器多了、项目的部署更加讲究)
1.2 微服务的集大成者:SpringCloud
简单来说,微服务的概念很早就有了,但是家家不同。SpringCloud的出现,就像统一度量衡制定了一系列行业标准。
SpringCloud是目前国内使用最广泛的微服务框架。 SpringCloud集成了各种微服务功能组件,并基于SpringBoot实现了这些组件的自动装配,从而提供了良好的开箱即用体验。
官网学习:Spring Cloud
这张图可重要啦,对应着我们在拆分微服务项目的时候会遇到的各种问题,SpringCloud都给出了相应的解决措施。
【SpringCloud 与 SpringBoot的版本对照】
由于目前企业使用jdk8、jdk11比较多,所有本次实验采用2021版Spring Cloud进行微服务搭建
1.3 微服务拆分原则
1. 单一职责原则
每个微服务应专注于执行单一的功能或业务领域。
2. 高内聚、低耦合
把相关的行为都聚集在一起,把不相干的行为放到其他地方。这样,当他们要修改某个行为的时候,只需要在一个地方修改即可,然后就能对该修改及早地进行发布。同时也有利于降低模块与模块之间的耦合性,提高每个模块的独立性。
如果要在很多不同的地方做修改,那么就需要同时发布多个微服务才能交付这个功能。修改进度不统一、测试工作不线性、部署工作不集中。从而大大降低开发效率。
3. 独立有边界
微服务应有明确定义的边界和功能。
1.4 微服务拆分方式
纵向拆分:按业务模块进行拆分,并遵循微服务拆分原则
横向补充:对于大家都会用到的公共服务,还可以在纵向基础上补充横向拆分,抽取公共服务,提高代码复用
【目前广泛应用的两种微服务架构】
1. 拆分独立若干project,分项目管理
这种拆分适合于超大规模的团队协调开发,项目完全解耦。
-
优点:服务之间耦合度低
-
缺点:每个项目都有自己的独立仓库,管理起来比较麻烦
2. 拆分依赖于主模块的若干子模块,统一Maven管理
这种拆分相对我们学习来说比较容易,一个项目内用不同的模块代表不同的服务
-
优点:项目代码集中,管理和运维方便
-
缺点:服务之间耦合,编译时间较长
二、微服务拆分入门步骤 :以拆分商品模块为例
本次实验以黑马商城的项目拆分为例,采用拆分模块的方式来进行微服务的学习:
2.1 入门拆分微服务步骤
- 第一步:新建微服务模块
- 第二步:拷贝父工程的依赖坐标
- 第三步:搭建基本框架,编写启动类
- 第四步:拷贝配置文件,修改相关配置
- 第五步:拷贝业务代码,遵循从 domain -- mapper -- service -- controller,注意检查导包
- 第六步:连接数据库,确认微服务数据库
- 第七步:测试项目 访问 接口文档 进行代码请求测试http://localhost:8081/doc.htmlhttp://localhost:8081/doc.html
<!--来自父工程--><dependencies><!--common--><dependency><groupId>com.heima</groupId><artifactId>hm-common</artifactId><version>1.0.0</version></dependency><!--web--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--数据库--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!--mybatis--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId></dependency><!--单元测试--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency></dependencies><build><finalName>${project.artifactId}</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
2.2 解决商品模块的请求 500 问题
【原因】 数据库连接失败,配置文件加载失败
【解决措施】修改配置文件名即可
三、服务注册订阅与远程调用:以拆分购物车为例
3.1 拆分模块、发现问题
前面的拆分步骤照常,以下是拆分购物车模块遇到的问题
购物车微服务需要依赖商品微服务模块的方法,因此产生了报错,对此我们先采取注释的方式。先确保购物车微服务模块搭建运行成功后,后续我们再来通过SpringCloud的技术来解决这个问题。
接着配置数据库、将微服务部署到8082端口,启动访问测试:http://localhost:8082/doc.html
3.2 问题分析、解决方法
我们发现,拆分微服务的项目往往会面临这样一个问题:一个微服务需要注入另外一个微服务的方法或属性。
可是拆分微服务需要遵循一个原则:独立性、单一职责。也就是说我们不应该在购物车的微服务业务中注入过多的商品微服务模块的方法。
因此,我们需要思考如何让购物车微服务获取到商品微服务的方法同时又不破坏拆分原则呢?我们知道实际上我们发送的请求是一个完整的URL,可不可以这样做:我们模仿前端向后端发送请求的格式,让购物车发送获取商品信息的请求,这样一来不就解决问题了嘛
3.3 实现远程调用:RestTemplate工具,实现端到端请求发送
服务拆分之后,不可避免的会出现跨微服务的业务,此时微服务之间就需要进行远程调用。微服务之间的远程调用被称为RPC,即远程过程调用。
【RestTemplate介绍】
java开发中,使用http连接访问第三方网络接口,通常使用工具HttpClient和OKHttp。(后面讲)
如果使用spring框架,可以使用Spring给我们提供了一个RestTemplate工具,可以方便的实现Http请求的发送。
restTemplate默认的连接方式是java中的HttpConnection,可以使用ClientHttpRequestFactory指定不同的HTTP连接方式。
【使用步骤】
1. 在springboot项目中,需要使用RestTemplate的模块【cartcart-service】新建config配置
2. 注入RestTemplate对象
3. 使用RestTemplate方法进行代码修改
private void handleCartItems(List<CartVO> vos) {// 1.获取商品idSet<Long> itemIds = vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());// 2.查询商品// 2.1 构建请求、发送请求ResponseEntity<List<ItemDTO>> response = restTemplate.exchange("http://localhost:8081/items?ids={ids}",HttpMethod.GET,null,new ParameterizedTypeReference<List<ItemDTO>>() {},Map.of("ids", CollUtil.join(itemIds, ",")));//2.2 解析响应对象if(!response.getStatusCode().is2xxSuccessful()) {// 查询失败return;}// 2.3 获取查询商品对象List<ItemDTO> items = response.getBody();if(CollUtils.isEmpty(items)) {return;}// 3.构建商品id与商品对象的映射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.setStock(item.getStock()); // 库存v.setStatus(item.getStatus()); // 状态}}
【功能测试】
四、微服务基础拆分总结
到此为止,事实上你已经可以搭建一个简易的微服务项目了。尽管这个微服务项目还有诸多问题和挑战。
例如你发现没有?在我们使用RestTemplate工具发送请求的时候,url是我们写死的。而且开发者需要非常清楚有这么一个方法,有这么一个方法的提供者,并且要知道提供者的具体地址。与此同时,如果服务的提供者宕机了,服务调用者也无法得知。这样的限制太大了。所以后期我们一定会寻求这方面的突破的。
在基础篇中,我们了解了微服务架构和单体架构的区别、学习了微服务拆分的模式和需要遵循的原则、尝试进行简单微服务模块的拆分并调试成功、成功发现跨微服务请求问题并初步尝试使用强大的RestTemplate去解决它......通过以上内容,相信你已经入门微服务了,接下来也能更好地去深入微服务项目的学习。
五、微服务基础追问巩固
1. 谈谈什么是微服务?什么时候要用微服务?目前主流的微服务技术有哪些?
2. 谈谈拆分微服务应该遵守哪些原则?你认为其中最有挑战性的是什么?
3. 详细讲解微服务的拆分步骤是什么?
4. 如果拆分过程中遇到要使用其他微服务提供的方法是,如何实现请求的远程调用?
5. 了解过RestTemplate没有?请谈谈你对RestTemplate的看法,具体的使用步骤是什么?