用户您好!请先登录!

Spring Cloud Alibaba GA且正式发布了

Spring Cloud Alibaba GA且正式发布了

无论怎么看,Alibaba都是在向Netflix致敬,Spring Cloud终于在国内也有替代的正式的,且开源解决方案,作为产品簇从Apache孵化器毕业真是一件了不起的事。

Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。

依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。

主要功能

  • 服务限流降级:默认支持 WebServlet、WebFlux, OpenFeign、RestTemplate、Spring Cloud Gateway, Zuul, Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。
  • 服务注册与发现:适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。
  • 分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。
  • 消息驱动能力:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
  • 分布式事务:使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。。
  • 阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
  • 分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。
  • 阿里云短信服务:覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。

组件

  • Sentinel:把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
  • Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
  • RocketMQ:一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。
  • Dubbo:Apache Dubbo™ 是一款高性能 Java RPC 框架。
  • Seata:阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。
  • Alibaba Cloud ACM:一款在分布式架构环境中对应用配置进行集中管理和推送的应用配置中心产品。
  • Alibaba Cloud OSS: 阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
  • Alibaba Cloud SchedulerX: 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。
  • Alibaba Cloud SMS: 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。

如何使用

如何引入依赖,如果需要使用已发布的版本,在 dependencyManagement 中添加如下配置。

<dependencyManagement>
 <dependencies>
 <dependency>
 <groupId>com.alibaba.cloud</groupId>
 <artifactId>spring-cloud-alibaba-dependencies</artifactId>
 <version>2.1.0.RELEASE</version>
 <type>pom</type>
 <scope>import</scope>
 </dependency>
 </dependencies>
</dependencyManagement>

然后在 dependencies 中添加自己所需使用的依赖即可使用。

演示 Demo

Sentinel Example

项目说明

本项目演示如何使用 Sentinel starter 完成 Spring Cloud 应用的限流管理。

Sentinel 是阿里巴巴开源的分布式系统的流量防卫组件,Sentinel 把流量作为切入点,从流量控制,熔断降级,系统负载保护等多个维度保护服务的稳定性。

示例

如何接入

在启动示例进行演示之前,我们先了解一下如何接入 Sentinel。

注意:本章节只是为了便于您理解接入方式,本示例代码中已经完成接入工作,您无需再进行修改。

  1. 首先,修改 pom.xml 文件,引入 Sentinel starter。
<dependency>
 <groupId>com.alibaba.cloud</groupId>
 <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
  1. 接入限流埋点
  • HTTP 埋点
  • Sentinel starter 默认为所有的 HTTP 服务提供了限流埋点,如果只想对 HTTP 服务进行限流,那么只需要引入依赖,无需修改代码。
  • 自定义埋点
  • 如果需要对某个特定的方法进行限流或降级,可以通过 @SentinelResource 注解来完成限流的埋点,示例代码如下:
 @SentinelResource("resource")
 public String hello() {
 return "Hello";
 }
  • 当然也可以通过原始的 SphU.entry(xxx) 方法进行埋点,可以参见 Sentinel 文档。
  1. 配置限流规则
  2. Sentinel 提供了两种配置限流规则的方式:代码配置 和 控制台配置。本示例使用的方式为通过控制台配置。
  3. 通过代码来实现限流规则的配置。一个简单的限流规则配置示例代码如下,更多限流规则配置详情请参考 Sentinel 文档。
List<FlowRule> rules = new ArrayList<FlowRule>();
FlowRule rule = new FlowRule();
rule.setResource(str);
// set limit qps to 10
rule.setCount(10);
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setLimitApp("default");
rules.add(rule);
FlowRuleManager.loadRules(rules);
  1. 通过控制台进行限流规则配置请参考文章后面的图文说明。

启动 Sentinel 控制台

  1. 首先需要获取 Sentinel 控制台,支持直接下载和源码构建两种方式。
  2. 直接下载:下载 Sentinel 控制台
  3. 源码构建:进入 Sentinel Github 项目页面,将代码 git clone 到本地自行编译打包,参考此文档。
  4. 启动控制台,执行 Java 命令 java -jar sentinel-dashboard.jar完成 Sentinel 控制台的启动。 控制台默认的监听端口为 8080。Sentinel 控制台使用 Spring Boot 编程模型开发,如果需要指定其他端口,请使用 Spring Boot 容器配置的标准方式,详情请参考 Spring Boot 文档。

应用启动

  1. 增加配置,在应用的 /src/main/resources/application.properties 中添加基本配置信息
spring.application.name=sentinel-example
server.port=18083
spring.cloud.sentinel.transport.dashboard=localhost:8080
  1. 启动应用,支持 IDE 直接启动和编译打包后启动。
  2. IDE直接启动:找到主类 ServiceApplication,执行 main 方法启动应用。
  3. 打包编译后启动:首先执行 mvn clean package 将工程编译打包,然后执行 java -jar sentinel-core-example.jar启动应用。

调用服务

使用 curl 分别调用两个 URL,可以看到访问成功。

配置限流规则并验证

  1. 访问 http://localhost:8080 页面,可以在左侧看到 Sentinel-Example 应用已经注册到了控制台,单击 流控规则 ,可以看到目前的流控规则为空。

注意:如果您在控制台没有找到应用,请调用一下进行了 Sentinel 埋点的 URL 或方法,因为 Sentinel 使用了 lazy load 策略。详细的排查过程请参见 Sentinel FAQ。

  1. 配置 URL 限流规则:点击新增流控规则,资源名填写需要限流的 URL 相对路径,单机阈值选择需要限流的阈值,点击新增进行确认。(为了便于演示效果,这里将值设置成了 1)。
  1. 配置自定义限流规则:点击新增流控规则,资源名填写 @SentinelResource 注解 value 字段的值,单机阈值选择需要限流的阈值,点击新增进行确认。(为了便于演示效果,这里将值设置成了 1)。
  1. 访问 URL,当 QPS 超过 1 时,可以看到限流效果如下。

自定义限流处理逻辑

  • 默认限流异常处理

URL 限流触发后默认处理逻辑是,直接返回 “Blocked by Sentinel (flow limiting)”。 如果需要自定义处理逻辑,实现的方式如下:

public class CustomUrlBlockHandler implements UrlBlockHandler {
 @Override
 public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException {
 // todo add your logic
 }
}
WebCallbackManager.setUrlBlockHandler(new CustomUrlBlockHandler());
  • 使用 @SentinelResource 注解下的限流异常处理

如果需要自定义处理逻辑,填写 @SentinelResource 注解的 blockHandler 属性(针对所有类型的 BlockException,需自行判断)或 fallback 属性(针对熔断降级异常),注意对应方法的签名和位置有限制,详情见 Sentinel 注解支持文档。示例实现如下:

public class TestService {
 // blockHandler 是位于 ExceptionUtil 类下的 handleException 静态方法,需符合对应的类型限制.
 @SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = {ExceptionUtil.class})
 public void test() {
 System.out.println("Test");
 }
 // blockHandler 是位于当前类下的 exceptionHandler 方法,需符合对应的类型限制.
 @SentinelResource(value = "hello", blockHandler = "exceptionHandler")
 public String hello(long s) {
 return String.format("Hello at %d", s);
 }
 public String exceptionHandler(long s, BlockException ex) {
 // Do some log here.
 ex.printStackTrace();
 return "Oops, error occurred at " + s;
 }
}
public final class ExceptionUtil {
 public static void handleException(BlockException ex) {
 System.out.println("Oops: " + ex.getClass().getCanonicalName());
 }
}

一个简单的 @SentinelResource 示例可以见 sentinel-demo-annotation-spring-aop。

Endpoint 信息查看

Spring Boot 应用支持通过 Endpoint 来暴露相关信息,Sentinel Starter 也支持这一点。

在使用之前需要在 Maven 中添加 spring-boot-starter-actuator依赖,并在配置中允许 Endpoints 的访问。

  • Spring Boot 1.x 中添加配置 management.security.enabled=false
  • Spring Boot 2.x 中添加配置 management.endpoints.web.exposure.include=*

Spring Boot 1.x 可以通过访问 http://127.0.0.1:18083/sentinel 来查看 Sentinel Endpoint 的信息。Spring Boot 2.x 可以通过访问 http://127.0.0.1:18083/actuator/sentinel 来访问。

查看实时监控

Sentinel 控制台支持实时监控查看,您可以通过 Sentinel 控制台查看各链路的请求的通过数和被限流数等信息。 其中 p_qps 为通过(pass) 流控的 QPS,b_qps 为被限流 (block) 的 QPS。

ReadableDataSource 支持

Sentinel 内部提供了动态规则的扩展实现 ReadableDataSource。

Sentinel starter 整合了目前存在的几类 ReadableDataSource。只需要在配置文件中进行相关配置,即可在 Spring 容器中自动注册 DataSource。

比如要定义两个ReadableDataSource,分别是 FileRefreshableDataSource 和 NacosDataSource,配置如下:

spring.cloud.sentinel.datasource.ds1.file.file=classpath: degraderule.json
spring.cloud.sentinel.datasource.ds1.file.data-type=json
spring.cloud.sentinel.datasource.ds2.nacos.server-addr=localhost:8848
spring.cloud.sentinel.datasource.ds2.nacos.dataId=sentinel
spring.cloud.sentinel.datasource.ds2.nacos.groupId=DEFAULT_GROUP
spring.cloud.sentinel.datasource.ds2.nacos.data-type=json

ds1 和 ds2 表示ReadableDataSource的名称,可随意编写。ds1 和 ds2 后面的 file 和 nacos 表示ReadableDataSource的类型。

目前支持file, nacos, zk, apollo,redis 这5种类型。

其中nacos,zk,apollo,redis 这4种类型的使用需要加上对应的依赖sentinel-datasource-nacos, sentinel-datasource-zookeeper, sentinel-datasource-apollo, sentinel-datasource-redis。

当ReadableDataSource加载规则数据成功的时候,控制台会打印出相应的日志信息:

[Sentinel Starter] DataSource ds1-sentinel-file-datasource load 3 DegradeRule
[Sentinel Starter] DataSource ds2-sentinel-nacos-datasource load 2 FlowRule

Nacos Config Example

项目说明

本项目演示如何使用 Nacos Config Starter 完成 Spring Cloud 应用的配置管理。

Nacos 是阿里巴巴开源的一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。

示例

如何接入

在启动示例进行演示之前,我们先了解一下 Spring Cloud 应用如何接入 Nacos Config。 注意 本章节只是为了便于您理解接入方式,本示例代码中已经完成接入工作,您无需再进行修改。

  1. 首先,修改 pom.xml 文件,引入 Nacos Config Starter。
 <dependency>
 <groupId>com.alibaba.cloud</groupId>
 <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
 </dependency>
  1. 在应用的 /src/main/resources/bootstrap.properties 配置文件中配置 Nacos Config 元数据
 spring.application.name=nacos-config-example
 spring.cloud.nacos.config.server-addr=127.0.0.1:8848
  1. 完成上述两步后,应用会从 Nacos Config 中获取相应的配置,并添加在 Spring Environment 的 PropertySources 中。这里我们使用 @Value 注解来将对应的配置注入到 SampleController 的 userName 和 age 字段,并添加 @RefreshScope 打开动态刷新功能
 @RefreshScope
 class SampleController {
    @Value("${user.name}")
    String userName;
    @Value("${user.age}")
    int age;
 }

启动 Nacos Server 并添加配置

  1. 首先需要获取 Nacos Server,支持直接下载和源码构建两种方式。推荐使用最新版本 Nacos Server
  2. 直接下载:Nacos Server 下载页
  3. 源码构建:进入 Nacos Github 项目页面,将代码 git clone 到本地自行编译打包,参考此文档。
  4. 启动 Server,进入下载到本地并解压完成后的文件夹(使用源码构建的方式则进入编译打包好的文件夹),再进去其相对文件夹 nacos/bin,并对照操作系统实际情况执行如下命令。详情参考此文档。
  5. Linux/Unix/Mac 操作系统,执行命令 sh startup.sh -m standalone
  6. Windows 操作系统,执行命令 cmd startup.cmd
  7. 在命令行执行如下命令,向 Nacos Server 中添加一条配置。
 curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos-config-example.properties&group=DEFAULT_GROUP&content=user.id=1%0Auser.name=james%0Auser.age=17"
  1. 注:你也可以使用其他方式添加,遵循 HTTP API 规范即可,若您使用的 Nacos 版本自带控制台,建议直接使用控制台进行配置
  2. 添加的配置的详情如下
 dataId 为 nacos-config-example.properties
 group 为 DEFAULT_GROUP
 
 内容如下
 
    user.id=1
    user.name=james
    user.age=17

应用启动

  1. 增加配置,在应用的 /src/main/resources/application.properties 中添加基本配置信息
 server.port=18084
 management.endpoints.web.exposure.include=*
  1. 启动应用,支持 IDE 直接启动和编译打包后启动。
  2. IDE直接启动:找到主类 Application,执行 main 方法启动应用。
  3. 打包编译后启动:首先执行 mvn clean package 将工程编译打包,然后执行 java -jar nacos-config-example.jar启动应用。

验证

验证自动注入

在浏览器地址栏输入 http://127.0.0.1:18084/user,并点击调转,可以看到成功从 Nacos Config Server 中获取了数据。

微服务开发的一站式解决方案Spring Cloud Alibaba

验证动态刷新

  1. 执行如下命令,修改 Nacos Server 端的配置数据
 curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos-config-example.properties&group=DEFAULT_GROUP&content=user.id=1%0Auser.name=james%0Auser.age=18"
  1. 在浏览器地址栏输入 http://127.0.0.1:18084/user,并点击调转,可以看到应用从 Nacos Server 中获取了最新的数据,age 变成了 18。
微服务开发的一站式解决方案Spring Cloud Alibaba

原理

Nacos Config 数据结构

Nacos Config 主要通过 dataId 和 group 来唯一确定一条配置,我们假定你已经了解此背景。如果不了解,请参考 Nacos 文档。

Nacos Client 从 Nacos Server 端获取数据时,调用的是此接口 ConfigService.getConfig(String dataId, String group, long timeoutMs)。

Spring Cloud 应用获取数据

dataID

在 Nacos Config Starter 中,dataId 的拼接格式如下

${prefix} - ${spring.profiles.active} . ${file-extension}
  • prefix 默认为 spring.application.name 的值,也可以通过配置项 spring.cloud.nacos.config.prefix来配置。
  • spring.profiles.active 即为当前环境对应的 profile,详情可以参考 Spring Boot文档
  • 注意,当 activeprofile 为空时,对应的连接符 – 也将不存在,dataId 的拼接格式变成 ${prefix}.${file-extension}
  • file-extension 为配置内容的数据格式,可以通过配置项 spring.cloud.nacos.config.file-extension来配置。 目前只支持 properties 类型。

group

  • group 默认为 DEFAULT_GROUP,可以通过 spring.cloud.nacos.config.group 配置。

自动注入

Nacos Config Starter 实现了 org.springframework.cloud.bootstrap.config.PropertySourceLocator接口,并将优先级设置成了最高。

在 Spring Cloud 应用启动阶段,会主动从 Nacos Server 端获取对应的数据,并将获取到的数据转换成 PropertySource 且注入到 Environment 的 PropertySources 属性中,所以使用 @Value 注解也能直接获取 Nacos Server 端配置的内容。

动态刷新

Nacos Config Starter 默认为所有获取数据成功的 Nacos 的配置项添加了监听功能,在监听到服务端配置发生变化时会实时触发 org.springframework.cloud.context.refresh.ContextRefresher 的 refresh 方法 。

如果需要对 Bean 进行动态刷新,请参照 Spring 和 Spring Cloud 规范。推荐给类添加 @RefreshScope 或 @ConfigurationProperties 注解,

更多详情请参考 ContextRefresher Java Doc。

Endpoint 信息查看

Spring Boot 应用支持通过 Endpoint 来暴露相关信息,Nacos Config Starter 也支持这一点。

在使用之前需要在 maven 中添加 spring-boot-starter-actuator依赖,并在配置中允许 Endpoints 的访问。

  • Spring Boot 1.x 中添加配置 management.security.enabled=false
  • Spring Boot 2.x 中添加配置 management.endpoints.web.exposure.include=*

Spring Boot 1.x 可以通过访问 http://127.0.0.1:18084/nacos_config 来查看 Nacos Endpoint 的信息。

Spring Boot 2.x 可以通过访问 http://127.0.0.1:18084/actuator/nacos-config 来访问。

微服务开发的一站式解决方案Spring Cloud Alibaba

如上图所示,Sources 表示此客户端从哪些 Nacos Config 配置项中获取了信息,RefreshHistory 表示动态刷新的历史记录,最多保存20条,NacosConfigProperties 则为 Nacos Config Starter 本身的配置。

Nacos Discovery Example

项目说明

本项目演示如何使用 Nacos Discovery Starter 完成 Spring Cloud 应用的服务注册与发现。

Nacos 是阿里巴巴开源的一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。

示例

如何接入

在启动示例进行演示之前,我们先了解一下 Spring Cloud 应用如何接入 Nacos Discovery。 注意 本章节只是为了便于您理解接入方式,本示例代码中已经完成接入工作,您无需再进行修改。

  1. 首先,修改 pom.xml 文件,引入 Nacos Discovery Starter。
 <dependency>
 <groupId>com.alibaba.cloud</groupId>
 <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
 </dependency>
  1. 在应用的 /src/main/resources/application.properties 配置文件中配置 Nacos Server 地址
 spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
  1. 使用 @EnableDiscoveryClient 注解开启服务注册与发现功能
 @SpringBootApplication
 @EnableDiscoveryClient
 public class ProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    @RestController
    class EchoController {
        @RequestMapping(value = "/echo/{string}", method = RequestMethod.GET)
        public String echo(@PathVariable String string) {
                return string;
        }
    }
 }

启动 Nacos Server

  1. 首先需要获取 Nacos Server,支持直接下载和源码构建两种方式。
  2. 直接下载:Nacos Server 下载页
  3. 源码构建:进入 Nacos Github 项目页面,将代码 git clone 到本地自行编译打包,参考此文档。推荐使用源码构建方式以获取最新版本
  4. 启动 Server,进入解压后文件夹或编译打包好的文件夹,找到如下相对文件夹 nacos/bin,并对照操作系统实际情况之下如下命令。
  5. Linux/Unix/Mac 操作系统,执行命令 sh startup.sh -m standalone
  6. Windows 操作系统,执行命令 cmd startup.cmd

应用启动

  1. 增加配置,在 nacos-discovery-provider-example 项目的 /src/main/resources/application.properties 中添加基本配置信息
 spring.application.name=service-provider
 server.port=18082
  1. 启动应用,支持 IDE 直接启动和编译打包后启动。
  2. IDE直接启动:找到 nacos-discovery-provider-example 项目的主类 ProviderApplication,执行 main 方法启动应用。
  3. 打包编译后启动:在 nacos-discovery-provider-example 项目中执行 mvn clean package 将工程编译打包,然后执行 java -jar nacos-discovery-provider-example.jar启动应用。

验证

查询服务

在浏览器输入此地址 http://127.0.0.1:8848/nacos/v1/ns/catalog/instances?serviceName=service-provider&clusterName=DEFAULT&pageSize=10&pageNo=1&namespaceId=,并点击跳转,可以看到服务节点已经成功注册到 Nacos Server。

微服务开发的一站式解决方案Spring Cloud Alibaba

服务发现

集成 Ribbon

为了便于使用,NacosServerList 实现了 com.netflix.loadbalancer.ServerList 接口,并在 @ConditionOnMissingBean 的条件下进行自动注入。如果您有定制化的需求,可以自己实现自己的 ServerList。

Nacos Discovery Starter 默认集成了 Ribbon ,所以对于使用了 Ribbon 做负载均衡的组件,可以直接使用 Nacos 的服务发现。

使用 RestTemplate 和 FeignClient

下面将分析 nacos-discovery-consumer-example 项目的代码,演示如何 RestTemplate 与 FeignClient。

注意 本章节只是为了便于您理解接入方式,本示例代码中已经完成接入工作,您无需再进行修改。此处只涉及Ribbon、RestTemplate、FeignClient相关的内容,如果已经使用了其他服务发现组件,可以通过直接替换依赖来接入 Nacos Discovery。

  1. 添加 @LoadBlanced 注解,使得 RestTemplate 接入 Ribbon
 @Bean
 @LoadBalanced
 public RestTemplate restTemplate() {
 return new RestTemplate();
 }
  1. FeignClient 已经默认集成了 Ribbon ,此处演示如何配置一个 FeignClient。
 @FeignClient(name = "service-provider")
 public interface EchoService {
 @RequestMapping(value = "/echo/{str}", method = RequestMethod.GET)
 String echo(@PathVariable("str") String str);
 }
  1. 使用 @FeignClient 注解将 EchoService 这个接口包装成一个 FeignClient,属性 name 对应服务名 service-provider。
  2. echo 方法上的 @RequestMapping 注解将 echo 方法与 URL “/echo/{str}” 相对应,@PathVariable 注解将 URL 路径中的 {str} 对应成 echo 方法的参数 str。
  3. 完成以上配置后,将两者自动注入到 TestController 中。
 @RestController
 public class TestController {
 
 @Autowired
 private RestTemplate restTemplate;
 @Autowired
 private EchoService echoService;
 
 @RequestMapping(value = "/echo-rest/{str}", method = RequestMethod.GET)
 public String rest(@PathVariable String str) {
 return restTemplate.getForObject("http://service-provider/echo/" + str, String.class);
 }
 @RequestMapping(value = "/echo-feign/{str}", method = RequestMethod.GET)
 public String feign(@PathVariable String str) {
 return echoService.echo(str);
 }
 }
  1. 配置必要的配置,在 nacos-discovery-consumer-example 项目的 /src/main/resources/application.properties 中添加基本配置信息
 spring.application.name=service-consumer
 server.port=18083
  1. 启动应用,支持 IDE 直接启动和编译打包后启动。
  2. IDE直接启动:找到 nacos-discovery-consumer-example 项目的主类 ConsumerApplication,执行 main 方法启动应用。
  3. 打包编译后启动:在 nacos-discovery-consumer-example 项目中执行 mvn clean package 将工程编译打包,然后执行 java -jar nacos-discovery-consumer-example.jar启动应用。

验证

  1. 在浏览器地址栏中输入 http://127.0.0.1:18083/echo-rest/1234,点击跳转,可以看到浏览器显示了 nacos-discovery-provider-example 返回的消息 “hello Nacos Discovery 1234″,证明服务发现生效。
微服务开发的一站式解决方案Spring Cloud Alibaba
  1. 在浏览器地址栏中输入 http://127.0.0.1:18083/echo-feign/12345,点击跳转,可以看到浏览器显示 nacos-discovery-provider-example 返回的消息 “hello Nacos Discovery 12345″,证明服务发现生效。
微服务开发的一站式解决方案Spring Cloud Alibaba

原理

服务注册

Spring Cloud Nacos Discovery 遵循了 spring cloud common 标准,实现了 AutoServiceRegistration、ServiceRegistry、Registration 这三个接口。

在 spring cloud 应用的启动阶段,监听了 WebServerInitializedEvent 事件,当Web容器初始化完成后,即收到 WebServerInitializedEvent 事件后,会触发注册的动作,调用 ServiceRegistry 的 register 方法,将服务注册到 Nacos Server。

服务发现

NacosServerList 实现了 com.netflix.loadbalancer.ServerList 接口,并在 @ConditionOnMissingBean 的条件下进行自动注入,默认集成了Ribbon。

如果需要有更加自定义的可以使用 @Autowired 注入一个 NacosRegistration 实例,通过其持有的 NamingService 字段内容直接调用 Nacos API。

Endpoint 信息查看

Spring Boot 应用支持通过 Endpoint 来暴露相关信息,Nacos Discovery Starter 也支持这一点。

在使用之前需要在 maven 中添加 spring-boot-starter-actuator依赖,并在配置中允许 Endpoints 的访问。

  • Spring Boot 1.x 中添加配置 management.security.enabled=false
  • Spring Boot 2.x 中添加配置 management.endpoints.web.exposure.include=*

Spring Boot 1.x 可以通过访问 http://127.0.0.1:18083/nacos_discovery 来查看 Nacos Endpoint 的信息。

Spring Boot 2.x 可以通过访问 http://127.0.0.1:18083/actuator/nacos-discovery 来访问。

如上图所示,NacosDiscoveryProperties 则为 Spring Cloud Nacos Discovery 本身的配置,也包括本机注册的内容,subscribe 为本机已订阅的服务信息。

RocketMQ Example

项目说明

本项目演示如何使用 RocketMQ Binder 完成 Spring Cloud 应用消息的订阅和发布。

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

在说明 RocketMQ 的示例之前,我们先了解一下 Spring Cloud Stream。

这是官方对 Spring Cloud Stream 的一段介绍:

Spring Cloud Stream 是一个用于构建基于消息的微服务应用框架。它基于 SpringBoot 来创建具有生产级别的单机 Spring 应用,并且使用 Spring Integration 与 Broker 进行连接。

Spring Cloud Stream 提供了消息中间件配置的统一抽象,推出了 publish-subscribe、consumer groups、partition 这些统一的概念。

Spring Cloud Stream 内部有两个概念:Binder 和 Binding。

  • Binder: 跟外部消息中间件集成的组件,用来创建 Binding,各消息中间件都有自己的 Binder 实现。

比如 Kafka 的实现 KafkaMessageChannelBinder,RabbitMQ 的实现 RabbitMessageChannelBinder 以及 RocketMQ 的实现 RocketMQMessageChannelBinder。

  • Binding: 包括 Input Binding 和 Output Binding。

Binding 在消息中间件与应用程序提供的 Provider 和 Consumer 之间提供了一个桥梁,实现了开发者只需使用应用程序的 Provider 或 Consumer 生产或消费数据即可,屏蔽了开发者与底层消息中间件的接触。

下图是 Spring Cloud Stream 的架构设计。

微服务开发的一站式解决方案Spring Cloud Alibaba

示例

如何接入

在启动示例进行演示之前,我们先了解一下 Spring Cloud 应用如何接入 RocketMQ Binder。

注意:本章节只是为了便于您理解接入方式,本示例代码中已经完成****接入工作,您无需再进行修改。

  1. 首先,修改 pom.xml 文件,引入 RocketMQ Stream Starter。
<dependency>
 <groupId>com.alibaba.cloud</groupId>
 <artifactId>spring-cloud-starter-stream-rocketmq</artifactId>
</dependency>
  1. 配置 Input 和 Output 的 Binding 信息并配合 @EnableBinding 注解使其生效
@SpringBootApplication
@EnableBinding({ Source.class, Sink.class })
public class RocketMQApplication {
    public static void main(String[] args) {
        SpringApplication.run(RocketMQApplication.class, args);
    }
}

配置 Binding 信息:

# 配置rocketmq的nameserver地址
spring.cloud.stream.rocketmq.binder.name-server=127.0.0.1:9876
# 定义name为output的binding
spring.cloud.stream.bindings.output.destination=test-topic
spring.cloud.stream.bindings.output.content-type=application/json
# 定义name为input的binding
spring.cloud.stream.bindings.input.destination=test-topic
spring.cloud.stream.bindings.input.content-type=application/json
spring.cloud.stream.bindings.input.group=test-group
  1. 消息发送及消息订阅

下载并启动 RocketMQ

在接入 RocketMQ Binder 之前,首先需要启动 RocketMQ 的 Name Server 和 Broker。

  1. 下载RocketMQ最新的二进制文件,并解压
  2. 启动 Name Server
sh bin/mqnamesrv
  1. 启动 Broker
sh bin/mqbroker -n localhost:9876
  1. 创建 Topic: test-topic
sh bin/mqadmin updateTopic -n localhost:9876 -c DefaultCluster -t test-topic

应用启动

  1. 增加配置,在应用的 /src/main/resources/application.properties 中添加基本配置信息
spring.application.name=rocketmq-example
server.port=28081
  1. 启动应用,支持 IDE 直接启动和编译打包后启动。
  2. IDE 直接启动:找到主类 RocketMQApplication,执行 main 方法启动应用。
  3. 打包编译后启动:首先执行 mvn clean package 将工程编译打包,然后执行 java -jar rocketmq-example.jar 启动应用。

消息处理

使用 name 为 output 对应的 binding 发送消息到 test-topic 这个 topic。

使用2个 input binding 订阅数据。

  • input1: 订阅 topic 为 test-topic 的消息,顺序消费所有消息(顺序消费的前提是所有消息都在一个 MessageQueue 中)
  • input2: 订阅 topic 为 test-topic 的消息,异步消费 tags 为 tagStr 的消息,Consumer 端线程池个数为20

配置信息如下:

spring.cloud.stream.rocketmq.binder.name-server=127.0.0.1:9876
spring.cloud.stream.bindings.output.destination=test-topic
spring.cloud.stream.bindings.output.content-type=application/json
spring.cloud.stream.bindings.input1.destination=test-topic
spring.cloud.stream.bindings.input1.content-type=text/plain
spring.cloud.stream.bindings.input1.group=test-group1
spring.cloud.stream.rocketmq.bindings.input1.consumer.orderly=true
spring.cloud.stream.bindings.input2.destination=test-topic
spring.cloud.stream.bindings.input2.content-type=text/plain
spring.cloud.stream.bindings.input2.group=test-group2
spring.cloud.stream.rocketmq.bindings.input2.consumer.orderly=false
spring.cloud.stream.rocketmq.bindings.input2.consumer.tags=tagStr
spring.cloud.stream.bindings.input2.consumer.concurrency=20

消息发送

使用 MessageChannel 进行消息发送:

public class ProducerRunner implements CommandLineRunner {
 @Autowired
 private MessageChannel output; // 获取name为output的binding
 @Override
 public void run(String... args) throws Exception {
 Map<String, Object> headers = new HashMap<>();
 headers.put(MessageConst.PROPERTY_TAGS, "tagStr");
 Message message = MessageBuilder.createMessage(msg, new MessageHeaders(headers));
 output.send(message);
 }
}

或者使用 RocketMQ 原生的 API 进行消息发送:

public class RocketMQProducer {
 DefaultMQProducer producer = new DefaultMQProducer("producer_group");
 producer.setNamesrvAddr("127.0.0.1:9876");
 producer.start();
 
 Message msg = new Message("test-topic", "tagStr", "message from rocketmq producer".getBytes());
 producer.send(msg);
}

消息接收

使用 @StreamListener 注解接收消息:

@Service
public class ReceiveService {
    @StreamListener("input1")
    public void receiveInput1(String receiveMsg) {
        System.out.println("input1 receive: " + receiveMsg);
    }
    @StreamListener("input2")
    public void receiveInput2(String receiveMsg) {
        System.out.println("input2 receive: " + receiveMsg);
    }
}

Endpoint 信息查看

Spring Boot 应用支持通过 Endpoint 来暴露相关信息,RocketMQ Stream Starter 也支持这一点。

在使用之前需要在 Maven 中添加 spring-boot-starter-actuator依赖,并在配置中允许 Endpoints 的访问。

  • Spring Boot 1.x 中添加配置 management.security.enabled=false
  • Spring Boot 2.x 中添加配置 management.endpoints.web.exposure.include=*

Spring Boot 1.x 可以通过访问 http://127.0.0.1:18083/rocketmq_binder 来查看 RocketMQ Binder Endpoint 的信息。Spring Boot 2.x 可以通过访问 http://127.0.0.1:28081/actuator/rocketmq-binder 来访问。

这里会统计消息最后一次发送的数据,消息发送成功或失败的次数,消息消费成功或失败的次数等数据。

{
    "runtime": {
        "lastSend.timestamp": 1542786623915
    },
    "metrics": {
        "scs-rocketmq.consumer.test-topic.totalConsumed": {
            "count": 11
        },
        "scs-rocketmq.consumer.test-topic.totalConsumedFailures": {
            "count": 0
        },
        "scs-rocketmq.producer.test-topic.totalSentFailures": {
            "count": 0
        },
        "scs-rocketmq.consumer.test-topic.consumedPerSecond": {
            "count": 11,
            "fifteenMinuteRate": 0.012163847780107841,
            "fiveMinuteRate": 0.03614605351360527,
            "meanRate": 0.3493213353657594,
            "oneMinuteRate": 0.17099243039490175
        },
        "scs-rocketmq.producer.test-topic.totalSent": {
            "count": 5
        },
        "scs-rocketmq.producer.test-topic.sentPerSecond": {
            "count": 5,
            "fifteenMinuteRate": 0.005540151995103271,
            "fiveMinuteRate": 0.01652854617838251,
            "meanRate": 0.10697493212602836,
            "oneMinuteRate": 0.07995558537067671
        },
        "scs-rocketmq.producer.test-topic.sentFailuresPerSecond": {
            "count": 0,
            "fifteenMinuteRate": 0.0,
            "fiveMinuteRate": 0.0,
            "meanRate": 0.0,
            "oneMinuteRate": 0.0
        },
        "scs-rocketmq.consumer.test-topic.consumedFailuresPerSecond": {
            "count": 0,
            "fifteenMinuteRate": 0.0,
            "fiveMinuteRate": 0.0,
            "meanRate": 0.0,
            "oneMinuteRate": 0.0
        }
    }
}

注意:要想查看统计数据需要在pom里加上 metrics-core依赖。如若不加,endpoint 将会显示 warning 信息而不会显示统计信息:

{
 "warning": "please add metrics-core dependency, we use it for metrics"
}

More

RocketMQ 是一款功能强大的分布式消息系统,广泛应用于多个领域,包括异步通信解耦、企业解决方案、金融支付、电信、电子商务、快递物流、广告营销、社交、即时通信、移动应用、手游、视频、物联网、车联网等。

Seata Example

项目说明

本项目演示如何使用 Seata Starter 完成 Spring Cloud 应用的分布式事务接入。

Seata 是 阿里巴巴 开源的 分布式事务中间件,以 高效 并且对业务 0 侵入 的方式,解决 微服务 场景下面临的分布式事务问题。

准备工作

在运行此示例之前,你需要先完成如下几步准备工作:

  1. 配置数据库
  2. 创建 UNDO_LOG 表
  3. 创建 示例中 业务所需要的数据库表
  4. 启动 Seata Server

配置数据库

首先,你需要有一个支持 InnoDB 引擎的 MySQL 数据库。

注意: 实际上,Seata 支持不同的应用使用完全不相干的数据库,但是这里为了简单地演示一个原理,所以我们选择了只使用一个数据库。

将 account-server、order-service、storage-service 这三个应用中的 resources 目录下的 application.properties 文件中的如下配置修改成你运行环境中的实际配置。

mysql.server.ip=your mysql server ip address
mysql.server.port=your mysql server listening port
mysql.db.name=your database name for test
mysql.user.name=your mysql server username
mysql.user.password=your mysql server password

创建 undo_log 表

Seata AT 模式 需要使用到 undo_log 表。

-- 注意此处0.3.0+ 增加唯一索引 ux_undo_log
CREATE TABLE `undo_log` (
 `id` bigint(20) NOT NULL AUTO_INCREMENT,
 `branch_id` bigint(20) NOT NULL,
 `xid` varchar(100) NOT NULL,
 `context` varchar(128) NOT NULL,
 `rollback_info` longblob NOT NULL,
 `log_status` int(11) NOT NULL,
 `log_created` datetime NOT NULL,
 `log_modified` datetime NOT NULL,
 `ext` varchar(100) DEFAULT NULL,
 PRIMARY KEY (`id`),
 UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

创建 示例中 业务所需要的数据库表

DROP TABLE IF EXISTS `storage_tbl`;
CREATE TABLE `storage_tbl` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `commodity_code` varchar(255) DEFAULT NULL,
 `count` int(11) DEFAULT 0,
 PRIMARY KEY (`id`),
 UNIQUE KEY (`commodity_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `order_tbl`;
CREATE TABLE `order_tbl` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `user_id` varchar(255) DEFAULT NULL,
 `commodity_code` varchar(255) DEFAULT NULL,
 `count` int(11) DEFAULT 0,
 `money` int(11) DEFAULT 0,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `account_tbl`;
CREATE TABLE `account_tbl` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `user_id` varchar(255) DEFAULT NULL,
 `money` int(11) DEFAULT 0,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

启动 Seata Server

点击这个页面 https://github.com/seata/seata/releases,下载最新版本的 Seata Server 端.

进入解压之后的 bin 目录,执行如下命令来启动

sh seata-server.sh -p $LISTEN_PORT -m $MODE(file or db)

在这个示例中,采用如下命令来启动 Seata Server

sh seata-server.sh -p 8091 -m file

注意 如果你修改了endpoint且注册中心使用默认file类型,那么记得需要在各个示例工程中的 file.conf 文件中,修改 grouplist 的值(当registry.conf 中registry.type 或 config.type 为file 时会读取内部的file节点中的文件名,若type不为file将直接从配置类型的对应元数据的注册配置中心读取数据),推荐大家使用 nacos 作为配置注册中心。

运行示例

分别运行 account-server、order-service、storage-service 和 business-service 这三个应用的 Main 函数,启动示例。

启动示例后,通过 HTTP 的 GET 方法访问如下两个 URL,可以分别验证在 business-service 中 通过 RestTemplate 和 FeignClient 调用其他服务的场景。

http://127.0.0.1:18081/seata/feign
http://127.0.0.1:18081/seata/rest

如何验证分布式事务成功?

Xid 信息是否成功传递

在 account-server、order-service 和 storage-service 三个 服务的 Controller 中,第一个执行的逻辑都是输出 RootContext 中的 Xid 信息,如果看到都输出了正确的 Xid 信息,即每次都发生变化,且同一次调用中所有服务的 Xid 都一致。则表明 Seata 的 Xid 的传递和还原是正常的。

数据库中数据是否一致

在本示例中,我们模拟了一个用户购买货物的场景,StorageService 负责扣减库存数量,OrderService 负责保存订单,AccountService 负责扣减用户账户余额。

为了演示样例,我们在 OrderService 和 AccountService 中 使用 Random.nextBoolean() 的方式来随机抛出异常,模拟了在服务调用时随机发生异常的场景。

如果分布式事务生效的话, 那么以下等式应该成立

  • 用户原始金额(1000) = 用户现存的金额 + 货物单价 (2) * 订单数量 * 每单的货物数量(2)
  • 货物的初始数量(100) = 货物的现存数量 + 订单数量 * 每单的货物数量(2)

对 Spring Cloud 支持点

  • 通过 Spring MVC 提供服务的服务提供者,在收到 header 中含有 Seata 信息的 HTTP 请求时,可以自动还原 Seata 上下文。
  • 支持服务调用者通过 RestTemplate 调用时,自动传递 Seata 上下文。
  • 支持服务调用者通过 FeignClient 调用时,自动传递 Seata 上下文。
  • 支持 SeataClient 和 Hystrix 同时使用的场景。
  • 支持 SeataClient 和 Sentinel 同时使用的场景。
X-Eyes Admin
X-Eyes Admin

要发表评论,您必须先登录