Spring Cloud Alibaba

7/6/2021 SpringCloud Alibaba

# 简介

Spring Cloud Alibaba旨在为微服务开发提供一站式解决方案。

说到Spring Cloud Alibaba就不得不提到Dubbo,Dubbo 项目诞生于 2008 年,然后在2014年停更,最后又在2017年重获新生,可谓是一波三折。

接着在2018年,Spring Cloud Alibaba正式进入Spring Cloud官方孵化器,并在Maven中央库发布了第一个版本。

正好Netflix的一些组件(Spring Cloud官方整合的组件很多是Netflix开源的,比如Eureka、Zuul、Hystrix等)也不再维护,所以学习Spring Cloud Alibaba也就提上日程了。

以下案例均采用Spring Cloud Alibaba 2.2.1.RELEASE版本

版本说明参考 (opens new window)

pom.xml

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>2.2.1.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
1
2
3
4
5
6
7
8
9
10
11

# 核心组件

  1. Nacos:服务发现与注册、分布式配置。
  2. Dubbo:服务调用
  3. Sentinel:服务熔断
  4. Seate:分布式事务
  5. RocketMQ:分布式消息

# Nacos

Nacos 是一个易于使用的动态服务发现、配置和服务管理平台,用于构建云原生应用

Nacos Spring Cloud 快速开始 (opens new window)

# 服务端下载及运行

  1. 下载nacos服务端,你有以下两种方式获取

git clone https://github.com/alibaba/nacos.git
cd nacos/
mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U  
ls -al distribution/target/

// change the $version to your actual path
cd distribution/target/nacos-server-$version/nacos/bin
1
2
3
4
5
6
7
  1. 启动Nacos服务端

    以下命令均在bin目录中

    Linux/Unix/Mac

    启动命令(standalone代表着单机模式运行,非集群模式):

sh startup.sh -m standalone
1

如果您使用的是ubuntu系统,或者运行脚本报错提示[[符号找不到,可尝试如下运行:bash startup.sh -m standalone

Windows

启动命令(standalone代表着单机模式运行,非集群模式):

startup.cmd -m standalone
1

需要关闭则使用shutdown命令即可。

# Nacos配置管理

在Nacos server启动之后便可以通过ip+端口打开nacos web页面了,用户名密码均为nacos。

进入管理界面添加一个配置文件,dataID为nacosconfig

配置如下

useLocalCache: test
1

记得发布。

代码中获取nacos的配置

pom.xml(默认已经添加springcloud / springboot

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
1
2
3
4

bootstrap.propertiesbootstrap.properties先于 application.properties 加载)

# 服务端的ip端口
spring.cloud.nacos.config.server-addr=192.168.191.1:8848
# dataID,用于确定配置
spring.application.name=nacosconfig
# 配置文件格式
spring.cloud.nacos.config.file-extension=yaml
1
2
3
4
5
6

ConfigController

@RestController
@RequestMapping("/config")
@RefreshScope // Spring Cloud 原生注解,实现配置自动更新
public class ConfigController {

    @Value("${useLocalCache:''}")
    private String useLocalCache;

    @RequestMapping("/get")
    public String get() {
        return useLocalCache;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

启动SpringBoot主程序,查看/config/get接口,发现可以获取远程配置。回到nacos web管理界面修改test为test1,刷新接口,发现值已自动修改。

# Nacos服务发现与注册

启动Nacos Server

首先创建两个模块,一个为服务提供者,另一个为服务消费者。

在服务提供端和服务消费端均添加spring-cloud-starter-alibaba-nacos-discovery依赖,默认已添加SpringBoot依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
1
2
3
4

服务提供端 application.propertiies

server.port=8070
spring.application.name=service-provider

spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
1
2
3
4

服务提供端 SpringBoot启动类

@SpringBootApplication
@EnableDiscoveryClient
public class NacosProviderApplication {

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

	@RestController
	class EchoController {
		@RequestMapping(value = "/echo/{string}", method = RequestMethod.GET)
		public String echo(@PathVariable String string) {
			return "Hello Nacos Discovery " + string;
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

服务消费端 application.propertiies

server.port=8080
spring.application.name=service-consumer

spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
1
2
3
4

服务消费端 SpringBoot启动类

@SpringBootApplication
@EnableDiscoveryClient
public class NacosConsumerApplication {

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

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

    @RestController
    public class TestController {

        private final RestTemplate restTemplate;

        @Autowired
        public TestController(RestTemplate restTemplate) {this.restTemplate = restTemplate;}

        @RequestMapping(value = "/echo/{str}", method = RequestMethod.GET)
        public String echo(@PathVariable String str) {
            return restTemplate.getForObject("http://service-provider/echo/" + str, String.class);
        }
    }
}
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

最后,启动 ProviderApplicationConsumerApplication ,调用 http://localhost:8080/echo/2018 (opens new window),返回内容为 Hello Nacos Discovery 2018。

此时也可以打开Nacos web管理界面地址,查看服务列表。

# Sentinel

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

# WebFlux简介

在介绍Sentinel之前,我想提一下Spring WebFlux(仅了解一下概念),因为Sentinel目前已经支持Spring WebFlux,而该框架是Spring 5推出的一个的非阻塞,响应式Web框架

响应式编程(reactive programming)是一种基于数据流(data stream)和变化传递(propagation of change)的声明式(declarative)的编程范式。

该框架类似于Spring MVC,不同之处在于它是非阻塞、响应式的Web框架,因此相较于Spring MVC,Spring WebFlux有着很大的性能优势。这并不意味着你需要马上切换到Spring WebFlux,因为响应式编程就意味着它会和许多以前阻塞式框架搭配起来不工作或者效果很差(可以但没有必要),但如果你觉得你已经完成准备好了应付非阻塞式系统及响应式编程,并且你的系统确实能通过它提升巨大的性能,就可以考虑切换到响应式编程的分支来。

另外Spring WebFlux提升的并不是单个接口的响应速率,而是提高整个系统的吞吐量。

举个例子,假设一个接口正在等待一个需要非常长时间才能响应的处理(访问大文件或者数据库全表查询之类),对于阻塞式的架构来说,此时线程卡在这里但是什么事也干不了,但对于非阻塞式架构,它就可以在此时将线程使用权让出来交给其它请求去使用。

限于篇幅,这里不多赘述,有个基本概念就好。

对于想从Spring MVC切换到Spring WebFlux的SpringBoot用户来说,只需要将artifactId从spring-boot-starter-web改成spring-boot-starter-webflux即可。

注意

在Spring MVC模式下,默认采用Servlet + Tomcat

而在Spring WebFlux下,默认采用Reactor + Netty

# Quick Start

Sentinel提供了两种方式来进行接入,一种是无侵入式接入,另一个则是代码手动接入。

无侵入接入

若希望无侵入将已有服务接入 Sentinel 来快速体验 Sentinel 全方位的流量防护和可观测能力,推荐采用 Java Agent 方式 (opens new window)快速、无侵入将应用接入到云上 AHAS 控制台 (opens new window)来体验 Sentinel 的能力。

代码手动接入

首先引入依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
1
2
3
4

通过@SentinelResource标识Sentinel资源,最常见的就是方法。

public class TestService {

    // 原函数
    @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;
    }

    // 这里单独演示 blockHandlerClass 的配置.
    // 对应的 `handleException` 函数需要位于 `ExceptionUtil` 类中,并且必须为 public static 函数.
    @SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = {ExceptionUtil.class})
    public void test() {
        System.out.println("Test");
    }
}
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

细节参考,Sentinel注解支持 (opens new window)

控制面板

release 页面 (opens new window) 下载最新版本的控制台 jar 包。

启动 (这里命令没有加上版本,自行删掉下载jar的版本号或者给命令加上版本)

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

从 Sentinel 1.6.0 起,Sentinel 控制台引入基本的登录功能,默认用户名和密码都是 sentinel。可以参考 鉴权模块文档 (opens new window) 配置用户名和密码。

application.properties

spring.cloud.sentinel.transport.port=8719
spring.cloud.sentinel.transport.dashboard=localhost:8080
1
2

确保客户端有访问量,Sentinel 会在客户端首次调用的时候进行初始化,开始向控制台发送心跳包。

更多细节参考Sentinel控制面板 (opens new window)

主流框架整合Demo:Github仓库 (opens new window)

文档参考:官方文档站 (opens new window)Github Wiki (opens new window)Spring Cloud Alibaba #Sentinel (opens new window)