系统架构
随着互联网的发展,网站应用的规模也在不断的扩大,进而导致系统架构也在不断的进行变化。 从互联网早起到现在,系统架构大体经历了下面几个过程: 单体应用架构--->集群应用架构--->垂直应用架构--->SOA 架构--->微服务架构.
单体应用架构
集群应用架构
垂直应用架构
SOA 架构
微服务架构介绍
微服务架构有哪些优势
独立开发 – 所有微服务都可以根据各自的功能轻松开发
独立部署 – 基于其服务,可以在任何应用程序中单独部署它们
故障隔离 – 即使应用程序的一项服务不起作用,系统仍可继续运行
混合技术堆栈 – 可以使用不同的语言和技术来构建同一应用程序的不同服务
粒度缩放 – 单个组件可根据需要进行缩放,无需将所有组件缩放在一起
微服务架构的常见问题
一旦采用微服务系统架构,就势必会遇到这样几个问题:
这么多小服务,如何管理他们?
这么多小服务,他们之间如何通讯?
这么多小服务,客户端怎么访问他们?
这么多小服务,一旦出现问题了,应该如何自处理?
这么多小服务,一旦出现问题了,应该如何排错?
对于上面的问题,是任何一个微服务设计者都不能绕过去的,因此大部分的微服务产品都针对每一个问题提供了相应的组件来解决它们。
微服务架构的常见概念
服务治理 :服务治理就是进行服务的自动化管理,其核心是服务的自动注册与发现。
服务注册: 服务实例将自身服务信息注册到注册中心。
服务发现: 服务实例通过注册中心,获取到注册到其中的服务实例的信息,通过这些信息去请求它们提 供的服务。
服务剔除: 服务注册中心将出问题的服务自动剔除到可用列表之外,使其不会被调用到。
服务调用
在微服务架构中,通常存在多个服务之间的远程调用的需求。
目前主流的远程调用技术有基于 HTTP 的 RESTful 接口以及基于 TCP 的 RPC协议。
REST(Representational State Transfer) 这是一种 HTTP 调用的格式,更标准,更通用,无论哪种语言都支持 http 协议.
RPC(Remote Promote Call) 一种进程间通信方式。允许像调用本地服务一样调用远程服务。RPC 框架的主要目标就是让远程服 务调用更简单、透明。RPC框架负责屏蔽底层的传输方式、序列化方式和通信细节。开发人员在使 用的时候只需要了解谁在什么位置提供了什么样的远程服务接口即可,并不需要关心底层通信细节 和调用过程。
服务网关
随着微服务的不断增多,不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,如果让客户端直接与各个微服务通信可能出现:
客户端需要调用不同的 url 地址,增加难度
在一定的场景下,存在跨域请求的问题
每个微服务都需要进行单独的身份认证
针对这些问题,API 网关顺势而生。
API 网关直面意思是将所有 API 调用统一接入到 API 网关层,由网关层统一接入和输出。
一个网关的 基本功能有:统一接入、安全防护、协议适配、流量管控、长短链接支持、容错能力。有了网关之后, 各个 API 服务提供团队可以专注于自己的的业务逻辑处理,而 API 网关更专注于安全、流量、路由等问题。
服务容错
在微服务当中,一个请求经常会涉及到调用几个服务,如果其中某个服务不可用,没有做服务容错 的话,极有可能会造成一连串的服务不可用,这就是雪崩效应。 我们没法预防雪崩效应的发生,只能尽可能去做好容错。
链路追踪
随着微服务架构的流行,服务按照不同的维度进行拆分,一次请求往往需要涉及到多个服务。互联 网应用构建在不同的软件模块集上,这些软件模块,有可能是由不同的团队开发、可能使用不同的编程 语言来实现、有可能布在了几千台服务器,横跨多个不同的数据中心。因此,就需要对一次请求涉及的 多个服务链路进行日志记录,性能监控即链路追踪.
微服务架构的常见解决方案
ServiceComb
Apache ServiceComb,前身是华为云的微服务引擎 CSE (Cloud Service Engine) 云服务,是全球 首个 Apache 微服务顶级项目。它提供了一站式的微服务开源解决方案,致力于帮助企业、用户和开发 者将企业应用轻松微服务化上云,并实现对微服务应用的高效运维管理。
SpringCloud
Spring Cloud 是一系列框架的集合。它利用 Spring Boot 的开发便利性巧妙地简化了分布式系统基 础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用 Spring Boot 的开发风格做到一键启动和部署。 Spring Cloud 并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服 务框架组合起来,通过 Spring Boot 风
格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留 出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
SpringCloud Alibaba
Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服 务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。
SpringCloud Alibaba 介绍
Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服 务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。 依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接 入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。
主要功能
服 务 限 流 降 级 : 默 认 支 持 WebServlet 、 WebFlux , OpenFeign 、 RestTemplate、Spring Cloud Gateway, Zuul, Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修 改限流降级规则,还支持查看限流降级 Metrics 监控。
服务注册与发现:适配 Spring Cloud 服务注册与发现标准,默认集成了Ribbon 的支持。 分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。
消息驱动能力:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
分布式事务:使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。 阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任 何时间、任何地点存储和访问任意类型的数据。
分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。 同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。
阿里云短信服务:覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建 客户触达通道。
组件
Sentinel:把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳 定性。
Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
RocketMQ:一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠 的消息发布与订阅服务。
Dubbo:Apache Dubbo™ 是一款高性能 Java RPC 框架。
Seata:阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。
Alibaba Cloud ACM:一款在分布式架构环境中对应用配置进行集中管理和推送的应用配置中心 产品。
Alibaba Cloud OSS: 阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提 供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和 访问任意类型的数据。
Alibaba Cloud SchedulerX: 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精 准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。
Alibaba Cloud SMS: 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速 搭建客户触达通道。
微服务环境搭建
以电商项目中的商品、订单、用户为案例
模块设计
springcloud-alibaba 父工程
shop-common 公共模块【实体类,工具类】
shop-user 用户微服务 【端口: 807x】
shop-product 商品微服务 【端口: 808x】
shop-order 订单微服务 【端口: 809x】
创建父工程
创建一个基本的 maven 工程,然后在 pom.xml 文件中添加下面内容
参考 pom.xml 文件
创建基础模块
1 创建 shop-common 模块,在 pom.xml 中添加依赖
2 创建实体类(略)
创建用户微服务
步骤:
1 创建 shop-user 服务子模块,添加公共模块依赖
com.ffyc.springcloud-alibaba
shop-common
1.0-SNAPSHOT
2 创建 SpringBoot 主类
@SpringBootApplication
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
3 加入配置文件
创建 application.yml
server:
port: 8071
spring:
application:
name: service-user
#数据库配置略
4 创建必要的接口和实现类(controller service dao)
创建商品微服务
重复用户创建步骤,注意修改端口,服务名
创建订单微服务
重复用户创建步骤,注意修改端口,服务名
微服务调用
在微服务架构中,最常见的场景就是微服务之间的相互调用。我们以电商系统中常见的用户下单为例来演示微服务的调用:客户向订单微服务发起一个下单的请求,在进行保存订单之前需要调用商品微 服务查询商品的信息。 我们一般把服务的主动调用方称为服务消费者,把服务的被调用方称为服务提供者。
在这种场景下,订单微服务就是一个服务消费者, 商品微服务就是一个服务提供者。
使用 RestTemplate 使用 http 方式远程访问
RestTemplate restTemplate;
Product p = restTemplate.getForObject("http://127.0.0.1:8072/product/findProduct/"+pid, Product.class);
这种调用方式的问题:
- 一旦服务提供者地址变化,就需要手工修改代码
- 一旦是多个服务提供者,无法实现负载均衡功能
- 一旦服务变得越来越多,人工维护调用关系困难
Nacos Discovery--服务治理
服务治理介绍
服务治理是微服务架构中最核心最基本的模块。用于实现各个微服务的自动化注册与发现。
服务注册:在服务治理框架中,都会构建一个注册中心,每个服务单元向注册中心登记自己提供服 务的详细信息。并在注册中心形成一张服务的清单,服务注册中心需要以心跳的方式去监测清单中 的服务是否可用,如果不可用,需要在服务清单中剔除不可用的服务。
服务发现:服务调用方向服务注册中心咨询服务,并获取所有服务的实例清单,实现对具体服务实 例的访问。
通过上面的调用图会发现,除了微服务,还有一个组件是服务注册中心,它是微服务架构非常重要 的一个组件,在微服务架构里主要起到了协调者的一个作用。
注册中心一般包含如下几个功能:
1. 服务发现: 服务注册:保存服务提供者和服务调用者的信息 服务订阅:服务调用者订阅服务提供者的信息,注册中心向订阅者推送提供者的信息
2. 服务配置: 配置订阅:服务提供者和服务调用者订阅微服务相关的配置 配置下发:主动将配置推送给服务提供者和服务调用者
3. 服务健康检测 检测服务提供者的健康情况,如果发现异常,执行服务
剔除
常见的注册中心
Zookeeper zookeeper 是一个分布式服务框架,是 Apache Hadoop 的一个子项目,它主要是用来解决分布式 应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用 配置项的管理等。
Eureka Eureka 是 Springcloud Netflix 中的重要组件,主要作用就是做服务注册和发现。但是现在已经闭 源 Consul Consul 是基于 GO 语言开发的开源工具,主要面向分布式,服务化的系统提供服务注册、服务发现 和配置管理的功能。
Nacos Nacos 是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。它是 Spring Cloud Alibaba 组件之一,负责服务注册发现和服务配置,可以这样认为nacos=eureka+config。
nacos
Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速 实现动态服务发现、服务配置、服务元数据及流量管理。 从上面的介绍就可以看出,nacos 的作用就是一个注册中心,用来管理注册上来的各个微服务。
搭建 nacos 环境
第 1 步: 安装 nacos
下载地址: https://github.com/alibaba/nacos/releases
下载 zip 格式的安装包,然后进行解压缩操作
第 2 步: 启动 nacos
#切换目录
cd nacos/bin#命令启动
startup.cmd -m standalone
第 3 步: 访问 nacos
打开浏览器输入 http://localhost:8848/nacos,即可访问服务, 默认密码是nacos/nacos
将商品微服务注册到 nacos
接下来开始修改 shop-product 模块的代码, 将其注册到 nacos 服务上
1 在 pom.xml 中添加 nacos 的依赖
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
2 在主类上添加@EnableDiscoveryClient 注解
3 在 application.yml 中添加 nacos 服务的地址
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
4 启动服务, 观察 nacos 的控制面板中是否有注册上来的商品微服务
将订单微服务注册到 nacos(略)
服务调用
@Autowired
DiscoveryClient discoveryClient;
//从 nacos 中获取服务地址
ServiceInstance serviceInstance =
discoveryClient.getInstances("service-product").get(0);
String url = serviceInstance.getHost() + ":" +
serviceInstance.getPort();
//使用
Product p = restTemplate.getForObject( "http://" + url + "/product/" +
pid, Product.class);
服务调用负载均衡
什么是负载均衡
通俗的讲, 负载均衡就是将负载(工作任务,访问请求)进行分摊到多个操作单元(服务器,组件)上进行执行。
自定义实现负载均衡
通过修改端口启动两个商品服务
可以将获取服务的方式改为随机获取
//获取服务列表
List instances =
discoveryClient.getInstances("service-product");
//随机生成索引
Integer index = new Random().nextInt(instances.size());
//获取服务
ServiceInstance productService = instances.get(index);
//获取服务地址
String purl = productService.getHost() + ":" +
productService.getPort();
基于 Ribbon 实现负载均衡
Ribbon 是 Spring Cloud 的一个组件, 它可以让我们使用一个注解就能轻松的
搞定负载均衡
第 1 步:在 RestTemplate 的生成方法上添加@LoadBalanced 注解
第 2 步:修改服务调用的方法
restTemplate .getForObject( "http:// 服务名 /user/findUser/" +uid, User . class );
Ribbon 支持的负载均衡策略 Ribbon 内置了多种负载均衡策略,内部负载均衡的顶级接口为 com.netflix.loadbalancer.IRule , 具体的负载策略:
七种负载均衡策略
轮询策略:RoundRobinRule,按照一定的顺序依次调用服务实例。比如一共有 3 个服务,第一次调用服务 1,第二次调用服务 2,第三次调用服务 3,依次类推。
NFLoadBalancerRuleClassName : com.netflix.loadbalancer.RoundRobinRule
权重策略:WeightedResponseTimeRule,根据每个服务提供者的响应时间分配一个权重,响应时间越长,权重越小,被选中的可能性也就越低。它的实现原理是,刚开始使用轮询策略并开启一个计时器,每一段时间收集一次所有服务提供者的平均响应时间,然后再给每个服务提供者附上一个权重,权重越高被选中的概率也越大。
NFLoadBalancerRuleClassName :com.netflix.loadbalancer.WeightedResponseTimeRule
随机策略:RandomRule,从服务提供者的列表中随机选择一个服务实例。
NFLoadBalancerRuleClassName : com.netflix.loadbalancer.RandomRule
最小连接数策略:BestAvailableRule,也叫最小并发数策略,它是遍历服务提供者列表,选取连接数最小的⼀个服务实例。如果有相同的最小连接数,那么会调用轮询策略进行选取。
NFLoadBalancerRuleClassName : com.netflix.loadbalancer.BestAvailableRule
重试策略:RetryRule,按照轮询策略来获取服务,如果获取的服务实例为null 或已经失效,则在指定的时间之内不断地进行重试来获取服务,如果超过指定时间依然没获取到服务实例则返回 null。
ribbon :
ConnectTimeout : 2000 # 请求连接的超时时间
ReadTimeout : 5000 # 请求处理的超时时间
service-product : # 调用的提供者的名称
ribbon :
NFLoadBalancerRuleClassName :com.netflix.loadbalancer.RandomRule
可用敏感性策略:AvailabilityFilteringRule,先过滤掉非健康的服务实例,然后再选择连接数较小的服务实例。
NFLoadBalancerRuleClassName : com.netflix.loadbalancer.AvailabilityFilteringRule
区域敏感策略: ZoneAvoidanceRule,根据服务所在区域(zone)的性能和服务的可用性来选择服务实例,在没有区域的环境下,该策略和轮询策略类似。
NFLoadBalancerRuleClassName : com.netflix.loadbalancer.ZoneAvoidanceRule
我们可以通过修改配置来调整 Ribbon 的负载均衡策略,具体代码如下
service-product : # nacos 中的服务 id
ribbon :
NFLoadBalancerRuleClassName : com.netflix.loadbalancer.RandomRule
基于 Feign 实现服务调用
什么是 Feign
Feign 是 Spring Cloud 提供的一个声明式的伪 Http 客户端, 它使得调用远程服务就像调用本地服务 一样简单, 只需要创建一个接口并添加一个注解即可。
Nacos 很好的兼容了 Feign, Feign 默认集成了 Ribbon, 所以在 Nacos 下使用 Fegin 默认就实现了负 载均衡的效果。
Feign 的使用
1.加入 Fegin 的依赖
org.springframework.cloud
spring-cloud-starter-openfeign
2. 在主类上添加 Fegin 的注解
@EnableFeignClients//开启 Fegin
3. 创建一个 service, 并使用 Fegin 实现微服务调用
4. 修改 controller 代码,并启动验证
@Autowired
ProductService productService;//注入接口代理对象
Product p = productService.findProduct(pid);//调用
Sentinel--服务容错
高并发带来的问题
在微服务架构中,我们将业务拆分成一个个的服务,服务与服务之间可以相互调用,但是由于网络 原因或者自身的原因,服务并不能保证服务的 100%可用,如果单个服务出现问题,调用这个服务就会 出现网络延迟,此时若有大量的网络涌入,会形成任务堆积,最终导致服务瘫痪。
模拟一个高并发的场景
1.在方法中添加线程休眠,再添加一个测试接口
@RequestMapping(path = "/createOrder/{pid}/{uid}/{number}")
public Order createOrder(@PathVariable Integer pid,
@PathVariable Integer uid,@PathVariable Integer number){
//使用 feign 实现服务调用
Product p = productService.findProduct(pid);//代码风格统一
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Order order=null;
if(p!=null&&u!=null&&p.getStock()>0){
order = orderService.createOrder(pid,uid,number);
}
return order;
}
//测试接口
@GetMapping(path = "/message")
public String message(){
return "测试高并发";
}
2.修改配置文件中 tomcat 的并发数
server:
port: 8071
tomcat:
max-threads: 10
3.安装 Apache Jmeter 用压测工具,对请求进行压力测试
3.1 下载 解压
3.2 修改配置,并启动软件
进入 bin 目录,修改 jmeter.properties 文件中的语言支持为 language=zh_CN,
然后点击 jmeter.bat 启动软件。
3.3 添加线程组
3.4 添加 http 取样
添加查看结果树
访问 message 接口,观察访问结果.
结论: 此时会发现, 由于 order 方法囤积了大量请求, 导致message 方法的访问出现了问题,这就是服务雪崩的雏形。
服务雪崩效应
在分布式系统中,由于网络原因或自身的原因,服务一般无法保证 100% 可用。如果一个服务出现了问题,调用这个服务就会出现线程阻塞的情况,此时若有大量的请求涌入,就会出现多条线程阻塞等待,进而导致服务瘫痪。
由于服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的 “雪崩效应”。
雪崩发生的原因多种多样,有不合理的容量设计,或者是高并发下某一个方法响应变慢,亦或是某台机器的资源耗尽。我们无法完全杜绝雪崩源头的发生,只有做好足够的容错,保证在一个服务发生问题,不会影响到其它服务的正常运行。也就是"雪落而不雪崩"。
常见容错方案
要防止雪崩的扩散,我们就要做好服务的容错,容错说白了就是保护自己不被猪队友拖垮的一些措施, 下面介绍常见的服务容错思路和组件。
常见的容错思路有: 隔离、超时、限流、熔断、降级这几种,下面分别介绍一下。
隔离
它是指将系统按照一定的原则划分为若干个服务模块,各个模块之间相对独立,无强依赖。当有故障发生时,能将问题和影响隔离在某个模块内部,而不扩散风险,不波及其它模块,不影响整体的系统服务。常见的隔离方式有:线程池隔离和信号量隔离.
超时
在上游服务调用下游服务的时候,设置一个最大响应时间,如果超过这个时间,下游未作出反应, 就断开请求,释放掉线程。
限流
限流就是限制系统的输入和输出流量已达到保护系统的目的。为了保证系统的稳固运行,一旦达到的需要限制的阈值,就需要限制流量并采取少量措施以完成限制流量的目的。
熔断
在互联网系统中,当下游服务因访问压力过大而响应变慢或失败,上游服务为了保护系统整体的可用性,可以暂时切断对下游服务的调用。这种牺牲局部,保全整体的措施就叫做熔断。
服务熔断一般有三种状态:
熔断关闭状态(Closed)
服务没有故障时,熔断器所处的状态,对调用方的调用不做任何限制
熔断开启状态(Open)
后续对该服务接口的调用不再经过网络,直接执行本地的 fallback 方法
半熔断状态(Half-Open)
尝试恢复服务调用,允许有限的流量调用该服务,并监控调用成功率。如果成功率达到预期,则说明服务已恢复,进入熔断关闭状态;如果成功率仍旧很低,则重新进入熔断关闭状态。
降级
降级其实就是为服务提供一个托底方案,一旦服务无法正常调用,就使用托底方案。
常见的容错组件
Hystrix
Hystrix 是由 Netflflix 开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。
Resilience4J
Resilicence4J 一款非常轻量、简单,并且文档非常清晰、丰富的熔断工具,这也是 Hystrix 官方推荐的替代产品。不仅如此,Resilicence4j 还原生支持SpringBoot,而且监控也支持和 prometheus 等多款主流产品进行整合。
Sentinel
Sentinel 是阿里巴巴开源的一款断路器实现,本身在阿里内部已经被大规模采用,非常稳定。