Eureka服务注册与发现

Eureka服务注册与发现

概念原理

过去,每个应用都是一个CPU,一个主机上的单一系统。然而今天,随着大数据和云计算时代的到来,任何独立的程序都可以运行在多个计算机上。并且随着业务的发展,访问用户量的增加,开发人员或小组的增加,
系统会被拆分成多个功能模块。拆分后每个功能模块可以作为一个独立的子系统提供其职责范围内的功能。而多个子系统中,由于职责不同并且会存在相互调用,同时可能每个子系统还需要多个实例部署在多台
服务器或者镜像中,导致了子系统间的相互调用形成了一个错综复杂的网状结构。

从单体演变成分布式架构。随着系统结构、架构的演变,系统功能的增加,用户量的增加,开发人员的增加等各种增加情况下,需要有一个比较好扩展的系统架构来快速、尽量减少代码改动的前提下以支持系统功能
的开发,用户量增加导致的硬件资源横向扩容,以及开发人员增加时的协同工作效率。在此基础上需要解决系统的稳定性、容错性、高并发的支持性等。以及随着系统功能的增加如何有效的管理系统,排查、
定位系统问题。同时当参与项目的人(包含测试、运维、业务等人员)越来越多时,如何能更高效的彼此之间协同办公的效率等等。所以微服务架构需要考虑的不仅仅是软件架构本身,需要从参与到整个项目实施
过程中的各个环节,可能的问题以及人员协同的整体情况去考虑。让整个项目做到可用(满足功能以及硬件资源的横向扩容)、可行(满足整个系统运行中的各个点的监控、排错等)、可持续(满足系统功能的可持续集成、
以及系统运行的可持续性)以及高效(系统运行的高效、人员协同工作的高效、功能迭代的高效等)。

微服务通俗讲解

Spring Cloud提供了微服务解决的一整套方案,而Eureka是其重要组件,所以先要了解什么是“微服务”。

在大型系统架构中,会拆分多个子系统。这些系统往往都有这几个功能:提供接口,调用接口,以及该子系统自身的业务功能。这样的一个子系统就称为一个“微服务”。(可以理解为一个子系统的代码所实现的功能)

实例:
每个服务都会部署到多个机器(或镜像)中,这些多个部署的应用就是实例。(可以理解为一套子系统代码被部署到了多个机器上)

Eureka的管理

基于以上概念,使用Eureka管理时会具备几个特性:

  • 服务需要有一个统一的名称(或服务ID)并且是唯一标识,以便于接口调用时各个接口的区分。并且需要将其注册到Eureka Server中,其他服务调用该接口时,也是根据这个唯一标识来获取。
  • 服务下有多个实例,每个实例也有一个自己的唯一实例ID。因为它们各自有自己的基础信息如:不同的IP。所以它们的信息也需要注册到Eureka Server中,其他服务调用它们的服务接口时,
    可以查看到多个该服务的实例信息,根据负载策略提供某个实例的调用信息后,调用者根据信息直接调用该实例。

eureka如何管理服务调用

  • 在Eureka Client启动的时候,将自身的服务的信息发送到Eureka Server。然后进行2调用当前服务器节点中的其他服务信息,保存到Eureka Client中。当服务间相互调用其它服务时,在Eureka Client中
    获取服务信息(如服务地址,端口等)后,进行第3步,根据信息直接调用服务。(注:服务的调用通过http(s)调用)

  • 当某个服务仅需要调用其他服务,自身不提供服务调用时。在Eureka Client启动后会拉取Eureka Server的其他服务信息,需要调用时,在Eureka Client的本地缓存中获取信息,调用服务。

  • Eureka Client通过向Eureka Serve发送心跳(默认每30秒)来续约服务的。 如果客户端持续不能续约,那么,它将在大约90秒内从服务器注册表中删除。 注册信息和续订被复制到集群中的Eureka Serve所有节点。 以此来确保当前服务还“活着”,可以被调用。

  • 来自任何区域的Eureka Client都可以查找注册表信息(每30秒发生一次),以此来确保调用到的服务是“活的”。并且当某个服务被更新或者新加进来,也可以调用到新的服务。

Eureka Server:

  • 提供服务注册:各个微服务启动时,会通过Eureka Client向Eureka Server进行注册自己的信息(例如服务信息和网络信息),Eureka Server会存储该服务的信息。

  • 提供服务信息提供:服务消费者在调用服务时,本地Eureka Client没有的情况下,会到Eureka Server拉取信息。

  • 提供服务管理:通过Eureka Client的Cancel、心跳监控、renew等方式来维护该服务提供的信息以确保该服务可用以及服务的更新。

  • 信息同步:每个Eureka Server同时也是Eureka Client,多个Eureka Server之间通过P2P复制的方式完成服务注册表的同步。同步时,被同步信息不会同步出去。也就是说有3个Eureka Server,Server1有新的服务信息时,同步到Server2后,Server2和Server3同步时,Server2不会把从Server1那里同步到的信息同步给Server3,只能由Server1自己同步给Server3。

  • 每个可用区有一个Eureka集群,并且每个可用区至少有一个eureka服务器来处理区内故障。为了实现高可用,一般一个可用区中由三个Eureka Server组成。

Eureka Client:

  • Eureka Client是一个Java客户端,用于简化与Eureka Server的交互。并且管理当前微服务,同时为当前的微服务提供服务提供者信息。

  • Eureka Client会拉取、更新和缓存Eureka Server中的信息。即使所有的Eureka Server节点都宕掉,服务消费者依然可以使用缓存中的信息找到服务提供者。

  • Eureka Client在微服务启动后,会周期性地向Eureka Server发送心跳(默认周期为30秒)以续约自己的信息。如果Eureka Server在一定时间内没有接收到某个微服务节点的心跳,Eureka Server将会注销该微服务节点(默认90秒)。

  • Eureka Client包含服务提供者Applicaton Service和服务消费者Application Client

  • Applicaton Service:服务提供者,提供服务给别个调用。

  • Application Client:服务消费者,调用别个提供的服务。

  • 往往大多数服务本身既是服务提供者,也是服务消费者。

动作

Register:服务注册

当Eureka客户端向Eureka Server注册时,它提供自身的元数据,比如IP地址、端口,运行状况指示符URL,主页等。

Renew:服务续约

Eureka Client会每隔30秒发送一次心跳来续约。 通过续约来告知Eureka Server该Eureka客户仍然存在,没有出现问题。 正常情况下,如果Eureka Server在90秒没有收到Eureka客户的续约,它会将实例从其注册表中删除。 建议不要更改续约间隔。

Fetch Registries:获取注册列表信息

Eureka客户端从服务器获取注册表信息,并将其缓存在本地。客户端会使用该信息查找其他服务,从而进行远程调用。该注册列表信息定期(每30秒钟)更新一次。每次返回注册列表信息可能与Eureka客户端的缓存信息不同, Eureka客户端自动处理。如果由于某种原因导致注册列表信息不能及时匹配,Eureka客户端则会重新获取整个注册表信息。 Eureka服务器缓存注册列表信息,整个注册表以及每个应用程序的信息进行了压缩,压缩内容和没有压缩的内容完全相同。Eureka客户端和Eureka 服务器可以使用JSON / XML格式进行通讯。在默认的情况下Eureka客户端使用压缩JSON格式来获取注册列表的信息。

Cancel:服务下线

Eureka客户端在程序关闭时向Eureka服务器发送取消请求。 发送请求后,该客户端实例信息将从服务器的实例注册表中删除。该下线请求不会自动完成,它需要调用以下内容:

DiscoveryManager.getInstance().shutdownComponent();

Eviction:服务剔除

在默认的情况下,当Eureka客户端连续90秒没有向Eureka服务器发送服务续约,即心跳,Eureka服务器会将该服务实例从服务注册列表删除,即服务剔除。

Skywalking 分布式链路追踪

Skywalking 分布式链路追踪

skywalking下载

在官网下载最新版skywalking:
下载地址

http://skywalking.apache.org/downloads/

解压后启动

启动skywalking后台程序和ui
在此之前可以修改ui等启动的端口。

例如ui的端口,在webapp目录下–>的webapp.yml文件,这里修改的是808端口,默认是8080

启动skywalking的方法是:

a.在解压下的bin目录,windows双击startup.bat,linux运行startup.sh

b.启动后检查端口

探针的配置

使用探针
在要监控的项目中使用探针,在开发环境,结合idea使用时,配置我们项目的启动参数:

1
-javaagent:D:\cloud\apache-skywalking-apm-bin\agent\skywalking-agent.jar

-javaagent后面时skywalking探针的绝对地址,也就是之前下载的skywalking的压缩包解压出来后目录下的agent目录下的skywalking-agent.jar包。

IDEA中使用 VM options中加入相同配置.

1
2
3
4
5
6
7
8
9
10
-javaagent:D:/apache-skywalking-apm-bin/agent/skywalking-agent.jar
-Dskywalking.collector.backend_service=localhost:12800

agent(实际地址)
IP 记得改

eg:

-javaagent:E:/微服务/skywalking/apache-skywalking-apm-bin-es7/agent/skywalking-agent.jar
-Dskywalking.collector.backend_service=172.16.129.139:12800

配置优先级

skywalking agent配置覆盖
在正式环境使用探针时,每使用一次,都去配置一下agent的参数信息也是比较繁琐的,所以skywalking有配置覆盖的选项,即不同的形式的配置优先级是不一样的,具体如下:

JVM配置 > 系统环境变量 > agent.config

上面所用的方式是agent.config的方式,优先级是最低的,所以还可以覆盖他的配置:
jvm配置:

1
2
-javaagent:/path/to/skywalking-agent.jar=[option1]=[value1],[option2]=[value2]
-javaagent:skywalkingagent.jar=agent.service_name=test,collector.backend_service=xxx:11800

环境变量配置:

1
2
-Dskywalking.[option1]=[value1]
-Dskywalking.service_name=xxxx

这样在启动的时候,就不用每次去复制一份agent文件了,只需要在启动时加上参数即可

skywalking修改数据源

skywalking 版本 8.0, 对应使用mysql8.0,所以复制相应驱动至 skywalking\oap-libs

1
mysql-connector-java-8.0.21.jar

SpringCloud Config配置中心

SpringCloud Config配置中心

简介

在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。在Spring Cloud中,有分布式配置中心组件spring cloud config ,
它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中。在spring cloud config 组件中,分两个角色,一是Config-Server,二是Config-Client。

Config Server从本地读取配置文件

创建一个spring-boot项目,取名为 config-server,依赖如下:

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>

注解 @EnableConfigServer 开启配置服务器

1
2
3
4
5
6
7
8
9
@SpringBootApplication
@EnableConfigServer //开启配置服务器
public class ConfigServerApplication {

public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}

}

需要在程序的配置文件application.properties文件配置以下。通过 spring.profile.active=native 来配置 ConfigServer 从本地读取配置,读取的路径为 classpath 下的 shared 目录。

1
2
3
4
5
6
7
8
9
10
11
12
server:
port: 8769
spring:
application:
name: config-server
profiles:
active: native
cloud:
config:
server:
native:
search-locations: classpath:/shared

在 resources 目录下新建 shared 文件夹,在 shared 文件夹下新建 config-client-dev.yml 文件。

1
2
3
4
server:
port: 8762

foo: foo version 1

启动 config-server 工程!

构建Config-Client

创建一个spring-boot项目,取名为 config-client,依赖如下:

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>

在 resources 目录下新建 bootstrap.yml 文件,因为 bootstrap 相对于 application 具有优先的执行顺序。
变量{spring.application.name}和{spring.profiles.active},两者以“-”相连,构成了向 Config Server 读取的配置文件名。

1
2
3
4
5
6
7
8
9
spring:
application:
name: config-client
cloud:
config:
uri: http://localhost:8769
fail-fast: true #读取没有成功,执行快速失败
profiles:
active: dev

编写一个接口,测试读取配置文件的 foo 变量,并通过 API 接口返回.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@SpringBootApplication
@RestController
public class ConfigClientApplication {

public static void main(String[] args) {
SpringApplication.run(ConfigClientApplication.class, args);
}

@Value("${foo}")
String foo;

@RequestMapping(value = "/foo")
public String hi(){
return foo;
}
}

启动 config-client 工程,访问 http://localhost:8762/foo,显示

foo version 1

可见 config-client 成功向 config-server 工程读取了配置文件中 foo 变量的值。

Config Server从远程Git仓库读取配置文件

修改 config-server 的配置文件 application.yml,代码如下.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server:
port: 8769
spring:
application:
name: config-server
cloud:
config:
label: master
server:
git:
uri: https://github.com/forezp/SpringcloudConfig
search-paths: respo
username: miles02@163.com
password:

如果Git仓库为公开仓库,可以不填写用户名和密码,如果是私有仓库需要填写,本例子是公开仓库,放心使用。

配置 解释
spring.cloud.config.server.git.uri 配置git仓库地址
spring.cloud.config.server.git.searchPaths 配置仓库路径
spring.cloud.config.label 配置仓库的分支
spring.cloud.config.server.git.username 访问git仓库的用户名
spring.cloud.config.server.git.password 访问git仓库的用户密码

远程仓库 https://github.com/xxxx/SpringcloudConfig/ 中有个文件config-client-dev.properties文件中有一个属性:

foo = foo version 2

但是没有规定 server.port 属性,所以会以默认 的 8080 启动,启动程序:访问http://localhost:8080/foo

foo version 2

可见,config-server 从远程 Git 仓库读取了配置文件,config-client 从config-server 读取了配置文件.

构建高可用的 Config Server

将配置中心 config-server 做成一个微服务,并且将其集群化,从而达到高可用。

config-client 在工程启动类上加上注解 @EnableEurekaClient,开启 EurekaClient的功能。

在配置文件 application.yml 加入相关配置,向 service-id 为 config-server 的配置服务读取配置文件.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
spring:
application:
name: config-client
cloud:
config:
fail-fast: true
discovery:
enabled: true
service-id: config-server
profiles:
active: dev
server:
port: 8762
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/

启动 config-server、config-client 工程,访问 http://localhost:8762/foo,浏览器显:

foo version 2

只需要启动多个 config-server 实例即可搭建高可用的 config-server。

SpringCloud进阶之Ribbon和Feign(负载均衡)

Spring Cloud 进阶之Ribbon和Feign(负载均衡)

Ribbon 负载均衡

Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端,负载均衡的工具;

Ribbon 核心组件IRule

根据特定算法,从服务列表中选取一个要访问的服务:

  • RoundRobinRule:轮询
  • RandomRule:随机
  • AvailabilityFilteringRule: 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,以及并发的连接数量
    超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问;
  • WeightedResponseTimeRule: 根据平均响应时间计算所有服务的权重,响应时间越快,服务权重越大,被选中的机率越高;
    刚启动时,如果统计信息不足,则使用RoundRobinRule策略,等统计信息足够时,会切换到WeightedResponseTimeRule
  • RetryRule: 先按照RoundRobinRule的策略获取服务,如果获取服务失败,则在指定时间内会进行重试,获取可用的服务;
  • BestAvailableRule: 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务;
  • ZoneAvoidanceRule: 默认规则,复合判断server所在区域的性能和server的可用性选择服务器;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// ConfigBean 添加新注解 @LoadBalanced, 用于加入 Ribbon 配置
@Configuration
public class ConfigBean {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}

@Configuration
public class ConfigBean {

@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}

@Bean
public IRule myRule() {
return new RoundRobinRule(); // 显式的指定使用轮询算法
}
}


// 修改主启动类
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name="MICROSERVICECLOUD-DEPT", configuration=MySelfRule.class) // 自定义Ribbon配置类
public class DeptConsumer80_App {

public static void main(String[] args) {

SpringApplication.run(DeptConsumer80_App.class, args);
}

}


// com.noodles.myrule
// 自定义Robbin规则类
@Configuration
public class MySelfRule{
@Bean
public IRule myRule(){
return new RandomRule(); //自定义均衡策略
}
}

Feign 负载均衡

Feign 是一个声明式WebService客户端:
使用方法: 定义一个接口,然后在上面添加注解;

  • 首先通过 @EnableFeignClients 注解开启 FeignClient 的功能。只有这个注解存在,才会在程序启动时开启 @FeignClient 注解的包扫描。
  • 根据Feign的规则实现接口,并在接口上面加上 @FeignClient 注解。
  • 程序启动后,会进行包扫描,扫描所有的@FeignClient 的注解的类,并将这些信息注入 IOC容器中。
  • 当接口的方法被调用时,通过JDK的代理来生成具体的 RequestTemplate 模板对象。
  • 根据 RequestTemplate 再生成 Http 请求的 Request 对象。
  • Request 对象交给 Client 去处理,其中 Client 的网络请求框架可以是 HTTPURLConnection、HttpClient 和 OkHttp。
  • 最后Client被封装到LoadBalanceClient类,这个类结合类 Ribbon 做到了负载均衡。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 新建DeptClientService接口,并新增注解@FeignClient,来指定调用哪个服务
@FeignClient(value="MICROSERVICECLOUD-DEPT")
public interface DeptClientService {

@RequestMapping(value="/dept/get/{id}", method= RequestMethod.GET)
public Dept get(@PathVariable("id") long id);

@RequestMapping(value="/dept/list", method= RequestMethod.GET)
public List<Dept> list();

@RequestMapping(value="/dept/add", method= RequestMethod.POST)
public boolean add(Dept dept);
}

// microservice-consumer-dept-feign 工程修改Controller
@RestController
public class DeptController_Consumer {

//用于服务调用
@Autowired
private DeptClientService service;

@RequestMapping(value="/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id) {
return this.service.get(id);
}

@RequestMapping(value="/consumer/dept/list")
public List<Dept> list(){
return this.service.list();
}

@RequestMapping(value="/consumer/dept/add")
public Object add(Dept dept) {
return this.service.add(dept);
}
}

Sentinel快速入门

Sentinel快速入门

如下的代码任然是侵入式的使用方式,也提供了注解支持模块

应用使用 pom 工程,则在 pom.xml 文件中加入以下代码即可:

1
2
3
4
5
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.7.2</version>
</dependency>

First Demo 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package sentinel;

import java.util.ArrayList;
import java.util.List;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;

public class SentinelDemo {

public static void main(String[] args) {
initFlowRules();
while (true) {
Entry entry = null;
try {
entry = SphU.entry("HelloWorld");
/* 您的业务逻辑 - 开始 */
System.out.println("hello world");
/* 您的业务逻辑 - 结束 */
} catch (BlockException e1) {
/* 流控逻辑处理 - 开始 */
System.out.println("block!");
/* 流控逻辑处理 - 结束 */
} finally {
if (entry != null) {
entry.exit();
}
}
}
}

// 接下来,通过规则来指定允许该资源通过的请求次数
// 例如下面的代码定义了资源 HelloWorld 每秒最多只能通过 20 个请求。
private static void initFlowRules() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("HelloWorld");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// Set limit QPS to 20.
rule.setCount(20);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
}

Demo 运行之后,我们可以在日志里看到下面的输出:

1
2
3
4
5
6
7
8
9
10
11
window机器位于 C:\Users\shengwangzhong\logs\xxxx
~/logs/csp/${appName}-metrics.log.xxx

输出:
|--timestamp-|------date time----|-resource-|p |block|s |e|rt
1529998904000|2018-06-26 15:41:44|HelloWorld|20|0 |20|0|0
1529998905000|2018-06-26 15:41:45|HelloWorld|20|5579 |20|0|728
1529998906000|2018-06-26 15:41:46|HelloWorld|20|15698|20|0|0
1529998907000|2018-06-26 15:41:47|HelloWorld|20|19262|20|0|0
1529998908000|2018-06-26 15:41:48|HelloWorld|20|19502|20|0|0
1529998909000|2018-06-26 15:41:49|HelloWorld|20|18386|20|0|0

其中 p 代表通过的请求, block 代表被阻止的请求, s 代表成功执行完成的请求个数, e 代表用户自定义的异常, rt 代表平均响应时长。

结合控制台使用:

首先需要保证控制台处于启动状态:

1
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar

其中 -Dserver.port=8080 用于指定 Sentinel 控制台端口为 8080

此外针对上面的应用需要添加依赖:

1
2
3
4
5
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>1.7.2</version>
</dependency>

同时需要添加程序启动参数:

1
2
3
-Dcsp.sentinel.dashboard.server=consoleIp:port
↓↓↓↓↓↓↓
-Dcsp.sentinel.dashboard.server=localhost:8080

若启动多个应用,则需要通过 -Dcsp.sentinel.api.port=xxxx 指定客户端监控 API 的端口(默认是 8719)。

这时候,在启动程序以后,可以再dashboard页面能对具体的应用进行实时监控。

Spring Cloud Alibaba 组件

Spring Cloud Alibaba 组件

  1. Sentinel:把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

  2. Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。

  3. RocketMQ:一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。

  4. Dubbo:Apache Dubbo™ 是一款高性能 Java RPC 框架。

  5. Seata:阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。

  6. Alibaba Cloud ACM:一款在分布式架构环境中对应用配置进行集中管理和推送的应用配置中心产品。

  7. Alibaba Cloud OSS: 阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。

  8. Alibaba Cloud SchedulerX: 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。

  9. Alibaba Cloud SMS: 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。

熔断

熔断

什么是服务雪崩

在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以通过 HTTP/RPC 相互调用,在 Spring Cloud 中可以用 ==RestTemplate== + ==LoadBalanceClient== 和 ==Feign== 来调用。为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证 100% 可用,如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,Servlet 容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的 “雪崩” 效应。为了解决这个问题,业界提出了 ++熔断器模型++。

Spring Cloud微服务设计中如何才能实现这样的机制呢?这里涉及到几个问题:

  • 微服务如何定义为故障,熔断的条件是什么?如何确定当前调用的微服务不可用,从而可以实现熔断操作;

  • 被定义为故障的微服务恢复后如何让熔断方感知?当前微服务何时才可以继续正常的调用需要调用的其他微服务,实现故障恢复;

Spring Cloud的代码实现机制是什么样的?

在Spring Cloud微服务设计中需要通过集成Hystrix框架来实现微服务间的熔断保护机制,Hystrix框架会通过监控微服务之间的调用情况,来决定是否启动熔断保护。

Sentinel 注解式支持

Sentinel 注解式支持

@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource 注解包含以下属性:

  • value:资源名称,必需项(不能为空)
  • entryType:entry 类型,可选项(默认为 EntryType.OUT)
  • blockHandler / blockHandlerClass

blockHandler 对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。

  • fallback / fallbackClass

fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。
fallback 函数签名和位置要求:

  1. 返回值类型必须与原函数返回值类型一致;
  2. 方法参数列表需要和原函数一致,或者可以额外多一个Throwable类型的参数用于接收对应的异常。
  3. fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • defaultFallback(since 1.6.0):

默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。

defaultFallback 函数签名要求:

  1. 返回值类型必须与原函数返回值类型一致;
  2. 方法参数列表需要为空,或者可以额外多一个Throwable类型的参数用于接收对应的异常。
  3. defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • exceptionsToIgnore:用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

public class TestService {

// 对应的 `handleException` 函数需要位于 `ExceptionUtil` 类中,并且必须为 static 函数.
@SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = {ExceptionUtil.class})
public void test() {
System.out.println("Test");
}

// 原函数
@SentinelResource(value = "hello", blockHandler = "exceptionHandler", fallback = "helloFallback")
public String hello(long s) {
return String.format("Hello at %d", s);
}

// Fallback 函数,函数签名与原函数一致或加一个 Throwable 类型的参数.
public String helloFallback(long s) {
return String.format("Halooooo %d", s);
}

// Block 异常处理函数,参数最后多一个 BlockException,其余与原函数一致.
public String exceptionHandler(long s, BlockException ex) {
// Do some log here.
ex.printStackTrace();
return "Oops, error occurred at " + s;
}
}

SpringCloud Alibaba

SpringCloud Alibaba

SpringCloud Alibaba

官方文档:
README

  1. 服务限流降级

默认支持 WebServlet、WebFlux, OpenFeign、RestTemplate、Spring Cloud Gateway, Zuul, Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。

  1. 服务注册与发现

适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。

  1. 分布式配置管理

支持分布式系统中的外部化配置,配置更改时自动刷新。

  1. 消息驱动能力

基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。

  1. 分布式事务
    使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。

  2. 阿里云对象存储

阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。

  1. 分布式任务调度

提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。

  1. 阿里云短信服务

覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。

Sentinel 主流框架的适配

Sentinel 主流框架的适配

适配

为了减少开发的复杂程度,Sentinel对大部分的主流框架,例如 Web Servlet、Dubbo、Spring Cloud、gRPC、Spring WebFlux、Reactor 等都做了适配。您只需要引入对应的依赖即可方便地整合 Sentinel。
包括以下配置:

Web 适配

  • Spring Boot/Spring Cloud
  • Web Servlet
  • Spring WebFlux

    RPC 适配

  • Apache Dubbo
  • gRPC
  • Feign
  • SOFARPC

    HTTP client 适配

  • Apache HttpClient
  • OkHttp
  • Reactive 适配
  • Reactor

    API Gateway 适配

  • Spring Cloud Gateway
  • Netflix Zuul 1.x
  • Netflix Zuul 2.x
  • Apache RocketMQ