项目技术栈图:
1. springcloud是什么?为什么要用?优缺点是什么?
1.1 是什么
Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、智能路由、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包
1.2 设计目标
协调各个微服务,简化分布式系统开发。
微服务的框架那么多比如:dubbo、Kubernetes,为什么就要使用Spring Cloud的呢?
1.3 优点:
- 产出于Spring大家族,Spring在企业级开发框架中无人能敌,来头很大,可以保证后续的更新、完善
- 组件丰富,功能齐全。Spring Cloud 为微服务架构提供了非常完整的支持。例如、配置管理、服务发现、断路器、微服务网关等;
- Spring Cloud 社区活跃度很高,教程很丰富,遇到问题很容易找到解决方案
- 服务拆分粒度更细,耦合度比较低,有利于资源重复利用,有利于提高开发效率
- 可以更精准的制定优化服务方案,提高系统的可维护性
- 减轻团队的成本,可以并行开发,不用关注其他人怎么开发,先关注自己的开发
- 微服务可以是跨平台的,可以用任何一种语言开发
- 适于互联网时代,产品迭代周期更短
1.4 缺点:
- 微服务过多,治理成本高,不利于维护系统
- 分布式系统开发的成本高(容错,分布式事务等)对团队挑战大
2. springcloud和dubbo的区别:
- 服务调用方式 dubbo是RPC springcloud Rest Api
- 注册中心,dubbo 是zookeeper springcloud是eureka,也可以是zookeeper
- 服务网关,dubbo本身没有实现,只能通过其他第三方技术整合,springcloud有Zuul路由网关,作为路由服务器,进行消费者的请求分发,springcloud支持断路器,与git完美集成配置文件支持版本控制,事物总线实现配置文件的更新与服务自动装配等等一系列的微服务架构要素。
3. 使用springcloud可能遇到的问题:
- 与分布式系统相关的复杂性-这种开销包括网络问题,延迟开销,带宽问题,安全问题。
- 服务发现-服务发现工具管理群集中的流程和服务如何查找和互相交谈。它涉及一个服务目录,在该目录中注册服务,然后能够查找并连接到该目录中的服务。
- 冗余-分布式系统中的冗余问题。
- 负载平衡 --负载平衡改善跨多个计算资源的工作负荷,诸如计算机,计算机集群,网络链路,中央处理单元,或磁盘驱动器的分布。
- 性能-问题 由于各种运营开销导致的性能问题。
- 部署复杂性-Devops 技能的要求。
4. CAP是什么:
- C:强一致性
- A:可用性
- P:分区容错性
5. springcloud有哪些项目?
- 服务注册与发现:
- euraka – 弃用
- zookeeper
- consul
- nacos – 国内用的最多,alibaba
- 服务调用
- feign – 弃用
- openfeign
- 负载均衡
- ribbon
- loadbalancer
- 服务降级
- hystrix – 弃用
- sentienl – alibaba
- 网关
- gateway
- zuul – 弃用
- zuul2
- 服务配置
- nacos
- config – 弃用
- 分布式事务
- seata
- 消息总线:
- bus
- nacos
6. 服务注册与发现:
目前国内主流的是nacos,eureka几乎已启用。
6.1 Eureka:注册中心
6.1.1 Eureka的两个组件:Eureka Server和Eureka Client
6.1.2 Eureka集群:
防止注册中心崩了,整个服务都崩了。
eureka之间相互注册,相互守望。
6.1.3 Eureka的自我保护
1. 心跳:
eureka会通过心跳来判断某个微服务是不是正常,如果某个eureakServer端在固定时间内没有收到某个微服务的心跳包,便会将该服务从服务注册列表中剔除。
2. 自我保护:
6.2 Nacos:
- Nacos = Eureka + Config + Bus。
- 替代Eureka做服务注册中心,替代Config做服务配置中心
- Nacos可以支持AP和CP的切换
我项目中主要用nacos来作服务发现,同时用作服务配置中心
6.2.1 作为注册中心:
配置:
server:
port: 9001
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
server-addr: 10.211.55.17:8848 #配置的Nacos地址(本机的写localhost:8848,服务器的写IP地址)
同时得在主启动类添加 @EnableDiscoveryClient
注解
6.2.2 作为配置中心:
配置:
- bootstrap中:
server:
port: 3377
spring:
application:
name: nacos-config-client
cloud:
nacos:
discovery:
server-addr: 10.211.55.17:8848 #Nacos服务注册中心地址(本机的写localhost)
config:
server-addr: 10.211.55.17:8848 #Nacos作为配置中心地址(本机的写localhost)
file-extension: yml #指定yml格式配置
- application.yml中
spring:
profiles:
active: dev #表示开发环境
7. 负载均衡:
7.1 什么是负载均衡
- 集中式LB:
即服务的调用方和提供方之间使用独立的LB设施,由该设施把访问请求通过某种策略转发至服务的提供方。 - 进程内LB:
将LB逻辑集成到消费方,消费方从服务注册中心获知哪些服务可用,然后自己从这些服务中选择一个服务。(Ribbon)
7.2 Ribbon实现负载均衡
- Ribbon其实就是一个软负载均衡的客户端组件,它可以和其他所需请求的客户端结合使用,和eureka结合只是其中的一个实例。
- 主要实现服务间调用的负载均衡算法。
- eureka、feign在内部集成了ribbon
8. 服务调用: Feign和OpenFeign
对于Feign的使用,我的理解:
-
在没有微服务的时候,我自己的项目都是使用HttpClient来发送请求,
-
在后来有了微服务后,我们使用RestTemplate对请求地址做封装处理,然后使用RestTemplate来发送请求
-
有了feign之后,我们可以使用feign定义一个接口,来实现请求的发送,服务的调用。
-
feign可以对服务调用的超时时间进行设置
-
可以对feign接口的调用情况进行监控和输出
-
feign集成了ribbon实现了负载均衡
8.1 feign和openfeign的区别
8.2 feign的超时设计
默认Feign客户端只等待一秒钟,但是服务端处理需要超过一秒钟,导致Feign客户端不想等待了,直接返回报错,为了避免这样的情况,有时候我们需要设置Feign客户端的超时控制。
注意feign的默认超时时间,我刚开始用feign的时候因为这个问题浪费了我一天时间
8.3 feign的熔断和服务降级
feign的熔断和降级支持hystrix和sentinel两种方式,默认使用的是hystrix。
可以在配置文件中设置sentinel.enable=true来开启
9. 服务降级:
9.1分布式面临的问题:
复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败。
处理方式:服务降级。
9.1.1 服务降级:
假如服务不能使用了,我们可以进行一个友好的提示,不让用户等待太长的时间。
哪些情况会导致服务降级:
- 程序运行异常
- 超时
- 服务熔断触发服务降级
- 线程池/信号量打满也会导致服务降级
我对服务降级的理解:
当双11活动时,把无关交易的服务统统降级,如查看蚂蚁深林,查看历史订单,商品历史评论,只显示最后100条等等。把主要的流量放到双11主战场,其他的无关服务通通降级。
9.1.2 服务熔断:
9.1.3 服务限流:
秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行。
9.1.4 熔断和限流的区别:
熔断的目的是当A服务模块中的某块程序出现故障后为了不影响其他客户端的请求而做出的及时回应。
降级的目的是为了解决整体项目的压力,而牺牲掉某一服务模块而采取的措施。
他们相似的地方:
- 目的很一致,都是从可用性可靠性着想,为防止系统的整体缓慢甚至崩溃,采用的技术手段;
- 最终表现类似,对于两者来说,最终让用户体验到的是某些功能暂时不可达或不可用;
- 粒度一般都是服务级别,当然,业界也有不少更细粒度的做法,比如做到数据持久层(允许查询,不允许增删改);
- 自治性要求很高,熔断模式一般都是服务基于策略的自动触发,降级虽说可人工干预,但在微服务架构下,完全靠人显然不可能,开关预置、配置中心都是必要手段;
区别的地方:
- 触发原因不太一样,服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑;
- 管理目标的层次不太一样,熔断其实是一个框架级的处理,每个微服务都需要(无层级之分),而降级一般需要对业务有层级之分(比如降级一般是从最外围服务开始)
9.2 Hystrix
9.2.1 概述:
我的理解:在发生服务雪崩等异常情况时,通过Hystrix来实现服务降级和服务熔断:停止某个服务的使用,如果有请求访问此服务,返回一个友好的提示。
9.2.2 用法:
主要注解:HystrixCommand
- 在会出现问题的方法上添加@HystrixCommand注解,并给其添加兜底方法,并且设置此方法的超时时间。
// 超时访问,演示降级
// fallbackMethod为兜底的方法,
// HystrixProperty为设置线程超时时间
@HystrixCommand(fallbackMethod = "paymentInfo_TimeoutHandler",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "5000")
})
public String paymentInfo_TimeOut(Integer id) {
int a = 5/0;
// TimeUnit.SECONDS.sleep(6);
return "接口调用正常";
}
// 兜底方法
public String paymentInfo_TimeoutHandler(Integer id) {
return "/(ToT)/调用支付接口超时或异常、\t" + "\t当前线程池名字" + Thread.currentThread().getName();
}
- 在主启动类上添加
@EnableCircuitBreaker
注解
9.3 sentinel:
-
用于代替Hystrix,
-
有自己的客户端
-
可以进行各种各样的降级规则配置
- 基于异常比例
- 基于平均响应时间
- 基于异常数
-
配置的限流、熔断的一些规则可以进行持久化。
-
主要注解:SentinelResource
yml中配置示例:
spring:
application:
name: cloudalibaba-sentinal-service
cloud:
nacos:
discovery:
#Nacos服务注册中心地址(改成自己的服务器ip地址,本地用localhost)
server-addr: 10.211.55.26:8848
sentinel:
transport:
#配置Sentin dashboard地址(改成自己的服务器ip地址,本地用localhost)
dashboard: 10.211.55.26:8858
# 默认8719端口,假如被占用了会自动从8719端口+1进行扫描,直到找到未被占用的 端口
port: 8719
9.4 我项目中的使用
在feign调用中添加回调函数:
@Component
@FeignClient(value = ServerNameConstant.INTEGRITY_MOBILE, contextId = "integralServiceClient", fallbackFactory = IntegralServiceFallback.class)
public interface IntegralFeignService {
@PostMapping("/integral/questionnaire")
void questionnaireScore(@RequestBody UserDetailInfo userInfo);
}
@Slf4j
@Fallback
public class IntegralServiceFallback implements FallbackFactory<IntegralFeignService> {
@Override
public IntegralFeignService create(Throwable throwable) {
return new IntegralFeignService() {
@Override
public void questionnaireScore(UserDetailInfo userInfo) {
}
};
}
}
10. gateway网关:
基于webflux框架实现的异步非阻塞架构,而webflux框架底层则使用了高性能的Reactor模式通信框架Netty。
应用位置:
10.1 gateway可以做什么?(路由和过滤)
- 反向代理
- 鉴权
- 流量控制
- 熔断
- 日志监控
- 。。。
10.2 gateway三大核心概念
- Route路由:路由时构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由
- Predicate断言:开发人员可以匹配HTTP请求中的素有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由
- Filter过滤:指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求倍路由前或者之后对请求进行修改
web请求,通过一些匹配条件,定位到真正的服务节点。并在这个转发过程的前后,进行一些精细化控制。predicate就是我们的匹配条件;而filter,就可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标uri,就可以实现一个具体的路由了。
10.3 工作流程:
10.4 uri的三种配置方式:
- 第一种:ws(websocket)方式:
uri: ws://localhost:9000
- 第二种:http方式:
uri: http://localhost:8130/
- 第三种:lb(注册中心中服务名字)方式:
uri: lb://brilliance-consumer
10.4 示例配置:
spring:
application:
name: cloud-gateway
cloud:
gateway:
routes: # 路由
- id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
#匹配后提供服务的路由地址
uri: http://localhost:8001
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
- id: payment_route2
uri: http://localhost:8001
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
通过微服务名实现动态路由:
默认情况下Gateway会根据注册中心注册的服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能。
cloud:
gateway:
globalcors:
corsConfigurations:
'[/**]':
allowedOrigins: "*"
allowedMethods: "*"
allowedHeaders: "*"
allowCredentials: false
routes:
- id: CREA-Auth-Social
uri: lb://CREA-Auth # 匹配后提供服务的地址
predicates:
- Path=/auth/social/** # 断言,路径相匹配的进行路由
filters:
- name: CircuitBreaker # CircuitBreaker类型断路器
- id: CREA-Auth
uri: lb://CREA-Auth
predicates:
- Path=/auth/**
# filters:
# - name: Hystrix
# args:
# name: attentionfallback
# fallbackUri: forward:/fallback/CREA-Auth
11. 服务配置:config(弃)
分布式系统面临的问题:
- 配置文件太多
- 需要修改配置文件时,需要修改的配置文件太多
解决方式:使用config进行统一的配置文件管理
nacos同样可以用作服务配置,我项目中使用的就是nacos。
12. 消息总线:Bus:
13. 分布式事务:seata:
是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。
13.1 一个典型的分布式事务过程:
分布式事务处理过程的1ID+三组件模型:
- 1ID:Transaction ID XID :全局唯一的事务ID
- 三组件:
- TC (Transaction Coordinator) - 事务协调者:维护全局和分支事务的状态,驱动全局事务提交或回滚。
- TM (Transaction Manager) - 事务管理器:定义全局事务的范围:开始全局事务、提交或回滚全局事务。
- RM (Resource Manager) - 资源管理器:管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
处理过程:
13.2 seata原理:
TC,TM,RM
- TC:相当于seata服务器
- TM:添加了@GlobalTransactional注解的方法,事物的发起方
- RM:事务的参与方
执行流程:
14. 消息驱动:stream
14.1 是什么:
屏蔽底层消息中间件的差异,降低切换成本,统一消息的编程模型
14.2 为什么要是用stream
不同的mq架构设计不同,如果以后我们需要修改mq,代码开发工作量巨大,使用stream就给我们一种解耦合的方式
14.3 stream如何做到统一底层差异:
14.4 常用注解:
14.5 示例:
配置:
cloud:
stream:
binders: #在此处配置要绑定的rabbitmq的服务信息
defaultRabbit: #表示定义的名称,用于binding整合
type: rabbit #消息组件类型
environment: #设置rabbitmq的相关环境配置
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
bindings: #服务的整合处理
output: #这个名字是一个通道的名称
destination: studyExchange #表示要使用的Exchange名称定义
content-type: application/json #设置消息类型,本次为json,本文要设置为“text/plain”
binder: defaultRabbit #设置要绑定的消息服务的具体设置
主要代码:
发送:
// 不需要@service注解
@EnableBinding(Source.class) //定义消息的推送管道(Source是spring的)
public class IMessageProviderImpl implements IMessageProvider {
@Resource
private MessageChannel output; //消息发送管道
@Override
public String send() {
String serial = UUID.randomUUID().toString();
output.send(MessageBuilder.withPayload(serial).build()); //MessageBuilder是spring的integration.support.MessageBuilder
System.out.println("*******serial: " + serial);
return null;
}
}
接收:
@EnableBinding(Sink.class)
@Controller
public class ReceiveMessageListenerController {
@Value("${server.port}")
private String serverPort;
@StreamListener(Sink.INPUT) //监听
public void input(Message<String> message){
System.out.println("消费者1号------>收到的消息:" + message.getPayload() + "\t port:" + serverPort);
}
}
14.6 消息重复消费问题:
- 问题:
- 原因:默认分组group是不同的,组流水号不一样,被认为不同组,可以消费。
- 解决:在配置文件中设置group
12. 服务注册和发现是什么意思?Spring cloud如何实现服务注册和发现?
服务注册组件有eureka、nacos、zookeeper。
我们想项目中使用的为nacos。
12.1 eureka(nacos)的作用:
- 两个服务order和user。
- 在启动的时候,order和user都会把自己注册到注册中心中
- order想远程访问user的数据,那就需要拿到user的ip和端口。
- order拉取user的注册信息
- 通过负载均衡后,拿到了8081这个端口的服务,就访问这个服务的数据
- 如果user中的某个服务宕机了,euraka心跳机制30秒没有收到心跳返回,就将其中服务列表中删除
12.2 问题回答:
12.3 nacos的工作流程:
主要是临时实例和非临时实例的区别:
- 临时实例(默认都是临时):采用心跳机制,每过多少秒高速注册中心我还活着
- 非临时实例:nacos主动询问是否活着,如果挂了,就给消费者主动推送
12.4 nacos和euraka的区别:
13. 你们项目负载均衡是如何实现的?
13.1 ribbon负载均衡流程:
13.2 ribbon负载均衡策略:
主要有:轮询、权重、随机
13.3 负载均衡策略的实现:
13.4 问题回答:
14. 服务雪崩:
14.1 服务降级
即如果一个服务的接口出现问题了,返回一个友好的提示。
通常通过feign进行配置:
14.2 服务熔断:
14.3 问题回答:
15. 微服务的监控:
我们项目中用的springboot-admin。
15.1 skywalking:
15.1.1 慢接口追踪:
可以看到是哪里(在哪个服务中)很慢
15.1.2 服务关系查看:
15.1.3 服务告警:
15.2 问题回答:
16. 限流:
16.1 Nginx限流:
使用的是漏桶算法:
16.1.1 控制速率:
16.1.2 控制并发:
16.2 网关限流:
16.3 问题回答:
17. CAP和BASE
17.1 CAP:
17.1.1 一致性:
17.1.2 可用性:
17.1.3 分区容错:
17.2 BASE:
17.3 问题回答:
18. 你们采用的哪种分布式事务框架?
18.1 seata:
18.1.1 seata的三个角色:
seata支持三种工作模式:
18.1.2 seata的XA模式:
18.1.3 AT模式(推荐的):
18.1.4 TCC模式:
18.2 MQ分布式事务
如果强一致性不高,可以使用这种方式。
18.3 问题回答:
19. 分布式服务的接口欧幂等性如何设计?
19.1 接口幂等
19.1.1 token+redis:
多次点击提交订单,此时因为第一次提交已经删除了token,所以后面的操作就获取不到这个token,就会直接返回。保证了只有一个请求执行:
19.1.2 分布式锁:
如果拿到锁,再执行,拿不到就等待(或者快速失败)。
这里可以控制锁的粒度。