朋友们,最近腾讯的微服务框架
SpringCloudTencent
开源了,咱们今天一起来尝尝鲜。看看它的表现,和易用程度是怎样的。
Spring Cloud Tencent 简介
Spring Cloud Tencent 是腾讯开源的一站式微服务解决方案。
Spring Cloud Tencent 实现了Spring Cloud 标准微服务 SPI,开发者可以基于 Spring Cloud Tencent 快速开发 Spring Cloud 云原生分布式应用。
Spring Cloud Tencent 的核心依托腾讯开源的一站式服务发现与治理平台 Polaris,实现各种分布式微服务场景。
Spring Cloud Tencent提供的能力包括但不限于:
上面的介绍来自于官方,下面的我们动手来使用一下。
通过查阅文档,我们发现Spring Cloud Tencent的注册和配置中心使用的是腾讯自己开源的一站式服务发现与治理平台 Polaris,所以我们首先将Polaris部署好。
环境准备
准备一台云服务器,配置如下:
核心 | 内存 | 带宽 |
---|---|---|
2 | 4g | 1M |
部署Polaris
Polaris简介
Polaris翻译过来就是北极星
,北极星是腾讯开源的服务发现和治理中心,致力于解决分布式或者微服务架构中的服务可见、故障容错、流量控制和安全问题。虽然,业界已经有些组件可以解决其中一部分问题,但是缺少一个标准的、多语言的、框架无关的实现。
北极星解决了哪些问题?
问题类型 | 问题示例 | 解决方案 |
---|---|---|
服务可见 | 主调方如何知道被调方的服务地址 | 注册发现 |
配置可见 | 如何实现服务配置的版本管理、动态下发、按需变更。 | 配置管理 |
故障容错 | 当被调方的部分实例异常时,如何屏蔽异常实例,屏蔽之后如何恢复 | 熔断降级 |
当某些主调方的请求量过多时,如何限制这些主调方的请求,避免影响其他主调方的请求 | 访问限流 | |
流量控制 | 被调方包含多个实例,主调方如何确定请求发送到哪个实例,如何保证请求均衡 | 负载均衡 |
如何实现按地域就近、单元化隔离、金丝雀发布等各种请求调度策略 | 动态路由 |
如上所示,我们发现北极星将熔断降级
、访问限流
这一类故障容错,流量控制功能集成了进来,对比于SpringCloud原生的Eureka,甚至阿里的Nacos还是要方便了很多,我们使用springCloudAlibaba时,要想实现熔断限流等,还需要部署它的sentinel。但是具体是功能差分出来好还是集中化好,这是一个见仁见智的过程。
部署Polaris
关于细节问题我们后续使用中逐步去发现,先来搭建自己的北极星。我们只需要搭建一个单机版即可。
北极星单机版包含以下4个组件:
- polaris-console:可视化控制台,提供服务治理管控页面
- polaris-server:控制面,提供数据面组件及控制台所需的后台接口
- prometheus:服务治理监控所需的指标汇聚统计组件
- pushgateway:prometheus推送网关,支持数据面通过推送方式上报监控数据到prometheus
北极星单机版默认占用以下端口:
- polaris-console:8080(http/tcp)
- polaris-server:8090(http/tcp)、8091(grpc/tcp)
- prometheus:9090(tcp)
- pushgateway:9091(tcp)
注意:这里官方少给了一个端口,配置中心是8093。很坑人
下载并安装
从北极星releases下载Linux单机版软件包。找到我们需要的进行下载:
这里插个题外话,如果你发下资源下载很慢,可以安装如下的浏览器插件,但是前提是要安装Tampermonkey(油猴脚本管理器),之后在点击下面的插件地址进行安装:
刷新你的github,再次到下载地址releases你会发现变成如下的样子:
我们选择韩国节点,就可以快速下载了。
将下载好的压缩包上传到服务器,并解压:
unzip polaris-standalone-release_v1.9.0.linux.amd64
进入文件夹,并执行脚本启动:
cd polaris-standalone-release_v1.9.0.linux.amd64
bash install.sh
访问8080端口:
默认账号密码是:polaris/polaris
到此为止,北极星就部署上了,使用细节咱们配合自己的服务再验证。
服务注册
下面我们将要创建自己的springCloudTencent应用了,这里使用的语言的java,所及首先咱们创建一个springboot项目,这里给出一个小经验,我们在有时候创建springboot项目发现公司的网络无法加载默认的spring地址,可以替换成阿里云的:start.aliyun.com
添加依赖
创建好后添加依赖, 依赖版本管理,一定要对应好自己的springboot版本,我这里直接将我的pom粘贴出来:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wjbgn</groupId>
<artifactId>spring-cloud-tencent-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cloud-tencent-test</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.2.RELEASE</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-polaris-discovery</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-dependencies</artifactId>
<version>1.5.2-Hoxton.SR9</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.7.RELEASE</version>
<configuration>
<mainClass>com.wjbgn.tencent.test.SpringCloudTencentTestApplication</mainClass>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
这里我必须吐槽,腾讯官方提供了好几种版本搭配,我都使用了一个遍,不是各种少依赖就是冲突,最终确定了这个版本没有问题。
添加配置文件
spring:
application:
# 应用名称
name: spring-cloud-tencent-test
cloud:
polaris:
# 北极星的服务地址
address: grpc://xxx.xxx.xxx.xxx:8091
# 命名空间使用默认
namespace: default
启动应用
启动测试发现连接到北极星超时了:
org.springframework.context.ApplicationContextException: Failed to start bean 'webServerStartStop'; nested exception is com.tencent.polaris.api.exception.PolarisException: ERR-1004(API_TIMEOUT), instance register request timeout.
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181) ~[spring-context-5.3.13.jar:5.3.13]
at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54) ~[spring-context-5.3.13.jar:5.3.13]
at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356) ~[spring-context-5.3.13.jar:5.3.13]
at java.lang.Iterable.forEach(Iterable.java:75) ~[na:1.8.0_181]
at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:155) ~[spring-context-5.3.13.jar:5.3.13]
at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:123) ~[spring-context-5.3.13.jar:5.3.13]
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:935) ~[spring-context-5.3.13.jar:5.3.13]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586) ~[spring-context-5.3.13.jar:5.3.13]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:144) ~[spring-boot-2.4.13.jar:2.4.13]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:771) [spring-boot-2.4.13.jar:2.4.13]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:763) [spring-boot-2.4.13.jar:2.4.13]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:438) [spring-boot-2.4.13.jar:2.4.13]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:339) [spring-boot-2.4.13.jar:2.4.13]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1329) [spring-boot-2.4.13.jar:2.4.13]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1318) [spring-boot-2.4.13.jar:2.4.13]
at com.wjbgn.tencent.test.SpringCloudTencentTestApplication.main(SpringCloudTencentTestApplication.java:10) [classes/:na]
Caused by: com.tencent.polaris.api.exception.PolarisException: ERR-1004(API_TIMEOUT), instance register request timeout.
at com.tencent.polaris.discovery.client.api.DefaultProviderAPI.register(DefaultProviderAPI.java:87) ~[polaris-discovery-client-1.6.1.jar:na]
at com.tencent.cloud.polaris.registry.PolarisServiceRegistry.register(PolarisServiceRegistry.java:108) ~[spring-cloud-starter-tencent-polaris-discovery-1.5.2-2020.0.5.jar:1.5.2-2020.0.5]
at org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration.register(AbstractAutoServiceRegistration.java:232) ~[spring-cloud-commons-3.0.5.jar:3.0.5]
at com.tencent.cloud.polaris.registry.PolarisAutoServiceRegistration.register(PolarisAutoServiceRegistration.java:68) ~[spring-cloud-starter-tencent-polaris-discovery-1.5.2-2020.0.5.jar:1.5.2-2020.0.5]
at org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration.start(AbstractAutoServiceRegistration.java:133) ~[spring-cloud-commons-3.0.5.jar:3.0.5]
at org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration.bind(AbstractAutoServiceRegistration.java:98) ~[spring-cloud-commons-3.0.5.jar:3.0.5]
at org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration.onApplicationEvent(AbstractAutoServiceRegistration.java:86) ~[spring-cloud-commons-3.0.5.jar:3.0.5]
at org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration.onApplicationEvent(AbstractAutoServiceRegistration.java:47) ~[spring-cloud-commons-3.0.5.jar:3.0.5]
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176) ~[spring-context-5.3.13.jar:5.3.13]
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169) ~[spring-context-5.3.13.jar:5.3.13]
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143) ~[spring-context-5.3.13.jar:5.3.13]
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:421) ~[spring-context-5.3.13.jar:5.3.13]
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:378) ~[spring-context-5.3.13.jar:5.3.13]
at org.springframework.boot.web.servlet.context.WebServerStartStopLifecycle.start(WebServerStartStopLifecycle.java:46) ~[spring-boot-2.4.13.jar:2.4.13]
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:178) ~[spring-context-5.3.13.jar:5.3.13]
... 15 common frames omitted
此处是因为我的云服务器端口没有开放,前面咱们说过,北极星默认占用的端口有5个,请返回前面查看,需要开放访问权限。修改后启动成功。
此时我们的服务成功注册了已经:
常用特性
元数据定义
服务实例通常带有一系列的标签信息,例如实例所属的机房信息、地域信息、环境信息等,这些信息统称为服务实例的元信息。实例注册到注册中心时,会带上自身的元数据信息。当消费方从注册中心获取到实例时,既可以同时获取到每个实例的元信息。
我们可以使用如下方式定义元数据:
-
配置文件
spring: cloud: tencent: metadata: content: idc: shanghai env: dev1
-
-D 启动参数
Java -jar -Dspring.cloud.tencent.metadata.content.env=dev2 demo.jar
-
环境变量
Spring Cloud Tencent 约定了前缀
SCT_METADATA_CONTENT_
的环境变量为应用的元数据信息。SCT_METADATA_CONTENT_IDC=shanghai SCT_METADATA_CONTENT_ENV=dev1
实例权重及上下线
与nacos一样,提供权重配置及服务实例上下线的功能。
配置中心
配置中心模块是 Spring Cloud Tencent 最核心的模块之一,实现了 Spring Cloud PropertySourceLoader SPI 接口。
参照官方文档描述,似乎没什么先进的地方,通过@Value
、@ConfigurationProperties
获取配置的内容。
注意: 由于 Spring Cloud PropertySourceLoader SPI 是在 Bootstrap 阶段调用,所以 Polaris Config 相关的配置内容(例如Polaris 服务地址)需要放在 bootstrap.yml 文件里,而不能放在 application.yml 文件里,否则会初始化失败。
上面这个注意让我觉得不是很友好?或者说这么做是有什么好处?
添加依赖
在前面我们使用服务注册的基础上,添加配置中心的依赖:
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-polaris-config</artifactId>
</dependency>
添加配置文件
必须要放在bootstrap.yml当中。
spring:
application:
name: spring-cloud-tencent-test
cloud:
polaris:
address: grpc://xxx.xxx.xxx.xxx:8091
namespace: default
config:
#address: grpc://xxx.xxx.xxx.xxx:8093 # 选填,只有在配置中心和注册中心是两个不同的地址时才需要配置
auto-refresh: true # 选填,当配置发布后,动态刷新 Spring 上下文,默认值为 true面
所以不妨我们将注册中心和配置中心的配置都放在bootstrap.yml中好了。
注入配置文件
与nacos相同,我们可以在北极星的界面去创建配置文件,推荐创建以应用名spring.application.name
为名称的分组:
Spring Cloud Tencent Config 会自动注入当前应用名分组下的
application-${activeProfile}.properties
application-${activeProfile}.yml
application.properties
application.yml
bootstrap-${activeProfile}.properties
bootstrap-${activeProfile}.yml
bootstrap.properties
bootstrap.yml
优先级从上到下依次降低
注意:是 yml 后缀,而不是 yaml
如果需要引入其他分组下的配置文件,可以增加下面的配置:
spring:
cloud:
polaris:
config:
groups:
- name: ${spring.application.name} # 选填,注入自定义配置的配置分组
files: [ "config/application.properties", "config/bootstrap.yml" ] # 注入自定义配置文件列表,当 key 冲突时,排在前面的配置文件优先级高于后面
经过前面的介绍后,下面我们来创建一个配置文件,点击刚才创建的分组进去,点击新增:
我们创建一个application.yml
文件,支持使用'/'创建文件夹。同时可以对配置文件执行key:value形式的标签。
我们在配置文件增加内容,并保存,发布:
启动项目
提供一个测试接口:
package com.wjbgn.tencent.test.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @description: 测试controller
* @author:weirx
* @date:2022/6/22 14:58
* @version:3.0
*/
@RestController
@RequestMapping("/test")
public class TestController {
private static final String text = "SpringCloudTencent,";
@Value("${test.springcloudtencent}")
private String str;
@GetMapping("")
public String test() {
return text + str;
}
}
项目成功启动,但是我发现我增加的配置,无论如何页获取不到,然而在本地配置增加是好使的。于是我尝试使用上面提到的指定分组的形式:
spring:
application:
name: spring-cloud-tencent-test
cloud:
polaris:
address: grpc://122.112.181.245:8091
namespace: default
config:
groups:
- name: ${spring.application.name} # 选填,注入自定义配置的配置分组
files: [ "application.yml" ]
#address: grpc://xxx.xxx.xxx.xxx:8093 # 选填,只有在配置中心和注册中心是两个不同的地址时才需要配置
auto-refresh: true # 选填,当配置发布后,动态刷新 Spring 上下文,默认值为 true面
此时报错了:
Caused by: io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection timed out: no further information: /xxx.xxx.xxx.xxx:8093
我发现配置中心的端口居然是8093
么?你官方不是说相同不用配?你前面说占用端口的时候也没有8093
啊?,巨坑!!!
当我将云服务的8093端口放开,直接可以获取到了:
我将配置改为原来的,不使用分组的形式,仍然不行,我真是醉了,后悔体验这玩应了,nacos不香吗?哥们服了,谁知道怎么回事教我一下!!!我先用分组的方式玩着!!
特性
动态刷新
需要在配置文件开启配置:
spring.cloud.polaris.config.auto-refresh=true
同时在使用配置的类上增加@RefreshScope
注解。但是这个在nacos也有啊。
修改后尝试访问,动态修改成功了:
监听回调
这个功能比较有意思,可通过注解@PolarisConfigKVFileChangeListener
实现针对指定的配置属性名称或者属性前缀进行监听,配置发生变更时会触发方法回调。
这里又是一个坑,我用的版本啊,根本没有这个注解@PolarisConfigKVFileChangeListener
!!!
这个先不管了,回头我换个版本的时候再来尝试。
限流
添加依赖
添加依赖,在我们前面pom文件基础上,添加限流的依赖:
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-polaris-ratelimit</artifactId>
</dependency>
添加配置文件
只需要保持跟服务注册时的配置就好,在bootstrap.yml中:
spring:
application:
name: spring-cloud-tencent-test
cloud:
polaris:
address: grpc://122.112.181.245:8091
namespace: default
添加规则
在北极星的控制台,添加限流规则:
支持接口的精确
和正则
匹配,同时支持单机
和分布式
限流。具体细节可以查看官方的文档说明。总体来说限流规则足够细致。
如果超出限流的阈值则会返回如下信息:
分布式限流感觉较复杂,这里不介绍了。
熔断
故障实例熔断是常见的一种容错保护机制。故障实例熔断能实现主调方迅速自动屏蔽错误率高或故障的服务实例,并启动定时任务对熔断实例进行探活。在达到恢复条件后对其进行半开恢复。在半开恢复后,释放少量请求去进行真实业务探测。并根据真实业务探测结果去判断是否完全恢复正常。
添加依赖
在服务注册配置基础上,给服务调用方增加以下依赖:
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-polaris-circuitbreaker</artifactId>
</dependency>
添加配置文件
使用如下配置文件:
spring:
application:
name: ${application.name}
cloud:
polaris:
address: grpc://${修改为第一步部署的 Polaris 服务地址}:8091
namespace: default
loadbalancer:
configurations: polaris # 2020.0.x SDK需要添加这个配置
规则配置
如下所示:
也支持json方式的配置:
springcloudTencent和springCloud一样,都是使用Feign和RestTemplate进行服务间调用的。
关于熔断的验证我这里不做演示了。
标签传递
微服务调用中,常常需要在链路上传递一些元数据信息,典型的场景例如:tracer 上下文信息、服务路由的标签信息等。Spring Cloud
默认的服务调用组件无法做到传递元数据。 Spring Cloud Tencent Metadata Transfer
扩展了 Feign 的能力,能够简单的集成链路元数据传递能力。
这个功能还是比较有意思的,下面做下简单介绍。
在前面介绍的时候,就提到过,支持在配置文件中配置元数据,而标签传递就是再次基础上做的。
springCloudTencent提供对元数据打标签
的能力,分为两种形式:静态和动态。
静态打标
通过spring.cloud.tencent.metadata.transitive属性,指定需要传递的元数据,否则不会传递,如下所示:
spring:
cloud:
tencent:
metadata:
content:
a: 1
b: 1
transitive:
- a
a将会传递给下游服务,b则不会。
动态打标
除了通过配置文件的方式静态打标以外,也可以通过拦截器动态打标。实现方式为在拦截器中往请求里写入 Key 为 SCT-CUSTOM-METADATA
的 Header。Spring-Cloud-Tencent-Metadata
内置的拦截器会合并动态打标和静态打标的元数据,并通过请求 Header 传递到下游。
引入依赖:
<!-- 引入依赖 -->
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-metadata-transfer</artifactId>
</dependency>
此处只做介绍,需要使用的参考官方文档吧。
总结
通过本次简单的使用和学习,其实对于开发人员来说,使用springCloudTencent和SpringbootAlibaba在功能上不会有太大的差别。只不过是腾讯将熔断的限流的功能集成在注册、配置中心当中。而阿里需要单独部署sentinel,实际功能大同小异。
使用过程中还是出现了不少问题,包括为什么我的配置文件获取不到?必须要使用指定组和名称的方式。还有版本之间的依赖也比较混乱,导致有些版本不能参照文档立即运行,还要解决依赖问题。
个人还是推荐使用SpringCloudAlibaba这一套,毕竟有了很大的用户基数和开源社区的支持,遇到问题在各大博客网站很容易找到解决方案。
不是说SpringCloudTencent不好,很多功能的设计和实现还是让人耳目一新的,但是一些细节以及社区使用量还是有待提升。
当然了,我用到的只是最常用的冰山一角,还有关于k8s,以及其他语言如go,php等的支持是我没有尝试的,欢迎大家去尝试,相信不是说SpringCloudTencent不好日后会越来越好的。