参考 https://www.fangzhipeng.com/spring-cloud.html
springcloud | springboot | spring cloud alibaba version |
---|---|---|
2020.0.x aka Ilford | 2.4.x | 2021.1 |
Hoxton | 2.2.x, 2.3.x (Starting with SR5) | 2.2.x |
Greenwich | 2.1.x | 2.1.x |
Finchley | 2.0.x | 2.0.x |
Edgware | 1.5.x | 1.5.x |
Dalston | 1.5.x | 1.5.x |
Spring cloud 在2020年最后几天发布了新版本,2020 年版本的 spring cloud 移除了netflix 组件,只保留了 eureka 组件
一、spingcloud netflix
1.Eureka 服务注册中心
eureka 服务注册中心: 主程序通过 @EnableEurekaServer 来声明自己为注册中心
服务提供者和消费者:通过 @EnableEurekaClient 或 @EnableDiscoveryClient 都能让注册中心能够发现,扫描到该服务。但 @EnableEurekaClient 只适用于 Eureka (不再更新)作为注册中心,@EnableDiscoveryClient 可以是其他注册中心(Zookeeper、Consul)。 Eureka 逐步被 Consul 取代,同时需要在配置文件中注明注册中心地址
Register:服务注册,当 Eureka 客户端向Eureka Server注册时,它提供自身的元数据,比如IP地址、端口等。
Renew:服务续约,Eureka客户会每隔【30秒】发送一次心跳来续约。告知注册中心我仍然存在,没有出现问题。 Eureka Server 默认在【90秒】没有收到 Eureka 客户端续约,会将实例从其注册表中删除。
Fetch Registries:获取注册列表信息,Eureka客户端(服务消费者)从注册中心获取注册表信息,并将其缓存在本地,每30秒更新。客户端使用该信息查找服务,进行远程调用。
Cancel:服务下线
Eureka 客户端停用前,调用以下内容: DiscoveryManager.getInstance().shutdownComponent(); 来通知注册中心剔除自己的服务。
Eviction :服务剔除,Eureka 客户端连续 90秒没有向Eureka服务器发送服务续约,即心跳,会将该服务实例从服务注册列表删除。
Eureka Client注册一个实例为什么这么慢
Eureka Client一启动(不是启动完成),不会立即向 Eureka Server 注册,默认延迟【40秒】向服务端注册
2.Ribbon 客户端负载均衡
前期用 RestTemplate+Ribbon去消费服务 ,后期用 Feign (内置Ribbon)来调用服务
在启动类中,通过 @EnableDiscoveryClient 向服务中心注册;注入一个bean: restTemplate;并通过 @LoadBalanced 注解表明这个 restRemplate 开启负载均衡的功能。
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
public class ServiceRibbonApplication {
public static void main(String[] args) {
SpringApplication.run( ServiceRibbonApplication.class, args );
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
注入ioc容器的 restTemplate 来消费 service-hi 服务的 “/hi” 接口,直接用服务名替代具体的 url 地址,在 ribbon 中通过负载均衡算法,最终实际调用 url(ip+端口):
@Service
public class HelloService {
@Autowired
RestTemplate restTemplate;
public String hiService(String name) {
return restTemplate.getForObject("http://SERVICE-HI/hi?name="+name,String.class);
}
}
3 Feign 声明式服务调用
Feign 默认集成了 Ribbon,并和 Eureka 结合,默认实现了负载均衡的效果。
springcloud F版本后升级为 OpenFeign,是 在Feign 的基础上支持了Spring MVC的注解,如@RequesMapping等等。@RequesMapping 不能在类名上与 @FeignClient 同时使用
1.启动类开启注解 @EnableFeignClients @EnableDiscoveryClient
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class ProductApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class, args);
}
}
2.服务消费端定义接口 @FeignClient 中 value 为服务名,@PostMapping 为服务提供的方法名
@FeignClient(value = "service-hi")
public interface CartFeignClient {
@PostMapping("/cart/{productId}")
Long addCart(@PathVariable("productId")Long productId);
}
3.服务消费端注入并调用
//这里直接注入feign client
@Autowired
private CartFeignClient cartFeignClient;
@PostMapping("/toCart/{productId}")
public ResponseEntity addCart(@PathVariable("productId") Long productId){
Long result = cartFeignClient.addCart(productId);
return ResponseEntity.ok(result);
}
首先通过@EnableFeignCleints注解开启FeignCleint
根据Feign的规则实现接口,并加@FeignCleint注解
程序启动后,会进行包扫描,扫描所有的@ FeignCleint的注解的类,并将这些信息注入到ioc容器中。
- 通过在主程序上加 @EnableFeignCleints 注解开启 FeignCleint
- 服务消费端通过 @FeignCleint 定义远程接口及方法
- 主程序启动后,扫描所有的 @ FeignCleint 的注解的接口,通过 JDK Proxy 代理实现这些接口,并将这些信息注入到spring ioc 容器中
- 当接口的方法被调用,由 Proxy代理实例去完成真正的远程访问,并且返回结果【接口方法被调用,是通过 jdk 的代理,来生成具体的 RequesTemplate,RequesTemplate 再生成 Request,Request 交给Client 去处理,其中 Client 可以是 HttpUrlConnection 或 Okhttp,最后Client被封装到LoadBalanceClient 类,该类结合 Ribbon 做到了负载均衡】
3.1 RestTemplate 和 feign
RestTemplate 只能写死服务调用地址或者要结合 @LoadBanlance 注解来实现负载均衡
Feign 是申明式调用(注解),集成了负载均衡,使用起来更方便
4 Hystrix 断路器(熔断降级)
分布式系统中,服务与服务之间的调用错综复杂,如果服务提供者出现问题,调用这个服务的消费者就会出现线程阻塞,此时若有大量的请求涌入,线程资源会被消耗完毕,导致该消费者也瘫痪。这种因“服务提供者”的不可用,导致“服务消费者”的不可用,并将不可用逐渐放大的过程。这就是服务故障的“雪崩”效应
对特定的服务的调用不可用达到一个阀值(Hystrix 是5秒20次) 断路器将会被打开。断路打开后,fallback 方法快速失败,直接返回一个固定值。
Feign 自带断路器,默认没有打开,需要在配置文件中配置打开,配置如下:
feign.hystrix.enabled=true
在 @FeignClient 注解中添加 fallback 属性,当远程服务不可以(宕机)时,可以快速失败
@FeignClient(value = "service-hi",fallback = SchedualServiceHiHystric.class)
public interface SchedualServiceHi {
@RequestMapping(value = "/hi",method = RequestMethod.GET)
String sayHiFromClientOne(@RequestParam(value = "name") String name);
}
@Component
public class SchedualServiceHiHystric implements SchedualServiceHi {
@Override
public String sayHiFromClientOne(String name) {
return "sorry "+name;
}
}
5 zuul 网关
- 统一入口:为全部微服务提供一个唯一的入口,将外部和内部隔离,保障了后台服务的安全性。
- 鉴权校验:识别每个请求的权限,拒绝不符合要求的请求。
- 动态路由:动态的将请求路由到不同的后端集群中。
- 限流控制
- 减少客户端与服务端的耦合:服务可以独立发展,通过网关层来做映射。
@SpringBootApplication
@EnableZuulProxy
@EnableDiscoveryClient
public class ServiceZuulApplication {
public static void main(String[] args) {
SpringApplication.run( ServiceZuulApplication.class, args );
}
}
1.动态路由的配置
先指定注册中心的地址为http://localhost:8761/eureka/,网关服务端口为8769,服务名为 service-zuul;以 /api-a/ 开头的请求都转发给 service-ribbon 服务;以 /api-b/ 开头的请求都转发给 service-feign 服务;
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
server:
port: 8769
spring:
application:
name: service-zuul
zuul:
routes:
api-a:
path: /api-a/**
serviceId: service-ribbon
api-b:
path: /api-b/**
serviceId: service-feign
2.服务过滤(鉴权等) 自定义过滤器来继承 ZuulFilter
@Component
public class MyFilter extends ZuulFilter {
private static Logger log = LoggerFactory.getLogger(MyFilter.class);
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
log.info(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));
Object accessToken = request.getParameter("token");
if(accessToken == null) {
log.warn("token is empty");
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
try {
ctx.getResponse().getWriter().write("token is empty");
}catch (Exception e){}
return null;
}
log.info("ok");
return null;
}
}
- filterType:返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型
pre:路由之前
routing:路由之时
post: 路由之后
error:发送错误调用 - filterOrder:过滤的顺序
- shouldFilter:这里可以写逻辑判断,是否要过滤,本文 true ,永远过滤。
- run:过滤器的具体逻辑。可用很复杂,包括查 sql,nosql去判断该请求到底有没有权限访问。
6 config 配置中心 及 bus 消息总线
- 配置中心通过 @EnableConfigServer 声明自己为注册中心,并在自己的配置文件中定义好git仓库的地址及账号信息等
- 需要调用配置中心服务的客户端服务,在配置文件中定义好【配置中心的服务地址】,启动即可
- Spring Cloud Config (configClient)在项目启动时加载配置内容这一机制,导致修改配置文件内容后,不会自动刷新。可以利用 @RefreshScope 注解并结合 actuator 来解决,注意要引入 spring-boot-starter-actuator 包,且在 config client 端配置中增加 actuator 配置,再需要读取配置的类上增加 @RefreshScope 注解,最后发送 POST 请求到 http://localhost:3302/actuator/refresh 这个接口,来触发加载新配置的,以上都是修改 config client 端
- 利用 Spring Cloud Bus 来自动刷新多个端,当client 有多端时,一个一个刷新就不太友好,通过bus总线来完成,需要配置消息中间件 mq, 添加依赖 spring-cloud-starter-bus-amqp,配置mq的ip及端口信息等,再用 POST访问其中一个的 actuator/bus-refresh 地址,会看到这两个端都有日志说明重新加载了配置,以上也是修改 config client 端
优化 Spring Cloud Bus 做配置更新步骤如下:
1.提交代码触发 webhook post 给 配置中心 Server 端发送 bus/refresh 刷新请求
2.Server端接收到请求并发送给Spring Cloud Bus
3.Spring Cloud bus 接到消息并通知给其它客户端
4.其它客户端接收到通知,请求 Server 端获取最新配置
5.全部客户端均获取到最新的配置
7.Hystrix Dashboard
Hystrix Dashboard 作为断路器的一个组件,提供了可视化数据监控和友好的图形化界面。可以监控所有托管在 hystrix 的远程调用,hystrix 会实时、累加地记录所有关于 HystrixCommand 的执行信息,包括每秒执行多少请求,多少成功和失败,及统计失败率等。底层原理是 Dashboard 间隔一定时间去“Ping”目标服务(服务消费者上),返回的结果是最新的监控数据,最后将数据显示出来,
1.创建被监控的目标服务,需具备两个条件。1.服务本身有对Hystrix做监控统计(spring-cloud-starter-hystrix启动依赖);2.暴露 hystrix.stream 端口(spring-boot-starter-actuator启动依赖)。所以需要确保目标服务引入如下依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2.创建 hystrix-dashboard 服务单独作为监控的服务(也可以直接集成在 hystrix 消费者上)
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
</dependencies>
启动类 @EnableHystrixDashboard 表明该服务是 dashboard 服务,然后 http://服务ip:端口/hystrix
上图中出现3个输入框。作用如下:
- 目标服务暴露的 hystrix.stream 端口
- “Ping”的间隔时间
- 目标服务的别名,可以随便取
在3个输入框输入无误后,点击“Monitor Stream”按钮,开始“监控”目标服务(消费者)展示如下
8. Turbine
Turbine 是 Hystrix Dashboard 的进阶,当我们有很多个服务的时候,看单个的 Hystrix Dashboard 的数据并没有什么多大的价值,需要聚合所有服务的 Hystrix Dashboard 的数据了。就用到Spring Cloud 的另一个组件 Hystrix Turbine
1.启动类使用 @EnableTurbine 注解开启Turbine
2.配置文件
#服务名为 turbine
spring.application.name=turbine
server.port=9000
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
#指定需要收集监控信息的服务名,多个用逗号分隔
turbine.app-config=RIBBON-CONSUMER
#指定集群的名称为 default,当服务实例非常多的时候,可以启动多个 Turbine 来构建不同的聚合集群,
#而该参数可以区分这些不同的集群,同时该参数可以在Hystrix仪表盘中用来定位不同的聚合集群,
#只需在Hystrix Stream的URL中通过cluster参数来指定。
turbine.cluster-name-expression="default"
#当该参数为 true,可以让同一主机上的服务通过主机名与端口号的组合来进行区分,默认会以host区分不同服务,
#这会使得在本地调试的时候,本机上不同服务聚合成一个服务来统计
turbine.combine-host-port=true
- 浏览器访问http://localhost:9000/hystrix
-输入http://localhost:9000/turbine.stream
9.Sleuth
Zipkin 是 Twitter 开源的分布式的跟踪系统,而 Spring Cloud Sleuth 集成了 zipkin 组件
- Span:基本工作单元,例如,在一个新建的span中发送一个RPC等同于发送一个回应请求给RPC,span通过一个64位ID唯一标识,trace以另一个64位ID表示,span还有其他数据信息,比如摘要、时间戳事件、关键值注释(tags)、span的ID、以及进度ID(通常是IP地址) span在不断的启动和停止,同时记录了时间信息,当你创建了一个span,你必须在未来的某个时刻停止它。
- Trace:一系列spans组成的一个树状结构,例如,如果你正在跑一个分布式大数据工程,你可能需要创建一个trace。
- Annotation:用来及时记录一个事件的存在,一些核心annotations用来定义一个请求的开始和结束
cs - Client Sent -客户端发起一个请求,这个annotion描述了这个span的开始
sr - Server Received -服务端获得请求并准备开始处理它,如将其sr减cs时间戳便得到网络延迟
ss - Server Sent -(当请求返回客户端),ss减sr时间戳得到服务端需要处理请求的时间
cr - Client Received -表明span的结束,客户端成功接收到服务端的回复,如果cr减去cs时间戳便可得到客户端从服务端获取回复的所有所需时间
将Span和Trace在一个系统中使用Zipkin注解的过程图形化:
demo 搭建
- 新建一个 Zipkin 服务端 ZipkinServer,@EnableZipkinServer 开启 ZipkinServer 的功能
- 服务生产者和消费者通过配置 ZipkinServer 的地址来打通
server.port=XXX
spring.zipkin.base-url=http://localhost:9411 # ZipkinServer 地址
spring.application.name=XXX
打开浏览器访问:http://localhost:9411/,会出现以下界面
点击Dependencies,可以发现服务的依赖关系:
点击find traces,可以看到具体服务相互调用的数据:
Zipkin Server 默认是将数据存储在内存中,重启后之前的链路数据将丢失,Zipkin 支持 Mysql、Elasticsearch、Cassandra 存储,高并发建议存储到 Elasticsearch,ElasticSearch 可以和 Kibana 结合,将链路数据展示在 Kibana上(需要安装 Elasticsearch 和 Kibana)
10. 调用链监控 Skywalking 与 Zipkin 对比
参考 https://blog.youkuaiyun.com/zhuyu19911016520/article/details/106782827
颗粒度 :Skywalking(华为员工开源) 方法级(展示的更详细),方法中所有的调用都展示出来了,如数据库调用、redis 调用,第三方网络调用,而 Zipkin 只能展示接口级
UI界面 :Skywalking 完胜,国产开源,更适合国人眼球
代码侵入:Skywalking 无代码侵入,使用字节码增强技术,在启动服务时使用 javaagent 指向 skywalking服务即可收集调用链 span 信息
Zipkin:简单、轻量级
Skywalking 介绍
Linux环境下SkyWalking 环境搭建部署入门Demo
- skywalking agent 和业务系统绑定在一起,负责收集各种监控数据
- Skywalking oapservice (相当于后端,常以集群形式存在)负责处理监控数据,1、接受 skywalking agent 的监控数据并保存;2、接受 skywalking webapp 前端请求,从数据库查询数据,并返给前端。
- skywalking webapp,前端界面,用于展示数据。
skywalking 会启动两个服务三个端口,一个是 skywalking-oap-server,一个是 skywalking-web-ui,
oap-server 服务启动后暴露 11800 和 12800 两个端口,分别为收集监控数据的和接受前端请求,端口配置文件为 config/applicaiton.yml,web-ui 的默认访问端口为 8080,配置文件为 webapp/webapp.yml
前端页面如下:
业务系统 和 skywalking agent 的绑定
- 1、业务系统修改 pom 文件添加 skywalking 依赖
- 2、在 resources 目录下添加 logback-spring.xml 文件
- 3、下载 skywalking,将下面的 agent 目录放到业务应用中
- 4、在 idea 程序启动命令增加如下 JVM 启动参数,如下
-javaagent:(agent文件夹所在的目录)\agent\skywalking-agent.jar -Dskywalking.agent.service_name=(服务名)-service -Dskywalking.agent.instance_name=(服务名)-instance -Dskywalking.collector.backend_service=(安装SkyWalking机器的IP):9022
Javaagent 是什么(JVM 启动前静态 Instrument)
Javaagent 是 java 命令的一个参数。参数 javaagent 可以用于指定一个 jar 包,并且对该 java 包有两个要求:
这个 jar 包的 MANIFEST.MF 文件必须指定 Premain-Class 项。
Premain-Class 指定的那个类必须实现 premain() 方法。
premain() 方法,从字面上理解,就是运行在 main() 函数之前的的类。当 Java 虚拟机启动时,在执行 main() 函数之前,jvm 会先运行 -javaagent 所指定 jar 包内 Premain-Class 这个类的 premain() 方法 。
二、springcloud 组件升级
1.Gateway
微服务各个服务的Api被请求时时,每个服务都需要做相同的事情,如鉴权、限流、日志输出等。此时就出现了网关的概念
Spring Cloud Gateway 是第二代网关框架,取代 Zuul 网关,和 Zuul 的特征差别不大。两者主要区别在底层的通信框架上。Springcloud Zuul 是基于servlet之上的一个阻塞式处理模型,Zuul 2.0 开始,使用了Netty,但是,Springcloud 官方没有把 zuul2.0 集成改版本的计划。Gateway 是基于 webflux 基础之上实现的。Webflux 虽然可以兼容多个底层的通信框架,但是一般情况下,底层使用的还是 Netty,
- 路由转发
- 权限校验
- 限流
- 日志输出
作为整个系统的前端工程,对流量进行控制,有限流的作用
作为系统的前端边界,外部流量只能通过网关才能访问系统
可以在网关层做
可以在网关层做缓存
Route(路由):路由是网关的基本单元,由ID、URI、一组Predicate、一组Filter组成,根据Predicate进行匹配转发。
Predicate(断言): 路由转发的判断条件,目前 Gateway 支持多种方式,常见如:Path、Query、Method、Header 等
Filter(过滤器): 过滤器是路由转发请求时所经过的过滤逻辑,可用于修改请求、响应内容
注意:其中 Route 和 Predicate 必须同时申明
Predicate(断言)
server:
port: 8080
spring:
application:
name: api-gateway
cloud:
gateway:
routes:
-id: url-proxy-1
uri: https://blog.csdn.net
predicates:
-Path=/csdn
id:我们自定义的路由 ID,保持唯一
uri:目标服务地址
predicates(断言):路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。
上面这段配置的意思是,配置了一个 id 为 url-proxy-1的URI代理规则,路由的规则为:
当访问 http://localhost:8080/csdn/1.jsp 时,会路由到 https://blog.youkuaiyun.com/1.jsp 去
上述配置也可以通过编写代码实现,效果等同
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("path_route", r -> r.path("/csdn")
.uri("https://blog.youkuaiyun.com"))
.build();
}
除了断言路径 path,还可以断言 请求头、cookie、请求时间等等
Filter(过滤器)
filter 在 “pre” 类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等,在“post”类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等
客户端向 Gateway 发出请求。然后在 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。
filter 除了分为 “pre” 和 “post” 两种方式的 filter 外,Gateway 的 filter 从作用范围可分为另外两种,一种是针对于【单个路由】的 gateway filter,它在配置文件中的写法同predict类似;另外一种是针对于所有路由的 global gateway filer
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: https://example.org
filters:
- PrefixPath=/mypath
以上为 对所有的请求路径添加前缀 mypath,同样的,过滤器即可用通过配置文件实现,也可以通过代码实现
限流
令牌桶算法:大致就是以一定 【固定的速率】 会往里面放令牌,请求过来先要从桶中获取令牌,如果没有获取到,那么这个请求就拒绝,如果获取到那么就放行
实现思路:可以准备一个队列,用来保存令牌,另外通过一个线程池【定期】(匀速)生成令牌放到队列中,每来一个请求,就从队列中获取一个令牌,并继续执行
Spring Cloud Gateway 官方提供了 RequestRateLimiterGatewayFilterFactory 这个类,结合 Redis 实现了令牌桶的方式,只需简单配置了 redis 的信息,并配置了 RequestRateLimiter 的限流过滤器即可
server:
port: 8081
spring:
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: '#{@hostAddrKeyResolver}'
redis-rate-limiter.replenishRate: 1
redis-rate-limiter.burstCapacity: 3
application:
name: gateway-limiter
redis:
host: localhost
port: 6379
database: 0
整合注册中心
在 uri 的 schema 协议部分为自定义的 lb: 类型,表示从微服务注册中心(如Eureka)订阅服务,并且进行服务的路由。
和等
spring:
application:
name: sc-gateway-server
cloud:
gateway:
discovery:
locator:
enabled: false
lowerCaseServiceId: true
routes:
- id: service-hi
uri: lb://SERVICE-HI #
predicates:
- Path=/demo/**
filters:
- StripPrefix=1
整合 Hystrix 做服务熔断降级
当上游的请求,进入了 Hystrix 熔断降级机制时,会调用 fallbackUri 配置的降级地址。需要注意的是,还需要单独设置 Hystrix的commandKey 的超时时间
fallbackUri配置的降级地址的代码如下:
server.port: 8082
spring:
application:
name: gateway
redis:
host: localhost
port: 6379
password: 123456
cloud:
gateway:
routes:
- id: rateLimit_route
uri: http://localhost:8000
order: 0
predicates:
- Path=/test/**
filters:
- StripPrefix=1
- name: Hystrix
args:
name: fallbackCmdA
fallbackUri: forward:/fallbackA
hystrix.command.fallbackCmdA.execution.isolation.thread.timeoutInMilliseconds: 5000
@RestController
public class FallbackController {
@GetMapping("/fallbackA")
public Response fallbackA() {
Response response = new Response();
response.setCode("100");
response.setMessage("服务暂时不可用");
return response;
}
}
2.Consul
作为服务注册中心
Consul 是 HashiCorp 公司推出的开源软件,在业界最广泛的用途就是作为服务注册中心,同 Eureka 类似,服务提供者和服务消费者分别向 consul 注册完成后,服务消费者通过 FeignClient 来消费服务提供者的服务。
作为配置中心
Consul 不仅能用来服务注册和发现,Consul 而且支持Key/Value键值对的存储,可以用来做配置中心。Spring Cloud 提供了Spring Cloud Consul Config 依赖去和 Consul 相集成,用来做配置中心
三、spring cloud alibaba 生态
1.nacos
阿里的服务注册发现组件,类似于Consul、Eureka,同时它又提供了分布式配置中心的功能,这点和Consul 的 config 类似,支持热加载。管理页面相比于 Eureka 确实更丰富
Nacos 的关键特性包括:
- 服务发现和服务健康监测
- 动态配置服务,带管理界面,支持丰富的配置维度。
- 动态 DNS 服务
- 服务及其元数据管理
2.Sentinel
阿里微服务的流量控制、熔断降级功能,类似Hystrix ,Sentinel(哨兵),可以有效的解决微服务调用产生的“雪崩”效应,Hytrxi 进入了维护期,不再提供新功能,Sentinel 是替代方案。Hystrix 采用线程池对服务的调用进行隔离,Sentinel 用了用户线程对接口进行隔离,Hystrxi 是服务级别的隔离,Sentinel 提供了接口级别的隔离,Sentinel 隔离级别更加精细,另外Sentinel 直接使用用户线程进行限制,相比 Hystrix 的线程池隔离,减少了线程切换的开销。另外 Sentinel 的 DashBoard 提供了在线更改限流规则的配置,也更加的优化
四、dubbo 和 springcloud
参考 https://blog.youkuaiyun.com/weixin_51291483/article/details/109212137/
1.SpringCloud与Dubbo的区别
两者都是主流的微服务框架
SpringCloud:Spring 公司开源的微服务框架,SpirngCloud 定位为微服务架构下的一站式解决方案。
Dubbo:阿里巴巴开源的 RPC 框架,Dubbo 是 SOA 时代的产物,它的关注点主要在于服务的调用,流量分发、流量监控和熔断
SpringCloud 生态丰富,功能完善,更像是品牌机,Dubbo 则相对灵活,可定制性强,更像是组装机。
dubbo 和 Feign 远程调用的差异
dubbo 和 Feign 都可以认为是 rpc 框架
PRC 全称是 Remote Procedure Call,即远程过程调用。rpc 和 dubbo 比起来,rpc 更像是一种编程细想或者是通信方式(RPC不是协议),dubbo 是 rpc 的一种实现方式,就像接口和实现类一样,rpc 是接口,dubbo 是实现类;dubbo 即是框架也是协议,dubbo 协议只能在 dubbo 框架上使用,Dubbo 协议与 HTTP、FTP,SMTP 这些应用层协议是并列的概念。除默认的 Dubbo 协议,Dubbo 框架还支持 RMI、Hessian、HTTP 等协议。
Feign 是 SpringCloud 中的远程调用方式,基于 Http 协议,采用 Rest 风格。
Dubbo 框架默认采用 Dubbo 协议,该协议在数据传输性能上会比 Http 协议要好一些。
不过这种性能差异除非是达极高的并发量级,否则无需多虑。
dubbo和dubbox之间的区别?
Dubbox 和 Dubbo 本质上没有区别,名字的含义扩展了 Dubbo 而已,
以下扩展出来的功能
1、支持 REST 风格远程调用(HTTP + JSON/XML);
2、支持基于 Kryo 和 FST 的 Java 高效序列化实现;
3、支持基于 Jackson 的 JSON 序列化;
4、支持基于嵌入式 Tomcat 的 HTTP remoting 体系;
5、升级 Spring 至 3.x;
6、升级 ZooKeeper 客户端;
7、支持完全基于 Java 代码的 Dubbo 配置;
注册中心 eureka 和 zookeeper 差异
zookeeper、eureka ,对 CAP 侧重点不同 一个侧重 CP 一个侧重 AP
1、zookeeper
1、原本的Zookeeper
在概念上,Zookeeper 是用来保证分布式一致性的一个软件,不是为了服务发现注册而设计的。不过该特性被二次开发成服务发现注册中心。
2、Zookeeper 保证 CP(一致性的要求要高于可用性)
当 zk 的 master 节点因为网络故障与其他节点失去联系时,剩余节点进行 leader 选举。选举过程要 30 ~ 120s,太长, 此时集群不可用,注册服务瘫痪(保证了一致性 C 而放弃可用性 A)。在云部署的环境下,因网络问题使得 zk 集群失去 master 节点概率较大,虽然服务能够最终恢复,但是漫长的选举时间导致的注册长期不可用是不能容忍的。
2、Eureka
Eureka 保证 AP
而 Eureka 设计时就优先保证可用性。Eureka 各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。如果客户端在连接服务端失败时,会切换至其它节点,只要有一台 Eureka 还在,就能保证注册服务可用(保证可用性),不过查到的信息可能不是最新的(不保证强一致性),此外,Eureka 还有一种自我保护机制,如果在15分钟内超过 85% 的节点都没有正常的心跳,那么 Eureka 就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:
- Eureka 不再从注册列表中移除因为长时间没收到心跳而应该过期的服务
- Eureka 仍接受新服务的注册和查询请求,但不会同步到其它节点上(即保证当前节点依然可用)
- 当网络稳定时,当前实例新的注册信息会被同步到其它节点中
因此, Eureka 可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像 zookeeper 那样使整个注册服务瘫痪
3、总结
-
从集群设计来看:Eureka 集群各节点平等,没有主从关系,因此可能出现数据不一致情况;ZK为了满足一致性,必须包含主从关系,一主多从。集群无主时,不对外提供服务
-
CAP 原则来看:Eureka 满足AP原则,为了保证整个服务可用性,牺牲了集群数据的一致性;而Zookeeper满足CP原则,为了保证各节点数据一致性,牺牲了整个服务的可用性。
-
服务拉取方式来看:Eureka采用的是服务主动拉取策略,消费者按照固定频率(默认30秒)去Eureka拉取服务并缓存在本地;ZK 中的消费者首次启动到 ZK 订阅自己需要的服务信息,并缓存在本地。然后监听服务列表变化,以后服务变更 ZK 会推送给消费者。
设计一个最基本的 RPC 框架
一般情况下, RPC 框架不仅要提供服务发现功能,还要提供负载均衡、容错等功能,这样的 RPC 框架才算真正合格的。
1、注册中心 :注册中心首先是要有的,推荐使用 Zookeeper。注册中心负责服务地址的注册与查找,相当于目录服务。服务端启动的时候将服务名称及其对应的地址(ip+port)注册到注册中心,服务消费端根据服务名称找到对应的服务地址。有了服务地址之后,服务消费端就可以通过网络请求服务端了。
2、网络传输 :既然要调用远程的方法就要发请求,请求中至少要包含你调用的类名、方法名以及相关参数吧!推荐基于 NIO 的 Netty 框架。
3、序列化 :既然涉及到网络传输就一定涉及到序列化,你不可能直接使用 JDK 自带的序列化吧!JDK 自带的序列化效率低并且有安全漏洞。 所以,你还要考虑使用哪种序列化协议,比较常用的有 hession2、kyro、protostuff。
4、动态代理 : 另外,动态代理也是需要的。因为 RPC 的主要目的就是让我们调用远程方法像调用本地方法一样简单,使用动态代理可以屏蔽远程方法调用的细节比如网络传输。也就是说当你调用远程方法的时候,实际会通过代理对象来传输网络请求,不然的话,怎么可能直接就调用到远程方法呢
5、负载均衡 :负载均衡也是需要的。为啥?举个例子我们的系统中的某个服务的访问量特别大,我们将这个服务部署在了多台服务器上,当客户端发起请求的时候,多台服务器都可以处理这个请求。那么,如何正确选择处理该请求的服务器就很关键。假如,你就要一台服务器来处理该服务的请求,那该服务部署在多台服务器的意义就不复存在了。负载均衡就是为了避免单个服务器响应同一请求,容易造成服务器宕机、崩溃等问题,我们从负载均衡的这四个字就能明显感受到它的意义。
zookeeper 在 dubbo 中作为注册中心的原理结构
1、原理图
2、原理图解释
流程:
1、服务提供者启动时向/dubbo/com.foo.BarService/providers目录下写入URL
2、服务消费者启动时订阅/dubbo/com.foo.BarService/providers目录下的URL向/dubbo/com.foo.BarService/consumers目录下写入自己的URL
3、监控中心启动时订阅/dubbo/com.foo.BarService目录下的所有提供者和消费者URL
支持以下功能:
1、当提供者出现断电等异常停机时,注册中心能自动删除提供者信息。
2、当注册中心重启时,能自动恢复注册数据,以及订阅请求。
3、当会话过期时,能自动恢复注册数据,以及订阅请求。
4、当设置<dubbo:registry check=“false” />时,记录失败注册和订阅请求,后台定时重试。
5、可通过<dubbo:registry username=“admin” password=“1234” />设置zookeeper登录信息。
6、可通过<dubbo:registry group=“dubbo” />设置zookeeper的根节点,不设置将使用无根树。
7、支持号通配符<dubbo:reference group="" version=“*” />,可订阅服务的所有分组和所有版本的提供者。
3、Dubbo 的基本服务流程
(1)服务提供者启动后将自己要对外提供的服务注册到 Zookeeper ,/dubbo/接口名/providers/url
(2)消费服者启动后订阅自己需要消费接口节点列表(/dubbo/接口名/providers),同时将自己的地址写到(/dubbo/接口名/consumers/url)
(3)当订阅成功后,一旦订阅的节点下的服务列表有变化,zookeeper 就会向消费端推送新的服务列表地址
(4)消费者要调用某个服务,则根据订阅的服务列表中根据路由规则挑选一台,调用服务提供者。
(5)消费者和服务提供者会统计调用次数,记录到内存中,定时将数据推送到 zookeeper 中。