架构的演变
单体应用架构
单体架构就是将所有的应用、数据库、文件都部署在一台机器上,俗称All-In-One。简单来讲其实就是我们熟知的SSH 架构或SSM架构,把所有的业务模块都放在一个应用中开发,这里面又衍生出三层架构,即表示层、业务逻辑层和数据库访问层,虽然在软件设计中划分了经典的三层模型,但是对业务场景没有划分,一个典型的单体应用就 是将所有的业务场景的表示层、业务逻辑层和数据访问层放在一个工程项目中,最终经过编译、打包,部署在一台服务器上。单体架构图如下:
-
优点
-
部署简单
由于是完整的结构体,可以直接部署在一个服务器上即可。
-
技术单一
项目不需要复杂的技术栈,往往一套熟悉的技术栈就可以完成开发,架构图简单易懂。
-
用人成本低
单个程序员可以完成业务接口到数据库的整个流程。
-
-
缺点
-
系统启动慢
一个进程包含了所有的业务逻辑,涉及到的启动模块过多,导致系统的启动、重启时间周期过长。
-
系统错误隔离性差、模块之间耦合性高
任何一个模块的错误均可能造成整个系统的宕机。
-
不安全
用户可以直接访问到服务器
-
扩展性差
无法针对某一模块进行扩展和优化,前台页面模块承载压力过大,无法对前台页面模块进行单独扩展
-
问题:当用户访问量过大时,服务器承受压力是有限制的,尽管可以增加服务器的配置,那也是有天花板的
垂直应用结构
随着公司业务的不断发展,由于单台服务器性能有限,我们无法对单个模块进行扩展,那么需要将各个模块进行拆分,分为治之,
如下图:将模块拆分成多个服务。
-
优点:
-
扩展性变强
系统拆分之后,可以对某个模块进行扩展和优化,
-
模块之间耦合性降低
某一个模块的错误不会太过于直接影响其它模块
-
-
缺点:
-
调用问题
系统之间无法直接相互调用
-
冗余性
随着项目功能的完善,代码会有部分重复
-
问题:各个系统之间相互会需要大量的冗余代码,如何能把各个模块都需要的公共模块提取出来?
分布式结构
把各个模块都需要的公共模块提取出来,比如基本信息、销售管理、采购管理都需要用户服务,商品服务,订单服务。
-
优点:
-
复用性
抽取公共代码模块为服务层,增强代码复用性
-
-
缺点:
-
复杂性
各个模块之间调用关系复杂,手动维护贼困难
-
SOA架构
SOA(Service Oriented Architecture)面向服务的架构
提供一个消息总线,各个服务之间调用都是去寻找消息总线
-
优点:
-
调用明确
使用服务治理中心帮助我们维护复杂的调用关系
-
-
缺点:
-
依赖性
服务有依赖性,可能会因为一个服务的问题,导致多个系统不可用(拆分的不够彻底)
-
微服务架构
也是拆分,但是强调的是原子化拆分,微服务架构在某种程度上是SOA继续发展的下一步,更加强调彻底拆分,
每个服务遵循单一职责,自己负责自己的事情,不互相干涉,如果双十二,订单任务量大,可以在搞一个订单服务。
各个服务之间相互调用,使用注册中心,将各个服务的地址配置到服务配置中心里面。
-
优点:
-
原子化
原子化拆分,独立打包,保证每个微服务有清晰任务划分,利于扩展
-
-
缺点:
-
难
分布式系统开发技术成本高,比如分布式事务等
-
服务件如何调用?需要方案 和具体技术实现
-
注册中心?具体技术实现
-
微服务架构
Spring Boot与Spring Cloud
-
Spring Boot是Spring的一套快速配置脚手架,可以基于Spring Boot快速开发单个微服务;
Spring Cloud是一个基于Spring Boot实现的云应用开发工具。
-
Spring Boot专注于快速、方便集成的单个个体;
Spring Cloud是关注全局的服务治理框架。
-
Spring Boot使用了约定大于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置;
Spring Cloud很大的一部分是基于Spring Boot来实现。
-
Spring Boot可以离开Spring Cloud独立使用开发项目;
但是Spring Cloud离不开Spring Boot,属于依赖的关系。
-
Spring Cloud是解决微服务问题的一个解决方案,有很多个组件组成
为什么用:
SpringCloud。高并发,高可用
Spring Cloud组件
服务的注册和发现
注册中心
无注册中心
有注册中心
服务间调用
服务间调用方式
-
Feign
-
OpenFeign
-
Dubbo
Feign调用服务
provider:service-user 被调用的服务
-
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2021.0.1.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
application.properties
server.port=8080
spring.application.name=service-provider
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
UserApplication.java
@EnableDiscoveryClient //开启注册服务
@SpringBootApplication
public class SpringcloudDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringcloudDemoApplication.class, args);
}
}
UserController
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/findAll")
public Map<Object, Object> findAll() {
HashMap<Object, Object> map = new HashMap<>();
map.put("msg", "请求成功,我是user");
map.put("code", 200);
return map;
}
}
consumer:调用服务方
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--服务调用 真正起到作用的是loadbalancer-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
负载均衡
负载均衡职责是将网络请求或者其他形式的负载“均摊”到不同的服务节点上,从而避免服务集群中部分节点压力过大、资源紧张,
而另一部分节点比较空闲的情况。
通过合理的负载均衡算法,我们希望可以让每个服务节点获取到适合自己处理能力的负载,实现处理能力和流量的合理分配
SpringCloud提供了LoadBalancer组件实现负载均衡
负载均衡的策略是:轮询(Round Robin)
熔断器
Hystrix熔断器
熔断器只与服务调用方有关
A服务调用B服务,B服务调用C服务,其中B服务出现了问题,造成的问题就是服务雪崩。
我们希望A在调用一个有问题的B服务时,B服务应该通知A服务,我有问题,不应该报错,那以后其它服务都不在访问B服务。
B已经坏了,就没有必要在找B了, 称之为熔断,
那么A会降级 响应一个友好的错误页面,
引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
application.properties
server.port=8081
spring.application.name=service-custom
spring.cloud.nacos.discovery.server-addr=localhost:8848
feign.circuitbreaker.enabled=true # 触发熔断
1.创建Feign接口的实现类com.codingfuture.feign.fallback.UserFeignFallback
@Component
public class UserFeignFallback implements UserFeign {
@Override
public Map<Object, Object> findAll() {
HashMap<Object, Object> map = new HashMap<>();
map.put("msg", "请求失败,页面出错了");
map.put("code", 403);
return map;
}
}
2.Feign接口
fallback = UserFeignFallback.class
@FeignClient(name = "service-provider", fallback = UserFeignFallback.class)
public interface UserFeign {
@GetMapping("/user/findAll")
Map<Object, Object> findAll();
}
Sentinel熔断器
引入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
application.properties
spring.cloud.sentinel.transport.port=8719
spring.cloud.sentinel.transport.dashboard=localhost:8181
feign.sentinel.enabled=true # 触发熔断
Feign 支持
1.创建Feign接口的实现类com.codingfuture.feign.fallback.UserFeignFallback
@Component
public class UserFeignFallback implements UserFeign {
/**
* 降级处理
* @return
*/
@Override
public Map<Object, Object> findAll() {
HashMap<Object, Object> map = new HashMap<>();
map.put("msg", "请求失败,页面出错了");
map.put("code", 403);
return map;
}
}
2.Feign接口
fallback = UserFeignFallback.class
@FeignClient(name = "service-provider", fallback = UserFeignFallback.class)
public interface UserFeign {
@GetMapping("/user/findAll")
Map<Object, Object> findAll();
}
熔断器作用:
-
降级
-
分布式系统中的降级:降级最主要解决的是资源不足和访问量增加的矛盾,在有限的资源情况下,可以应对高并发大量请求。那么在有限的资源情况下,想要达到以上效果就需要对一些服务功能进行一些限制,放弃一些功能,保证整个系统能够平稳运行。
-
降级方式:
-
将强一致性变成最终一致性,不需要强一致性的功能,可以通过消息队列进行削峰填谷,变为最终一致性达到应用程序想要的效果。
-
简化功能流程,程序出现问题,做响应应急处理,提高用户访问体验。
-
停止访问一些次要功能,比如双十一不让退货等。
-
-
自动降级条件:
-
流量达到阈值触发服务降级
-
调用失败次数达到阈值
-
请求下游服务发生故障通过状态码
-
-
-
熔断
-
生活中熔断:保险丝烧断 保险丝也被称为熔断器。
-
分布式系统中的熔断:熔断模式可以防止应用程序不断地尝试请求下游(目标服务/第三方服务)可能超时和失败的服务,可以不必等待下游服务的修复而达到应用程序可执行的状态。
-
不熔断带来的问题:
-
等待增加了整个链路的请求时间。
-
下游系统有问题,不断的请求导致下游系统有持续的访问压力难以恢复。
-
等待时间过长造成上游服务线程阻塞,CPU占用率过高。
-
-
-
限流
-
生活中限流:乘坐交通工具限流
-
分布式系统中的限流:只允许指定的事件进入系统,超过的部分将被拒绝服务、排队或等待、降级等处理。
对于服务而言,限流为了保证一部分的请求流量可以得到正常的响应,总好过全部的请求都不能得到响应,甚至导致系统雪崩
-
限流实现方式:sentinel 流控处理
-
网关
我们某一个服务做了集群,前端发请求也不能总换地址,那就统一走网关
192.168.1.2:8081
192.168.1.2:8082
网关也要注册到nacos中
依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2021.0.1.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
application.properties
server.port=9999
spring.application.name=gateway-demo
spring.cloud.nacos.discovery.server-addr=git.coding-future.com:8848
spring.cloud.gateway.discovery.locator.enabled=true
访问接口规则
网关 服务名 接口名
http://localhost:9999/service-custom/custom/findAll
http://localhost:8082/find?username=zs
http://localhost:8083/find?username=zs
跨域问题
出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,
如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。
所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)
网关中配置跨域
application.properties
# 解决跨域问题
spring.cloud.gateway.globalcors.cors-configurations[/**].allowed-origin-patterns=* #允许任何域发起跨域请求
spring.cloud.gateway.globalcors.cors-configurations[/**].allow-credentials=true # 设置允许Cookie
spring.cloud.gateway.globalcors.cors-configurations[/**].allowed-methods=* #设置允许跨域请求的方法
spring.cloud.gateway.globalcors.cors-configurations[/**].allowed-headers=* #允许跨域请求包含content-type
application.yml
server:
port: 9999
spring:
application:
name: gateway-demo
cloud:
nacos:
discovery:
server-addr: git.coding-future.com:8848
gateway:
discovery:
locator:
enabled: true
globalcors:
cors-configurations:
'[/**]':
allowedOriginPatterns: "*"
allowCredentials: true
allowedMethods: "*"
allowed-headers: "*"
命名空间
spring:
application:
name: gateway-server
cloud:
nacos:
discovery:
namespace: 87850062-a4e6-4615-833a-c72e0d605177