系统架构的演变
1.单体应用架构(Monolithic Architecture)
定义:单体应用架构是一种传统的软件架构模式,其中所有的功能模块被构建成一个独立的可部署单元。大部分web⼯程(包含前端⻚⾯,web层代码,service层代码,dao层代码)是将 所 有的功能模块,打包到⼀起并放在⼀个web容器中运行。
优点:
-
简单性:设计和部署相对简单,易于理解和维护。
-
项目架构简单,前期开发成本低,周期短,⼩型项⽬的⾸选。
-
部署方便:所有的功能集成在⼀个项目工程中,部署和更新相对容易。
缺点:
-
可扩展性差:当应用程序变大时,构建和部署时间会显著增加。
-
维护困难:随着代码库的增长,维护变得更加复杂。
-
灵活性差:更新某个模块可能需要重新部署整个应用程序。
-
可靠性问题:一个模块的错误可能导致整个应用程序的崩溃。
适用场景:小型项目、初创公司产品、需求稳定且变动较少的系统。
2.垂直应用架构(Vertical Application Architecture)
定义:垂直应用架构是一种将应用程序按照业务功能进行分割的架构模式。每个垂直应用(或称为垂直切片)独立负责一个特定的业务功能,从数据存储到用户界面都在一个单独的模块中实现。
优点:
-
项⽬架构简单,前期开发成本低,周期短,⼩型项⽬的⾸选。
-
通过垂直拆分,原来的单体项目不至于无限扩大
-
不同的项目可采用不同的技术。
缺点:
-
重复工作:相同的功能可能在多个垂直应用模块中重复实现。
-
资源利用率低:由于各个垂直应用模块独立运行,可能导致资源浪费。
-
系统复杂性增加:需要管理多个独立的模块。
适用场景:业务功能明确且相对独立的系统、需要快速迭代和交付的项目、多团队协作的大型项目。
3.SOA架构(Service-Oriented Architecture)
定义:当垂直应⽤越来越多,应⽤之间交互不可避免,将核⼼业务抽取出来,作为独⽴的服务,逐渐形成稳定 的服务 中⼼,使前端应⽤能更快速的响应多变的市场需求
优点
-
抽取公共的功能为服务,提⾼开发效率
-
对不同的服务进⾏集群化部署解决系统压⼒
-
基于ESB/DUBBO减少系统耦合
缺点
-
抽取服务的粒度较⼤
-
服务提供⽅与调⽤⽅接⼝耦合度较⾼
适用场景:大型企业应用、需要高可扩展性和高可用性的系统。
4.微服务架构(Microservices Architecture)
定义:微服务架构是一种将应用程序分解为小型、独立且松耦合的服务的架构模式。每个服务负责特定的业务功能,并通过轻量级的通信机制(如HTTP/REST、消息队列)与其他服务进行交互。
优点:
-
独立部署:各个服务可以独立部署,减少了发布的复杂性和风险。
-
独立扩展:每个服务可以根据需求独立扩展,优化资源使用。
-
技术多样性:允许不同服务使用最适合的技术栈。
-
故障隔离:一个服务的故障不会导致整个系统的崩溃。
-
微服务遵循单⼀原则。微服务之间采⽤Restful等轻量协议传输。
缺点:
-
微服务过多,服务治理成本⾼,不利于系统维护。
-
分布式系统开发的技术成本⾼(容错、分布式事务等)。
适用场景:大型和复杂系统、需要高可扩展性和高可用性的场景、多团队协作且要求快速迭代和独立部署的项目。
SOA与微服务的关系
分布式核心知识
分布式中的远程调用(通信)
-
RESTful接口
GET⽤来获取资源,POST⽤来新建资 源(也可以⽤于更新资源),PUT⽤来更新资源,DELETE⽤来删除资源
-
每⼀个URI代表⼀种资源;
-
客户端和服务器之间,传递这种资源的某种表现层;
-
客户端通过四个HTTP动词,对服务器端资源进⾏操作,实现"表现层状态转化"。
-
-
RPC协议
RPC( Remote Procedure Call )⼀种进程间通信⽅式。允许像调⽤本地服务⼀样调⽤远程服务。主要目的是让远程服务调用更简单、透明。
两者区别
比较项 | Restful | RPC |
---|---|---|
通讯协议 | Http | TCP |
性能 | 略低 | 较高 |
灵活度 | 高 | 低 |
应用 | 微服务架构 | SOA架构 |
分布式中的CAP原理(各个节点的状态如何同步)
分布式系统中的三个特性
-
Consistency(⼀致性) :数据⼀致更新,所有数据的变化都是同步的
-
Availability(可⽤性) :在集群中⼀部分节点故障后,集群整体是否还能响应客户端的读写请求
-
Partition tolerance(分区容忍性) :某个节点的故障,并不影响整个系统的运行
常见的微服务框架
SpringCloud
一系列框架的有序集合,简化了分布式系统基础设施的开发
ServiceComb
一个开源微服务解决方案,实现对微服务应用的高效运维管理
ZeroC ICE
基于RPC框架发展,新⼀代的面向对象的分布式系统中间件
微服务相关概念
-
服务注册与发现
服务注册:将自身服务信息注册到注册中心
服务发现:服务实例请求注册中心获取所依赖服务信息
-
负载均衡
将工作负载分布到多个服务器来提高网站、应用、数据库、其他服务的性能和可靠性
-
熔断(牺牲局部,保全整体)
下游服务因访问压力过大而响应失败,上游服务为保护系统整体可用性,暂时切断对下游服务的调用
-
链路追踪
对一次请求涉及多个服务 链路进行日志记录、性能监控
-
API网关
介于客户端和服务器端之间的中间层,它负责处理客户端的请求,并将这些请求路由到相应的后端服务。
SpringCloud(微服务框架)
概念
一系列框架的有序集合,简化了分布式系统基础设施的开发
核心组件
Spring Cloud Netflix组件
组件名称 | 作用 |
---|---|
Eureka | 服务注册中心 |
Ribbon | 客户端负载均衡 |
Feign | 声明式服务调用 |
Hystrix | 客户端容错保护 |
Zuul | API服务网关 |
Spring Cloud Alibaba组件
组件名称 | 作用 |
---|---|
Nacos | 服务注册中心 |
Sentinel | 客户端容错保护 |
Spring Cloud原生及其他组件
组件名称 | 作用 |
---|---|
Consul | 服务注册中心 |
Config | 分布式配置中心 |
Gateway | API服务网关 |
Sleuth/Zipkin | 分布式链路追踪 |
微服务间的调用关系
案例搭建
服务提供者:服务的被调用方,提供调用接口的一方
服务消费者:服务的调用方,依赖于其他服务的一方
以电商系统中常见的用户下单为例,用户向订单微服务发起⼀个购买的请求。在进行保存订单之前 需要调用商品微服务查询当前商品库存,单价等信息。在这种场景下,订单微服务就是⼀个服务消费者,商品微服务就是⼀个服务提供者
实现步骤:
-
创建公共父模块,引入子模块(作用:作为文件夹、集中管理坐标(聚合) 项目不需要打包测试运行,只留一个pom文件即可)
公共模块 shop_common ,⽤于存放公共的实体类和⼯具类 订单微服务模块 shop_order 端⼝809X 商品微服务模块 shop_product 端⼝808X ⽤户微服务模块 shop_user 端⼝807X
-
在商品微服务项目中,利用三层架构完成根据商品id查询商品信息的功能
-
在订单微服务项目中,编写下单代码逻辑(通过restTemplate调⽤商品微服务)
RestTemplate类可用于在应用中调用rest服务,它简化了与http服务的通信方式,统 ⼀了RESTful的标准,封装了http链接, 我们只需要传入url及返回值类型即可。
-
订单微服务项目启动类中,配置RestTemplate交给spring管理
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
👏👏👏改进一下
上述案例将提供者的网络地址 (ip,端口)等硬编码到了代码中,这种使得应用场景有局限并且无法动态调整
解决方案:通过注册中心动态的对服务注册和服务发现
微服务的注册中心
注册中心记录了服务和服务地址的映射关系,将服务注册到这里,当服务需要调用其它服务时,就这里找到服务的地址,进行调用。
常见的注册中心
Zookeeper
分布式服务框架,解决分布式应用中经常遇到的⼀些数据管理问题,
如:统⼀命名服务、状态同步服务、集群管理、分布式应⽤配置项的管理等。简单来说zookeeper=文件系统+监听通知机制。
Eureka
Eureka是在Java语⾔上,基于Restful Api开发的服务注册与发现组件,Springcloud Netflix中的重要组件
Consul
Consul是由HashiCorp基于Go语⾔开发的⽀持多数据中⼼分布式⾼可⽤的服务发布和注册服务软件, 采⽤Raft算法保证服务的⼀致性,且⽀持健康检查。
Nacos
Nacos是⼀个更易于构建云原⽣应⽤的动态服务发现、配置管理和服务管理平台。简单来说 Nacos 就是 注册中心+ 配置中心的组合,提供简单易⽤的特性集,帮助我们解决微服务开发必会涉及到的服务注册与发现,服务配置,服务管理等问题。
四者区别
Nacos(注册中心)
Nacos简介
nacos是⼀个注册中心,用来管理注册上来的各个微服务
nacos环境搭建
-
安装nacos
下载地址: https://github.com/alibaba/nac
os/releases
下载zip格式的安装包,然后进⾏解压缩操作
-
启动nacos
#切换⽬录
cd nacos/bin
#命令启动
startup.cmd -m standalone
或者直接双击startup.cmd运⾏
-
访问nacos
打开浏览器输⼊http://localhost:8848/nacos,即可访问服务, 默认密码是nacos/nacos
Nacos配置管理
可以集中管理所有实例的配置
注意:项⽬的核心配置,需要热更新的配置才有放到nacos管理的必要。基本不会变更的⼀些配置还是保存在微服务本地⽐较好。
1.在nacos中添加配置文件
2.从微服务拉取配置
实现步骤:
-
引入nacos-config依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
-
添加bootstrap.yaml(删除application.yml)
配置文件优先级(由高到低): bootstrap.properties -> bootstrap.yml -> application.properties -> application.yml
spring:
application:
name: service-product
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848 #nacos中⼼地址
file-extension: yaml # 配置⽂件格式
#指定加载哪个配置文件的信息
# profiles:
# active: dev # 环境标识,开发环境
3.配置热更新
修改nacos中的配置后,微服务中无需重启即可让配置生效,也就是配置热更新
1.配置中心添加配置
方式一:
在@Value注⼊的变量所在类上添加注解@RefreshScope
方式二:
硬编码方式
@RestController
public class NacosConfigController {
@Autowired
private ConfigurableApplicationContext applicationContext;
@GetMapping("/nacos-config-test2")
public String nacosConfingTest2() {
return applicationContext.getEnvironment().getProperty("config.app
Name");
}
}
4.配置共享
抽取公共配置代码,减少配置代码重复
-
同服务内配置共享
# 访问http://localhost:8081/nacos-config-test1 输出test
@RestController
@RefreshScope//只需要在需要动态读取配置的类上添加此注解就可以
public class ProductController {
@Value("${config.appName}")
private String appName;
@GetMapping("/nacos-config-test1")
public String nacosConfingTest1() {
return appName;
}
-
不同服务内配置共享
配置共享的优先级
当nacos、服务本地同时出现相同属性时,优先级有⾼低之分:
微服务注册到nacos
订单服务举例
-
在pom.xml中添加nacos的依赖
<!--nacos客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
-
在启动类上添加@EnableDiscoveryClient注解
@SpringBootApplication
//@EnableDiscoveryClient自动变成一个服务发现客户端。会在启动时向配置的服务注册中心注册自己也能够从服务注册中心中查询到其他服务的信息,以便进行服务间的调用。
@EnableDiscoveryClient
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
-
在application.yml中添加nacos服务的地址
server:
port: 8091
spring:
application:
name: service-order
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql:///xinxin?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
username: root
password: 20020630
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
-
启动服务, 观察nacos的控制⾯板中是否有注册上来的商品微服务
商品服务同样操作,直到全部注册成功
-
编写Controller逻辑代码,使用discoveryClient实现对商品微服务的调用(保存订单前需查询到部分商品信息)。
@Autowired
RestTemplate restTemplate;
//获取nacos中服务的元数据
@Autowired
DiscoveryClient discoveryClient;
//下单
@RequestMapping("/order/prod/{pid}")
public Order order(@PathVariable("pid") Integer pid){
//从nacos中获取商品服务地址
ServiceInstance serviceInstance = discoveryClient.getInstances("service-product").get(0);
//获取商品服务的端口,拼接成url ---> localhost:8081
String url = serviceInstance.getHost() + ":" +serviceInstance.getPort();
//代入url,再通过restTemplate调用商品微服务
Product product = restTemplate.getForObject( "http://" + url +"/product/" + pid, Product.class);
//下单(创建订单)
Order order = new Order();
order.setUid(1);
order.setUsername("测试用户");
order.setPid(pid);
order.setPname(product.getPname());
order.setPprice(product.getPprice());
order.setNumber(1);
orderService.createOrder(order);
return order;
}
}
👏👏👏改进一下
更加方便的调用微服务,多个微服务的提供者如何选择,如何负载均衡等。
解决方法:使用负载均衡器Ribbon