微服务架构通过将应用分解为一组小的、独立的服务来实现,每个服务围绕特定业务功能构建,并能独立部署与扩展。这种架构增强了开发灵活性、提高了系统的可维护性和扩展性,使得团队可以更快地响应变化和市场需求。
目录
认识微服务
单体架构
单体架构:顾名思义,整个项目中所有功能模块都在一个工程中开发;项目部署时需要对所有模块一起编译、打包;项目的架构设计、开发模式都非常简单。
适用场景;当项目规模较小时,这种模式上手快,部署、运维也都很方便,因此早期很多小型项目都采用这种模式。
项目的业务规模越来越大,团队开发人员也不断增加,单体架构就呈现出越来越多的问题:
-
团队协作成本高:试想一下,你们团队数十个人同时协作开发同一个项目,由于所有模块都在一个项目中,不同模块的代码之间物理边界越来越模糊。最终要把功能合并到一个分支,你绝对会陷入到解决冲突的泥潭之中。
-
系统发布效率低:任何模块变更都需要发布整个系统,而系统发布过程中需要多个模块之间制约较多,需要对比各种文件,任何一处出现问题都会导致发布失败,发布耗时。
-
系统可用性差:单体架构各个功能模块是作为一个服务部署,相互之间会互相影响,一些热点功能会耗尽系统资源,导致其它服务低可用。
此时如果我们对系统做水平扩展,增加更多机器,资源还是会被这样的热点接口占用,从而影响到其它接口,并不能从根本上解决问题。这也就是单体架构的扩展性差的一个原因。
微服务架构
为了解决单体架构因为带来的问题,于是微服务出现了。
微服务架构,首先是服务化,就是将单体架构中的功能模块从单体应用中拆分出来,独立部署为多个服务。同时要满足下面的一些特点:
-
单一职责:一个微服务负责一部分业务功能,并且其核心数据不依赖于其它模块。
-
团队自治:每个微服务都有自己独立的开发、测试、发布、运维人员,团队人员规模不超过10人(2张披萨能喂饱)
-
服务自治:每个微服务都独立打包部署,访问自己独立的数据库。并且要做好服务隔离,避免对其它服务产生影响
问题解决:
问题一:由于服务拆分,每个服务代码量大大减少,参与开发的后台人员在1~3名,协作成本大大降低
问题二:每个服务都是独立部署,当有某个服务有代码变更时,只需要打包部署该服务即可
问题三:每个服务独立部署,并且做好服务隔离,使用自己的服务器资源,不会影响到其它服务
当然,对于Java领域开发者来说,SpringCloud框架可以说是目前最全面的微服务组件的集合。
微服务拆分
服务拆分原则
拆分时机
在项目初期,尤其是初创项目,通常优先考虑敏捷开发以快速产出可投入市场的最小可行产品(MVP),以便尽早验证项目的可行性。此时,采用简单的单体架构较为常见,因其开发成本低、部署快速,即便项目最终不被市场接受,损失也相对较小。随着用户量增长和业务复杂度提升,再逐步向微服务架构迁移,尽管这可能会导致后期因代码耦合面临拆分难题。
相反,对于从立项就目标明确的大型项目 ,考虑到长期发展和扩展性,初期即选择复杂的微服务架构进行设计。这种方式虽然前期需要更多的资源投入,但有利于后续的维护与扩展,避免了日后服务拆分的困扰。简而言之:
初创小项目:先用单体架构快速上线测试市场反应,之后视情况转向微服务(前易后难)。
大型明确项目:直接采用微服务架构规划长远发展,减少后期重构麻烦(前难后易)。
拆分细则
拆分目标角度:
高内聚:每个微服务的职责要尽量单一,包含的业务相互关联度高、完整度高。
低 耦合:每个微服务的功能要相对独立,尽量减少对其它微服务的依赖,或者依赖接口的稳定性要强。
拆分方式角度:
纵向拆分:依据项目功能模块进行拆分。例如,在一个电商应用中,可以将用户管理、订单管理、购物车、商品管理和支付等功能各自独立为单独的服务。这种方式提高了每个服务的内聚性,使得各服务专注于特定业务领域。
横向拆分:识别并提取各个功能模块中的共通业务逻辑作为通用服务。比如,无论是用户登录还是下单过程,都涉及到发送消息通知和记录风控数据的操作。这些共用的功能可以被抽取出来,形成独立的消息中心服务和风控管理服务。这样做不仅提高了代码复用性,减少了重复开发工作,而且由于接口相对稳定,不会造成服务间的过度耦合。
微服务工程结构
一般微服务项目有两种不同的工程结构:
完全解耦
- 描述:每个微服务是一个独立工程,可使用不同语言开发。
- 优点:服务间耦合度低。
- 缺点:各自独立仓库,管理复杂。
Maven聚合
- 描述:整个项目作为一个Project,各微服务为Module。
- 优点:代码集中,便于管理和运维。
- 缺点:服务间存在耦合,编译时间较长。
服务拆分实现
按照Maven聚合实现拆分对于小型项目更为合适。
针对于已经完成开发的项目,我们拆分一般按照以下步骤:
1.对于拆分服务的精确划分,应尽量符合服务拆分原则
2.新建对于模块,并且引入依赖,配置application.yaml等配置文件
3.正确引入实体类,controller,service,mapper等业务代码
事实上,往往服务内都会需要进行,对于服务的调用,我们该如何跨服务调用?
服务调用
RestTemplate
Spring给我们提供了一个RestTemplate的API,可以方便的实现Http请求的发送。
先将RestTemplate注册为一个Bean
java
@Configuration
public class RemoteCallConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
利用RestTemplate发送http请求
java
ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
"http://localhost:8081/items?ids={ids}",// 请求URL,包含占位符{ids}
HttpMethod.GET,// 使用GET方法
null,// 不使用请求体或请求头
new ParameterizedTypeReference<List<ItemDTO>>() {}, // 指定响应类型为List<ItemDTO>
Map.of("ids", CollUtil.join(itemIds, ","))// 替换URL中的{ids}为逗号分隔的itemIds字符串
);
// 2.2.解析响应
if(!response.getStatusCode().is2xxSuccessful()){
// 查询失败,直接结束
return;
}
List<ItemDTO> items = response.getBody();
测试完毕:实现服务之间进行远程调用