文章目录
更多相关内容可查看
SpringBoot和SpringCloud什么区别 ?
springboot和springcloud的区别主要是:
- 作用不同;springboot的作用是为了提供一个默认配置,从而简化配置过程;springcloud的作用是为了给微服务提供一个综合管理框架
- 使用方式不同;springboot可以单独使用;springcloud必须在springboot使用的前提下才能使用。
springboot的设计目的是为了在微服务开发过程中可以简化配置文件,提高工作效率,可以认为是一个脚手架工具 , 帮助我们快速的搭建基于spring的项目
SpringCloud就是利用SpringBoot将各种微服务项目中的解决方案封装成starter , 提供默认配置 , 让我们在项目开发过程中能够方便的使用各种组件
你们项目为什么要使用微服务
传统的大型单体应用程序在部署和运行时,需要单台服务器具有大量内存和其他资源。大型的单体应用必须通过在多个服务器上复制整个应用程序来实现横向扩展,因此其扩展能力极差;此外,这些应用程序往往更复杂,各个功能组件紧耦合,使得维护和更新非常困难。在这种情况下,想单独升级应用的一个功能组件,就会牵一发而动全身 , 某一个功能升级了可能会导致其他功能又出现故障 , 项目运行过程中某一个地方出现了bug可能会导致整个应用崩溃
在微服务架构中,传统的大型单体应用被拆分为小型模块化的服务,每项服务都围绕特定的业务领域构建,不同微服务可以用不同的编程语言编写,甚至可以使用完全不同的工具进行管理和部署 , 与单体应用程序相比,微服务组织更好、更小、更松耦合,并且是独立开发、测试和部署的。由于微服务可以独立发布,因此修复错误或添加新功能所需的时间要短得多,并且可以更有效地将更改部署到生产中。此外,由于微服务很小且无状态,因此更容易扩展 !
但是微服务也有问题 , 微服务最致命的地方在于成本和管理问题。一个系统由多个微服务组成,多个微服务之间的系统整合,团队间合作和沟通难度,是呈指数级增长的。项目管理和部署的难度也非常高 , 需要考虑到很多东西 , 而且因为服务比较多 , 需要的服务器成本也比较高
所以具体要不要搞微服务, 还是得仔细的分析和思考 , 权衡之后再决定 , 我们的项目中因为面向的是整个互联网用户 , 考虑到后期运营比较好的情况下可能会出现的用户增长 , 流量增长 , 以及不断的版本迭代 , 方便后期扩展和功能迭代所以选择了微服务架构
Spring Cloud 5大组件有哪些?👍
Spring Cloud五大组件主要是
SpirngCloud alibaba SpirngCloud Netflix
- 注册中心组件 , 例如 : Nacos和 Eureka
- 负载均衡组件 , 例如 : Spring Cloud LoadBalancer 和 Ribbon
- 远程调用组件 , 例如 : Dubbo 和 Feign
- 服务熔断组件 , 例如 : Sentinel和Hystrix
- 服务网关组件 , 例如 : Spring Cloud Gateway 和zuel
什么是微服务?微服务的优缺点是什么?
微服务就是一个独立的 , 职责单一的服务应用程序,一般在项目开发过程中会按照业务和需求将项目拆分成N个微服务
1.优点:松耦合,聚焦单一业务功能,无关开发语言,团队规模降低 , 扩展性好, 天然支持分库
2.缺点:随着服务数量增加,管理复杂,部署复杂,服务器需要增多,服务通信和调用压力增大
你们项目中微服务之间是如何通讯的? 👍
服务的通讯方式主要有二种 :
1.同步通信:
通过Feign发送http请求调用
原理:Feign通过动态代理技术生成一个InvocationHandler的实现类,根据接口的注解构建HTTP请求。同时,Feign还负责处理响应数据、异常等,并将结果返回给调用方
通过Dubbo发送RPC请求调用
原理:Dubbo使用动态代理技术生成一个InvocationHandler的实现类,然后在RPC发送请求之前会将请求数据(序列化请求参数,调用的服务发放等)根据特定协议进行封装,当代理对象的方法被调用时,就是发起RPC调用。然后使用Netty将封装好的RPC请求发送
2.异步通信:使用消息队列进行服务调用,如RabbitMQ、KafKa等
服务注册和发现是什么意思?Spring Cloud 如何实现服务注册发现?👍
各种注册中心组件的原理和流程其实大体上类似 , 核心的功能就一下几个 :
- 服务注册 : 服务启动的时候会将服务的信息注册到注册中心, 比如: 服务名称 , 服务的IP , 端口号等
- 服务发现 : 服务调用方调用服务的时候, 根据服务名称从注册中心拉取服务列表 , 然后根据负载均衡策略 , 选择一个服务, 获取服务的IP和端口号, 发起远程调用
- 服务状态监控 : 服务提供者会定时向注册中心发送心跳 , 注册中心也会主动向服务提供者发送心跳探测, 如果长时间没有接收到心跳, 就将服务实例从注册中心下线或者移除
你们项目中使用的注册中心是什么 ? 有没有了解过原理 ?
我们项目中注册中心用的是Nacos , 基本上所有的注册中心的核心功能都包括服务注册 , 服务发现, 服务状态监控 , 他的核心原理如下 :
- 服务注册:客户端启动时将服务信息封装为Instance对象,然后创建定时任务向Nacos服务器注册服务,包括IP、端口号、服务名、分组名、集群名等信息。
- 心跳检测:客户端定时向Nacos服务器发送心跳请求,服务器接收到后检查服务列表,如果没有该实例则重新注册,同时更新实例的最后心跳时间。如果实例非健康状态则改为健康状态。另外,还有定时任务检查实例在线状态,根据心跳时间设置健康状态,超过一定时间删除实例。
- 服务注册表管理:通过CopyOnWrite机制防止并发读写冲突,更新服务注册表时先拷贝一份数据,更新完成后再替换原注册表。更新完成后,通过UDP通信发布服务变化事件,通知客户端更新。
- 服务发现:客户端定时从服务端拉取服务数据保存在本地缓存,服务端发生心跳检测、服务列表变更或健康状态改变时会触发推送事件,通过UDP通信将服务列表推送到客户端,并定时推送数据到客户端。
- 负载均衡:使用Spring Cloud Ribbon组件实现负载均衡,客户端调用一般通过网关,Ribbon拦截RestTemplate请求,根据服务名称获取服务列表,并根据负载均衡策略选择服务实例。常见的负载均衡策略有轮询(RoundRobinRule)和可用性过滤(AvailabilityFilteringRule)等。
你们项目负载均衡如何实现的 ? 👍
服务调用过程中的负载均衡一般使用SpringCloud的Ribbon 组件实现 , Feign的底层已经自动集成了Ribbon , 使用起来非常简单 , 客户端调用的话一般会通过网关, 通过网关实现请求的路由和负载均衡
RIbbon负载均衡原理 :
SpringCloudRibbon的底层采用了一个拦截器,拦截了RestTemplate发出的请求,对地址做了修改。
基本流程如下:
LoadBalancerInterceptor
拦截RestTemplate请求- 调用
RibbonLoadBalancerClient
从请求url中获取服务名称 - 调用
DynamicServerListLoadBalancer
根据服务名称到注册中心拉取服务列表 , 注册中心返回列表 DynamicServerListLoadBalancer
调用IRule使用配置的负载均衡策略负载均衡规则,从服务列表中选择一个服务实例RibbonLoadBalancerClient
用服务实例的IP和端口替换请求路径中的服务名称- 向服务实例发起http请求
Ribbon负载均衡策略有哪些 👍
Ribbon默认的负载均衡策略有七种 :
内置负载均衡规则类 | 规则描述 |
---|---|
RoundRobinRule | 简单轮询服务列表来选择服务器。它是Ribbon默认的负载均衡规则。 |
AvailabilityFilteringRule | 对以下两种服务器进行忽略: |
- 在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态
- 并发数过高的服务器 , 会被设置为短路状态 , 会被忽略
|
| WeightedResponseTimeRule | 为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小 |
| ZoneAvoidanceRule(默认) | 以区域可用的服务器为基础进行服务器的选择。而后再对区域内的多个服务做轮询。 |
| BestAvailableRule | 忽略那些短路的服务器,并选择并发数较低的服务器。 |
| RandomRule | 随机选择一个可用的服务器。 |
| RetryRule | 重试机制的选择逻辑 |
**如果想要自定义负载均衡 , 可以自己创建类实现IRule接口 , 然后再通过配置类或者配置文件配置即可 : **
通过定义IRule实现可以修改负载均衡规则,有两种方式:
**代码方式:**通过配置类, 配置一个IRule
接口的实现类 , 这是一种通用配置 , 使用于所有的服务调用
@Bean
public IRule randomRule(){
return new RandomRule();
}
**配置文件方式:**在配置文件中配置负载均衡规则
userservice: # 给某个微服务配置负载均衡规则,这里是userservice服务
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则
springcloud Gateway的底层原理:
基于Reactor Netty的底层服务实现,允许网关处理大量并发请求而不阻塞线程,通过过滤器链来实现请求的预处理和后处理(对请求、响应进行修改,校验及日志记录等),通过路由的配置规则(基于请求的路径,方法,参数)转发到服务端
你们项目的配置文件是怎么管理的 ? 👍
大部分的固定的配置文件都放在服务本地 , 一些根据环境不同可能会变化的部分, 放到配置中心中管理 , 我们项目使用Nacos配置中心进行配置的统一管理 , 包括 : 开发环境配置 , 测试环境配置 和 生产环境配置
你们项目中有没有做过限流 ? 怎么做的 ? 👍
限流其实在整个请求的处理流程中都需要做
- 客户端发送请求到Nginx需要进行限流 .
- 通过Nginx负载均衡请求到网关 , 网关也需要限流
- 网关路由请求到微服务 , 微服务也需要限流
每一层所使用的限流方案也不一样 :
- nginx层主要是对请求的IP进行限流 , 使用的是
limit_req_zone
和limit_req
配置 , 底层使用的是漏桶算法实现的 , nginx层限流主要是对下游的网关起到保护作用
http {
limit_req_zone $binary_remote_addr zone=iplimit:10m rate=1r/s;
limit_req_zone $server_name zone=iplimit:10m rate=1r/s;
server {
server_name www.nginx-lyntest.com;
listen 80;
location ^~/my-api/ {
proxy_pass http://127.0.0.1:9999/;
limit_req zone=iplimit burst=20 nodelay;
limit_req_status 429; # 默认返回 http 503状态码
limit_req_log_level warn; # 默认为 error级别
}
}
}
- 网关层限流主要使用的是Spring Cloud Gateway提供
Request Rate Limiting
过滤器实现的 , 底层使用Redis基于令牌桶算法实现限流 , 网关限流主要是对下游的微服务系统起到保护作用
server:
port: 8081
spring:
application:
name: gateway-limiter
redis:
host: localhost
port: 6379
database: 0
cloud:
gateway:
routes:
- id: limit_route
uri: http://httpbin.org:80/get
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
filters:
- name: RequestRateLimiter
args:
# key-resolver,用于限流的键的解析器的 Bean 对象的名字
key-resolver: "#{@hostAddrKeyResolver}"
redis-rate-limiter.replenishRate: 1 # 令牌桶填充的速率 秒为单位
redis-rate-limiter.burstCapacity: 1 # 令牌桶总容量
redis-rate-limiter.requestedTokens: 1 # 每次请求获取的令牌数
@Component
public class HostAddrKeyResolver implements KeyResolver {
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
}
}
- 微服务限流的目的主要是为了保护微服务本身不被大流量冲垮 , 可以使用Hystrix和Sentinel进行限流 , 底层使用的是信号量和线程隔离实现的 , 当请求达到限制或者失败频率较高会自动熔断 , 执行降级逻辑
断路器/熔断器用过嘛 ? 断路器的状态有哪些👍
我们项目中使用Hystrix/Sentinel实现的断路器 , 断路器状态机包括三个状态:
- closed:关闭状态,断路器放行所有请求,并开始统计异常比例、慢请求比例。超过阈值则切换到open状态
- open:打开状态,服务调用被熔断,访问被熔断服务的请求会被拒绝,快速失败,直接走降级逻辑。Open状态5秒后会进入half-open状态
- half-open:半开状态,放行一次请求,根据执行结果来判断接下来的操作
- 请求成功:则切换到closed状态
- 请求失败:则切换到open状态
你们项目中有做过服务降级嘛 ?
我们项目中涉及到服务调用得地方都会定义降级, 一般降级逻辑就是返回默认值 , 降级的实现也非常简单 , 就是创建一个类实现FallbackFactory接口 , 然后再对应的Feign客户端接口上面 , 通过@FeignClient注解中的fallbackFactory属性指定降级类工厂
package com.dkd.feign.fallback;
import com.dkd.feign.TaskService;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class TaskServiceFallbackFactory implements FallbackFactory<TaskService> {
@Override
public TaskService create(Throwable throwable) {
return new TaskService() {
@Override
public Integer getSupplyAlertValue() {
return 50;
}
@Override
public Boolean hasTask(String innerCode, int productionType) {
return true;
}
};
}
}
引用FallbackFactory
@FeignClient(value = "task-service",fallbackFactory = TaskServiceFallbackFactory.class)
public interface TaskService { }