SOFAStack学习笔记
SOFAStack(Scalable Open Financial Architecture Stack,可扩展开放金融架构栈)是蚂蚁金服开源的技术栈,国内多家金融和互联网公司在生产环境使用了此技术栈。
基于Spring Boot,额外提供了以下特性:
- 健康检查(Readiness探针):在Spring Boot Liveness探针的基础上增加Readiness探针,仅当此探针成功后实例才能接受流量
- 类(加载器)隔离,基于SOFAArk,实现业务代码的类、SOFA中间件相关的类的隔离,避免了类冲突
- 日志空间隔离:将SOFA中间件日志和业务代码产生的日志分开
- 和其他SOFA中间件便捷的集成:提供各种SOFA中间件的Starter
- 模块化:可以为同一JVM中运行的不同SOFABoot模块提供独立的Spring ApplicationContext,可以规避BeanId的冲突
SOFABoot需要JDK 7+和Maven 3.2.5来完成构建。
通过IntelliJ IDEA的Spring Initializer来创建项目骨架,依赖选择Web。然后修改POM,将parent改为:
1 2 3 4 5 |
<parent> <groupId>com.alipay.sofa</groupId> <artifactId>sofaboot-dependencies</artifactId> <version>${sofa.boot.version}</version> </parent> |
并添加依赖:
1 2 3 4 5 6 7 8 9 10 |
<!-- 提供健康检查能力 --> <dependency> <groupId>com.alipay.sofa</groupId> <artifactId>healthcheck-sofa-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> |
SOFABoot要求提供必要的参数:
1 2 |
spring.application.name=Learing SOFABoot logging.path=./logs |
启动上述项目后,运行命令 curl http://localhost:8080/actuator/health,可以看到服务的健康状况,正常情况下输出 {"status":"UP"}
运行命令 curl -s http://localhost:8080/actuator/readiness | jq .可以获得Readiness探针执行结果的细节:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
{ "details": { "diskSpace": { "details": { "threshold": 10485760, "free": 166560387072, "total": 358796750848 }, "status": "UP" }, "SOFABootReadinessHealthCheckInfo": { "status": "UP" } }, "status": "UP" } |
检查logs目录,可以看到日志根据来源的不同,分割到logs、infra等目录中。
你可以使用SpringRunner进行单元测试,但是如果在项目中使用了SOFABoot的类隔离特性,则必须使用SofaBootRunner、SofaJUnit4Runner进行单元测试,并引入依赖:
1 2 3 4 |
<dependency> <groupId>com.alipay.sofa</groupId> <artifactId>test-sofa-boot-starter</artifactId> </dependency> |
单元测试用例的例子:
1 2 3 4 5 6 7 8 9 10 11 12 |
@SpringBootTest @RunWith(SpringRunner.class) public class SofaBootWithModulesTest { // 引用SOFABoot模块发布的服务 @SofaReference private SampleJvmService sampleJvmService; @Test public void test() { Assert.assertEquals("Hello, jvm service xml implementation.", sampleJvmService.message()); } } |
从2.4版本开始SOFABoot引入了基于Spring上下文隔离的模块化能力。SOFABoot定义了三个模块化隔离级别:
- 代码组织上的模块化:开发阶段分多个工程开发,例如使用Dubbo的团队,通常都会将API拆分为独立的Maven模块。这种隔离对运行时没有任何影响
- 基于Spring上下文隔离的模块化“类似于1,代码和配置分散在不同工程中。但是在运行时会启动多个Spring上下文(它们有共同的父上下文),DI仅仅在子上下文内发生。可以规避BeanId冲突问题
- 基于类加载器隔离的模块化:每个模块都使用独立的类加载器,可以规避模块依赖了冲突的类版本的问题
SOFABoot模块化开发属于第二种模块化形式 —— 基于 Spring 上下文隔离的模块化,每个模块使用独立的Spring上下文。
每个SOFABoot模块包含Java代码、Spring配置文集、SOFA模块标识等信息,打包形式为JAR。不同模块之间不能通过DI来引用,需要转而使用SOFA服务。SOFABoot支持两种形式的服务(发布/引用):
- JVM服务发布和引用:解决同一SOFABoot 应用内各 SOFABoot 模块之间的调用问题
- RPC服务发布和引用:解决多个 SOFABoot 应用之间的远程调用问题
由于相互之间没有Bean依赖,SOFABoot模块可以并行的启动,这可以提升应用启动速度。
这种模块化开发,和微服务的理念是违背的,各模块仍然需要共享单个JVM的资源。
为了体验SOFABoot的模块化开发,我们直接使用其源码附带的示例应用:
1 2 3 4 5 6 7 |
git clone https://github.com/alipay/sofa-boot.git tree sofa-boot/sofaboot-samples/sofaboot-sample-with-isle -L 1 # . # ├── service-consumer 服务的消费者 # ├── service-facade 服务的API # ├── service-provider 服务的提供者 # ├── sofa-boot-run 启动包含模块的SOFABoot应用 |
service-facade中定义的接口如下:
1 2 3 |
public interface SampleJvmService { String message(); } |
service-provider将上面的接口发布为JVM服务。发布方式有三种:
- 注解方式:
12345678910// 唯一性的ID,不设置默认为空串@SofaService(uniqueId = "annotationImpl")public class SampleJvmServiceAnnotationImpl implements SampleJvmService {@Overridepublic String message() {String message = "Hello, jvm service annotation implementation.";System.out.println(message);return message;}} -
XML方式:
123456789public class SampleJvmServiceImpl implements SampleJvmService {private String message;@Overridepublic String message() {System.out.println(message);return message;}}需要配合XML配置:
1234567<bean id="sampleJvmService" class="com.alipay.sofa.isle.sample.SampleJvmServiceImpl"><property name="message" value="Hello, jvm service xml implementation."/></bean><sofa:service ref="sampleJvmService" interface="com.alipay.sofa.isle.sample.SampleJvmService"><sofa:binding.jvm/></sofa:service> - 编程式:
1234567891011121314151617181920@Componentpublic class PublishServiceWithClient implements ClientFactoryAware {private ClientFactory clientFactory;@PostConstructpublic void init() {ServiceClient serviceClient = clientFactory.getClient(ServiceClient.class);ServiceParam serviceParam = new ServiceParam();serviceParam.setInstance(new SampleJvmServiceImpl( "Hello, jvm service service client implementation."));serviceParam.setInterfaceType(SampleJvmService.class);serviceParam.setUniqueId("serviceClientImpl");// 发布服务serviceClient.service(serviceParam);}@Overridepublic void setClientFactory(ClientFactory clientFactory) {this.clientFactory = clientFactory;}}
你需要在sofa-module.properties中声明模块名称、模块依赖:
1 2 3 4 5 6 |
# service-provider Module-Name=com.alipay.sofa.service-provider # service-consumer Module-Name=com.alipay.sofa.service-consumer Require-Module=com.alipay.sofa.service-provider |
service-consumer负责消费service-provider发布的服务:
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 |
public class JvmServiceConsumer implements ClientFactoryAware { private ClientFactory clientFactory; // 引用基于XML配置的JVM服务 @Autowired private SampleJvmService sampleJvmService; // 引用基于注解配置的JVM服务,使用uniqueId @SofaReference(uniqueId = "annotationImpl") private SampleJvmService sampleJvmServiceByFieldAnnotation; public void init() { sampleJvmService.message(); sampleJvmServiceByFieldAnnotation.message(); // 编程式客户端 ReferenceClient referenceClient = clientFactory.getClient(ReferenceClient.class); ReferenceParam referenceParam = new ReferenceParam<>(); referenceParam.setInterfaceType(SampleJvmService.class); referenceParam.setUniqueId("serviceClientImpl"); SampleJvmService sampleJvmServiceClientImpl = referenceClient.reference(referenceParam); sampleJvmServiceClientImpl.message(); } @Override public void setClientFactory(ClientFactory clientFactory) { this.clientFactory = clientFactory; } } |
如果需要在SOFABoot项目中使用SOFA中间件,需要依赖相应的Starter:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<!-- SOFARPC --> <dependency> <groupId>com.alipay.sofa</groupId> <artifactId>rpc-sofa-boot-starter</artifactId> </dependency> <!-- SOFATracer --> <dependency> <groupId>com.alipay.sofa</groupId> <artifactId>tracer-sofa-boot-starter</artifactId> </dependency> <!-- SOFALookout --> <dependency> <groupId>com.alipay.sofa</groupId> <artifactId>lookout-sofa-boot-starter</artifactId> </dependency> |
如果需要在SOFABoot项目中使用扩展组件,需要依赖相应的Starter:
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 |
<!-- 健康检查扩展 --> <dependency> <groupId>com.alipay.sofa</groupId> <artifactId>healthcheck-sofa-boot-starter</artifactId> </dependency> <!-- 模块化隔离扩展 --> <dependency> <groupId>com.alipay.sofa</groupId> <artifactId>isle-sofa-boot-starter</artifactId> </dependency> <!-- 类隔离扩展 --> <dependency> <groupId>com.alipay.sofa</groupId> <artifactId>sofa-ark-springboot-starter</artifactId> </dependency> <!-- 测试扩展 --> <dependency> <groupId>com.alipay.sofa</groupId> <artifactId>test-sofa-boot-starter</artifactId> </dependency> |
如果要使用SOFAArk提供的类加载器隔离功能,则需要依赖相应的ARK插件,并替换到对应的Starter:
ARK插件 | ArtifactId |
SOFARPC | rpc-sofa-boot-plugin |
SOFATracer | tracer-sofa-boot-plugin |
ARK插件能够让业务应用的类、SOFA中间件的类(以及它依赖的类)使用不同的类加载器,从而避免类冲突问题。
Spring Boot Actuator提供的HealthIndicator接口可以提供Liveness健康检查,SOFABoot在此基础上增加了Readiness检查功能。使用SOFA中间件时,最好配合SOFABoot的健康检查,以实现优雅上线,避免未准备好的实例过早加入服务池。
健康检查扩展提供了多个扩展点,允许用户定制其行为。
健康检查相关配置属性:
配置属性 | 说明 | 默认值 |
com.alipay.sofa.healthcheck.skip.all | 是否跳过整个 Readiness Check 阶段 | false |
com.alipay.sofa.healthcheck.skip.component | 是否跳过 SOFA 中间件的 Readiness Check | false |
com.alipay.sofa.healthcheck.skip.indicator | 是否跳过 HealthIndicator 的 Readiness Check | false |
com.alipay.sofa.healthcheck.component.check.retry.count | 组件健康检查重试次数 | 20 |
com.alipay.sofa.healthcheck.component.check.retry.interval | 组件健康检查重试间隔时间 | 1000ms |
com.alipay.sofa.healthcheck.module.check.retry.count | sofaboot 模块健康检查重试次数 | 0 |
com.alipay.sofa.healthcheck.module.check.retry.interval | sofaboot 模块健康检查重试间隔时间 | 1000ms |
SOFABoot支持异步的执行Bean的初始化方法,要使用该特性,引入依赖:
1 2 3 4 |
<dependency> <groupId>com.alipay.sofa</groupId> <artifactId>runtime-sofa-boot-starter</artifactId> </dependency> |
并且为目标Bean提供配置属性async-init:
1 |
<bean id="testBean" class="com.alipay.sofa.runtime.beans.TimeWasteBean" init-method="init" async-init="true"/> |
SOFABoot在独立的线程池中进行Bean的异步初始化,相关配置属性:
配置属性 | 说明 |
com.alipay.sofa.boot.asyncInitBeanCoreSize | 线程池默认线程数 |
com.alipay.sofa.boot.asyncInitBeanMaxSize | 线程池最大线程数 |
SOFABoot模块的JAR包遵循如下约定:
- 包含文件sofa-module.properties,定义模块名称、模块之间的依赖关系等元数据
- META-INF/spring目录下的任意Spring配置文件都会作为本模块的配置而加载
模块元数据声明在sofa-module.properties文件中,包含以下配置属性:
配置属性 | 说明 | ||
Module-Name | SOFABoot模块名称,唯一标识,Java包路径形式 | ||
Spring-Parent | 指定一个模块的名称,将其Spring上下文设置为当前模块的父上下文,这样就可以解除对目标模块的隔离 | ||
Require-Module | 逗号分隔的,依赖的其它模块列表 | ||
Module-Profile |
所属的Profile,通过Spring配置属性 com.alipay.sofa.boot.active-profile,可以指定哪些SOFABoot Profile启用(逗号分隔多个值) 只有其Profile启用的模块才激活(也就是启动) 此外,在Spring配置文件中,可以嵌套beans元素,子元素可以指定profile属性,来指定仅当特定Profile启用时才激活的Bean:
|
参考SOFARPC - 起步 - 整合SOFABoot一节。
SOFAArk 是一款基于 Java 实现的轻量级类隔离容器,提供类隔离和应用(模块)合并部署能力。
在大型软件开发过程中,通常会推荐底层功能插件化,业务功能模块化的开发模式,以期达到低耦合、高内聚、功能复用的优点。SOFAArk 提供了一套插件化、模块化的开发规范,它的能力包括:
- 定义类加载模型,运行时底层插件、业务应用(模块)的类相互隔离,每个插件和应用(模块)由不同的 ClassLoader 加载,可以有效避免相互之间的包冲突
- 定义了插件开发规范,可以基于Maven将多个第三方JAR打包为Ark Plugin(插件)
- 定义模块开发规范,可以基于Maven将应用打包为Ark Biz(模块)
- 提供针对Plugin、Biz的标准API,提供事件、扩展点支持
- 多Biz合并部署,打包为扁平的可执行JAR
- 支持在运行时通过API或配置中心来动态安装/写在Biz
Ark 包是满足特定目录格式要求的可执行扁平(Flat,也就是打包了所有依赖)Jar,使用Maven 插件 sofa-ark-maven-plugin可以将单个或多个应用打包成标准格式的 Ark 包,Ark包包含三类构件:
- Ark Container:负责 Ark 包启动、运行时的管理,Ark Plugin 和 Ark Biz 运行在 SOFAArk 容器之上。Ark Container具备管理插件和应用的能力,容器启动成功后,会自动解析类路径包含的 Ark Plugin 和 Ark Biz 依赖,完成隔离加载并按优先级依次启动它们
- Ark Plugin,使用Maven插件 sofa-ark-maven-plugin可以将单个或多个普通Jar包打包为Ark Plugin。Ark Plugin有一个配置文件,其中包含:插件类导入导出配置、资源导入导出配置、插件启动优先级等信息。SOFAArk使用独立的PluginClassLoader来加载插件
- Ark Biz,使用Maven插件 sofa-ark-maven-plugin可以将业务应用打包为Biz包。每个Ark包可以包含多个Biz,他们按优先级依次启动,通过JVM服务交互
执行Ark包时,Ark Container优先启动,然后启动Plugin,最后启动Biz。它们的逻辑关系图如下:
在解决类冲突(两个运行在单个JVM中的模块依赖某个类的两个不兼容版本)方面,SOFAArk比OSGI简单的多。SOFAArk 提出了一种特殊的包结构Ark Plugin,在存在包冲突时,用户可以使用 Maven 插件将若干冲突包打包成 Plugin,运行时由独立的 PluginClassLoader 加载,从而解决包冲突。
将复杂项目拆分到多个工程的主要动机包括避免VCS提交冲突、规避技术栈导致的依赖冲突。SOFA支持模块化,可以将多个业务模块打包为Biz,在运行时合并部署到单个JVM,各模块基于同一的API进行交互。
这种模式下,Biz之间的依赖可以通过Maven管理,当应用打为扁平化JAR时其依赖的Biz可以合并进来。每个Biz使用独立的BizClassLoader加载,Biz之间通过JVM服务(SofaService/SofaRefernece)进行交互。
这种模式下,Biz可以在运行时,通过API或者配置中心(ZooKeeper)来动态部署/卸载。
这里需要提到主应用(Master Biz)的概念,其实不管静态/动态合并,此概念都是存在的。如果Ark包打了单个Biz则它就是主应用,如果打了多个Biz包则需要配置指定主应用。主应用不得卸载。
通常情况下,各模块的实现放在动态Biz中,供主应用调用。主应用通过两种方式部署/卸载动态Biz:
- 调用SOFAArk的API
- 使用SOFAArk的Config插件,对接ZooKeeper配置中心。此插件会解析配置并控制动态Biz的部署/卸载
Ark Plugin可以导入、导出类或资源:
-
导入类:插件启动时,优先委托给导出该类的插件负责加载,如果加载不到,才会尝试从本插件内部加载
-
导出类:其他插件如果导入了该类,优先从本插件加载
-
导入资源:插件在查找资源时,优先委托给导出该资源的插件负责加载,如果加载不到,才会尝试从本插件内部加载
-
导出资源:其他插件如果导入了该资源,优先从本插件加载
SOFAArk的代码库提供了一个样例项目。其结构如下:
1 2 3 4 |
├── sample-ark-plugin │ ├── common # 此模块包含了插件导出类 │ ├── plugin # 包含插件服务的实现、PluginActivator接口实现 │ ├── pom.xml |
plugin项目的POM中包含如下配置:
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 |
<build> <plugins> <plugin> <groupId>com.alipay.sofa</groupId> <artifactId>sofa-ark-plugin-maven-plugin</artifactId> <version>${project.version}</version> <executions> <execution> <id>default-cli</id> <goals> <goal>ark-plugin</goal> </goals> <configuration> <!-- Ark 容器启动插件的入口类,最多只能配置一个。一般在此执行初始化操作,比如发布插件服务 --> <activator>com.alipay.sofa.ark.sample.activator.SamplePluginActivator</activator> <!-- 导出的包、类、资源列表 --> <exported> <packages> <package>com.alipay.sofa.ark.sample.common</package> </packages> <classes> <class>com.alipay.sofa.ark.sample.facade.SamplePluginService</class> </classes> </exported> <!-- 打包后的插件的存放位置,默认${project.build.directory} --> <outputDirectory>../target</outputDirectory> </configuration> </execution> </executions> </plugin> </plugins> </build> |
此配置声明了当前插件的一切必要信息。
SamplePluginActivator负责在插件启动时,发布JVM服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package com.alipay.sofa.ark.sample.activator; import com.alipay.sofa.ark.exception.ArkRuntimeException; import com.alipay.sofa.ark.sample.facade.SamplePluginService; import com.alipay.sofa.ark.sample.impl.SamplePluginServiceImpl; import com.alipay.sofa.ark.spi.model.PluginContext; import com.alipay.sofa.ark.spi.service.PluginActivator; public class SamplePluginActivator implements PluginActivator { // 插件启动时的回调 public void start(PluginContext context) throws ArkRuntimeException { // 通过插件上下文发布服务 context.publishService(SamplePluginService.class, new SamplePluginServiceImpl()); } // 插件停止时的回调 public void stop(PluginContext context) throws ArkRuntimeException { System.out.println("stopping in ark plugin activator"); } } |
除了发布服务之外,你还可以引用其他插件或Ark容器发布的服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class SamplePluginServiceImpl implements SamplePluginService { // 引用Ark容器发布的,事件管理服务 @ArkInject private EventAdminService eventAdminService; public String service() { return "I'm a sample plugin service published by ark-plugin"; } public void sendEvent(ArkEvent arkEvent) { eventAdminService.sendEvent(arkEvent); } } |
使用sofa-ark-maven-plugin也可以把普通的Spring Boot项目打为Ark包:
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 |
<build> <plugins> <plugin> <groupId>com.alipay.sofa</groupId> <artifactId>sofa-ark-maven-plugin</artifactId> <executions> <execution> <id>default-cli</id> <!-- 此目标生成可执行Ark包 --> <goals> <goal>repackage</goal> </goals> <configuration> <!-- 可执行Ark包的输出目录 --> <outputDirectory>./target</outputDirectory> <!-- 可以指定Ark包的Maven坐标的classifier字段 --> <arkClassifier>executable-ark</arkClassifier> </configuration> </execution> </executions> </plugin> </plugins> </build> |
添加以下依赖即可:
1 2 3 4 5 |
<dependency> <groupId>com.alipay.sofa</groupId> <artifactId>sofa-ark-springboot-starter</artifactId> <version>${sofa.ark.version}</version> </dependency> |
需要添加依赖:
1 2 3 4 5 |
<dependency> <groupId>com.alipay.sofa</groupId> <artifactId>sofa-ark-support-starter</artifactId> <version>${sofa.ark.version}</version> </dependency> |
并且在入口点方法中启动Ark容器:
1 2 3 4 5 |
public class Application{ public static void main(String[] args) { SofaArkBootstrap.launch(args); } } |
SOFAArk提供了org.junit.runner.Runner的实现类ArkJUnit4Runner,用于集成JUnit4单元测试框架:
1 2 3 4 5 6 7 |
@RunWith(ArkJUnit4Runner.class) public class JUnitTest { @Test public void test() { Assert.assertTrue(true); } |
上面的用例会在Ark容器之上运行。
SOFAArk提供了org.junit.runner.Runner的实现类ArkBootRunner,用于在Spring Boot下进行集成测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@RunWith(ArkBootRunner.class) @SpringBootTest(classes = SpringbootDemoApplication.class) public class IntegrationTest { // 可以注入依赖 @Autowired private SampleService sampleService; @Test public void test() { sampleService.service(); } } |
这是一个Java的RPC框架,提供了负载均衡,流量转发,链路追踪,链路数据透传,故障剔除等特性,兼容 bolt,RESTful,dubbo,H2C协议。
SOFARPC的基本工作原理和Dubbo类似:
- 如果当前应用需要发布RPC服务,那么SOFARPC会将服务注册到服务注册中心
- 如果当前应用需要调用RPC服务,那么SOFARPC会到注册中心订阅相关服务的元数据。注册中心会即时的推送元数据更新,例如服务的端点信息
需要加入依赖:
1 2 3 4 5 |
<dependency> <groupId>com.alipay.sofa</groupId> <artifactId>sofa-rpc-all</artifactId> <version>5.6.0-SNAPSHOT</version> </dependency> |
需要发布的服务接口:
1 2 3 |
public interface HelloService { String sayHello(String string); } |
接口实现HelloServiceImpl这里略去。
SOFARPC服务器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class QuickStartServer { public static void main(String[] args) { ServerConfig serverConfig = new ServerConfig() .setProtocol("bolt") // 设置一个协议,默认bolt .setPort(12200) // 设置一个端口,默认12200 .setDaemon(false); // 非守护线程方式运行 // 使用ZK作为注册表 RegistryConfig registryConfig = new RegistryConfig() .setProtocol("zookeeper") .setAddress("127.0.0.1:2181"); ProviderConfig providerConfig = new ProviderConfig() .setInterfaceId(HelloService.class.getName()) // 指定接口 .setRef(new HelloServiceImpl()) // 指定实现 .setServer(serverConfig) // 指定服务端 .setRegistry(registryConfig); // 服务注册表 providerConfig.export(); // 发布服务 } } |
SOFARPC客户端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public class QuickStartClient { private final static Logger LOGGER = LoggerFactory.getLogger(QuickStartClient.class); public static void main(String[] args) { ConsumerConfig consumerConfig = new ConsumerConfig() .setInterfaceId(HelloService.class.getName()) // 指定接口 .setProtocol("bolt") // 指定协议 .setDirectUrl("bolt://127.0.0.1:12200") // 指定服务端的直连地址 .setConnectTimeout(10 * 1000); HelloService helloService = consumerConfig.refer(); helloService.sayHello("world"); } } |
SpringBoot应用的名字必须配置:
1 |
spring.application.name=test |
你需要为SOFABoot工程引入SOFARPC的starter:
1 2 3 4 |
<dependency> <groupId>com.alipay.sofa</groupId> <artifactId>rpc-sofa-boot-starter</artifactId> </dependency> |
你可以以POJO的形式定义服务接口和它的实现。
发布服务时,可以使用下面的XML配置:
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 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:sofa="http://sofastack.io/schema/sofaboot" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://sofastack.io/schema/sofaboot http://sofastack.io/schema/sofaboot.xsd" default-autowire="byName"> <!-- 服务的Bean定义 --> <bean id="personServiceImpl" class="com.alipay.sofa.boot.examples.demo.rpc.bean.PersonServiceImpl"/> <!-- 发布SOFARPC服务 --> <sofa:service ref="personServiceImpl" interface="com.alipay.sofa.boot.examples.demo.rpc.bean.PersonService"> <!-- 绑定的通信协议 --> <sofa:binding.bolt> <sofa:global-attrs timeout="3000" address-wait-time="2000"/> <!-- 调用超时;地址等待时间 --> <sofa:route target-url="127.0.0.1:22000"/> <!-- 直连到提供者,不走负载均衡 --> <sofa:method name="sayName" timeout="3000"/> <!-- 方法级别超时配置 --> </sofa:binding.bolt> <sofa:binding.rest/> </sofa:service> <!-- 订阅SOFARPC服务 --> <sofa:reference id="personReferenceBolt" interface="com.alipay.sofa.boot.examples.demo.rpc.bean.PersonService"> <sofa:binding.bolt/> </sofa:reference> <sofa:reference id="personReferenceRest" interface="com.alipay.sofa.boot.examples.demo.rpc.bean.PersonService"> <sofa:binding.rest/> </sofa:reference> </beans> |
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 |
// 发布服务 @SofaService(interfaceType = AnnotationService.class, bindings = { @SofaServiceBinding(bindingType = "bolt"), @SofaServiceBinding(bindingType = "bolt") }) @Component public class AnnotationServiceImpl implements AnnotationService { @Override public String sayAnnotation(String stirng) { return stirng; } } // 订阅服务 @Component public class AnnotationClientImpl { @SofaReference(interfaceType = AnnotationService.class, binding = @SofaReferenceBinding(bindingType = "bolt")) private AnnotationService annotationService; public String sayClientAnnotation(String str) { String result = annotationService.sayAnnotation(str); return result; } } |
SOFARPC支持过滤器。实现过滤器非常简单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class PersonServiceFilter extends Filter { @Override public SofaResponse invoke(FilterInvoker invoker, SofaRequest request) throws SofaRpcException { // 前置钩子 System.out.println("PersonFilter before"); try { // 执行RPC调用 return invoker.invoke(request); } finally { // 后置钩子 System.out.println("PersonFilter after"); } } } |
过滤器需要配置才能生效。
Bolt是一个高性能的TCP协议,其性能比HTTP好。
Bolt支持同步、异步、回调、单向等多种调用类型:
调用类型 | 说明 | ||||||||||||
Synchronous | 默认的调用类型,调用发起后,当前线程会阻塞以等待结果 | ||||||||||||
Asynchronous |
调用发起后,当前线程立即处理后续逻辑,当调用结果到达后SOFAGRPC会缓存之,你可以异步的调用API以获得结果 XML配置:
注解配置:
编程式,使用Spring的情况下:
编程式,不使用Spring的情况下:
要获得异步响应,调用:
JDK的Future对象也可以得到:
|
||||||||||||
Callback |
调用类型名称:callback 类似Asynchronous,不需要等待结果。当结果到达后,自动调用注册的回调函数。你需要实现如下的回调接口:
然后,你可以使用下面的方式注册回调接口。 XML方式:
注解方式:
编程式,使用Spring的情况下:
编程式,不使用Spring的情况下:
你还可以在调用期间临时的设置:
|
||||||||||||
Oneway |
调用类型名称:oneway 发送请求后就不管了,不在乎结果如何的情况下使用 |
使用Bolt协议时默认的超时是3s。你可以在多个级别设置超时。
在服务级别设置:
1 2 3 4 5 |
<sofa:reference interface="com.example.demo.SampleService" id="sampleService"> <sofa:binding.bolt> <sofa:global-attrs timeout="2000"/> </sofa:binding.bolt> </sofa:reference> |
1 2 |
@SofaReference(binding = @SofaReferenceBinding(bindingType = "bolt", timeout = 2000)) private SampleService sampleService; |
在方法级别设置:
1 2 3 4 5 |
<sofa:reference interface="com.example.demo.SampleService" id="sampleService"> <sofa:binding.bolt> <sofa:method name="hello" timeout="2000"/> </sofa:binding.bolt> </sofa:reference> |
SOFARPC允许消费者在不知道服务接口的情况下发起调用。前提条件是:
- 使用Bolt作为通信协议
- 使用Hessian 2作为串行化协议
SOFARPC支持Hessian 2、protobuf两个串行化协议,默认使用前者。如果要修改,参考:
1 2 3 4 5 6 7 8 9 10 11 |
sofa:service ref="sampleService" interface="com.alipay.sofarpc.demo.SampleService"> <sofa:binding.bolt> <sofa:global-attrs serialize-type="protobuf"/> </sofa:binding.bolt> </sofa:service> <!-- 提供者/消费者都需要设置 --> <sofa:reference interface="com.alipay.sofarpc.demo.SampleService" id="sampleServiceRef" jvm-first="false"> <sofa:binding.bolt> <sofa:global-attrs serialize-type="protobuf"/> </sofa:binding.bolt> </sofa:reference> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<bean id="helloService" class="com.alipay.sofa.rpc.quickstart.HelloService"/> <!-- 自定义一个线程池 --> <bean id="customExecutor" class="com.alipay.sofa.rpc.server.UserThreadPool" init-method="init"> <property name="corePoolSize" value="10" /> <property name="maximumPoolSize" value="10" /> <property name="queueSize" value="0" /> </bean> <sofa:service ref="helloService" interface="XXXService"> <sofa:binding.bolt> <!-- 引用线程池 --> <sofa:global-attrs thread-pool-ref="customExecutor"/> </sofa:binding.bolt> </sofa:service> |
或者,使用注解方式:
1 2 3 |
@SofaService(bindings = {@SofaServiceBinding(bindingType = "bolt", userThreadPool = "customThreadPool")}) public class SampleServiceImpl implements SampleService { } |
SOFARPC支持RESTful协议,使用此协议时,你需要为服务接口添加JAX-RS注解:
1 2 3 4 5 6 |
@Path("sample") public interface SampleService { @GET @Path("hello") String hello(); } |
发布服务的方式如下:
1 2 3 4 5 6 7 8 9 |
@Service // 设置绑定类型 @SofaService(bindings = {@SofaServiceBinding(bindingType = "rest")}) public class RestfulSampleServiceImpl implements SampleService { @Override public String hello() { return "Hello"; } } |
这种服务直接可以通过浏览器访问: http://localhost:8341/sample/hello
在SOFARPC客户端,消费服务的方式如下:
1 2 |
@SofaReference(binding = @SofaReferenceBinding(bindingType = "rest")) private SampleService sampleService; |
SOFARPC支持多种注册中心。当前bolt、rest、duboo传输协议均支持ZooKeeper作为注册中心,bolt、rest还支持本地文件系统作为注册中心(主要用于测试)。
当前SOFARPC(SOFARPC: 5.5.2, SOFABoot: 2.6.3)已经支持SOFARegistry注册中心:
1 |
com.alipay.sofa.rpc.registry.address=sofa://127.0.0.1:9603 |
要使用此注册中心,配置:
1 2 3 |
com.alipay.sofa.rpc.registry.address=zookeeper://127.0.0.1:2181 # 指定身份验证信息 com.alipay.sofa.rpc.registry.address=zookeeper://xxx:2181?file=/home/admin/registry&scheme=digest&addAuth=sofazk:rpc1 |
要使用此注册中心,配置:
1 |
com.alipay.sofa.rpc.registry.address=local:///home/admin/registry/localRegistry.reg |
服务消费者可以直接指定服务提供者的地址:
1 2 3 4 |
ConsumerConfig consumer = new ConsumerConfig() .setInterfaceId(HelloService.class.getName()) .setRegistry(registryConfig) .setDirectUrl("bolt://127.0.0.1:12201"); |
1 2 3 4 5 |
<sofa:reference interface="com.alipay.sample.HelloService" id="helloService"> <sofa:binding.bolt> <sofa:route target-url="127.0.0.1:12200"/> </sofa:binding.bolt> </sofa:reference> |
1 2 |
@SofaReference(binding = @SofaReferenceBinding(bindingType = "bolt", directUrl = "127.0.0.1:12220")) private SampleService sampleService; |
而不经过SOFARPC的负载均衡系统。
目前支持的负载均衡算法:
算法 | 说明 |
random | 随即选取服务提供者,默认 |
localPref | 如果存在本地可用的服务提供者,优先使用之 |
roundRobin | 轮询算法 |
consistentHash | 一致性哈希算法,每个相同方法级别的请求路由到同一节点 |
weightRoundRobin | 加权的轮询 |
配置负载均衡算法的方式如下:
1 2 3 4 5 |
<sofa:reference interface="com.example.demo.SampleService" id="sampleService"> <sofa:binding.bolt> <sofa:global-attrs loadBalancer="roundRobin"/> </sofa:binding.bolt> </sofa:reference> |
重试的配置方式如下:
1 2 3 4 5 |
<sofa:reference jvm-first="false" id="retriesServiceReferenceBolt" interface="com.alipay.sofa.rpc.samples.retries.RetriesService"> <sofa:binding.bolt> <sofa:global-attrs retries="2"/> </sofa:binding.bolt> </sofa:reference> |
1 2 |
@SofaReference(binding = @SofaReferenceBinding(bindingType = "bolt", retries = 2)) private SampleService sampleService; |
SOFARPC目前支持以下Tracer:
- SOFATracer,内置集成
- Skywalking
如果要禁用分布式追踪特性,可以配置:
1 |
com.alipay.sofa.rpc.defaultTracer= |
SOFARPC支持内置的容错机制,还可以和Hystrix进行集成。
运行机制:
- 单机故障剔除会统计一个时间窗口内的调用次数和异常次数,并计算每个服务对应ip的异常率和该服务的平均异常率
- 当达到ip异常率大于服务平均异常率到一定比例时,会对服务+ip的维度进行权重降级
- 如果该服务+ip维度的权重并没有降为0,那么当该服务+ip维度的调用情况正常时,则会对其进行权重恢复
- 整个计算和调控过程异步进行,不会阻塞调用
使用这种单机故障剔除,需要进行如下配置:
1 2 3 4 5 6 7 8 9 |
FaultToleranceConfig faultToleranceConfig = new FaultToleranceConfig(); faultToleranceConfig.setRegulationEffective(true); faultToleranceConfig.setDegradeEffective(true); // 时间窗口 20s faultToleranceConfig.setTimeWindow(20); // 如果被判定为故障,则权重掉1/2 faultToleranceConfig.setWeightDegradeRate(0.5); FaultToleranceConfigManager.putAppConfig("appName", faultToleranceConfig); |
基于Hystrix的熔断能力,目前还不健全。要使用此特性,添加Hystrix的依赖:
1 2 3 4 5 |
<dependency> <groupId>com.netflix.hystrix</groupId> <artifactId>hystrix-core</artifactId> <version>1.5.12</version> </dependency> |
然后显式启用Hystrix(导致Hystrix过滤器被加载):
1 2 3 4 5 6 7 |
// 全局开启 RpcConfigs.putValue(HystrixConstants.SOFA_HYSTRIX_ENABLED, true); // 对特定 Consumer 开启 ConsumerConfig consumerConfig = new ConsumerConfig() .setInterfaceId(HelloService.class.getName()) .setParameter(HystrixConstants.SOFA_HYSTRIX_ENABLED, String.valueOf(true)); |
FallbackFactory接口用于注入Hystrix的Fallback,当Hystrix遇到异常、超时、线程池拒绝、熔断等情况时,指定执行此Fallback(降级)逻辑:
1 2 3 4 5 |
// 可以直接使用默认的 FallbackFactory 直接注入 Fallback 实现 SofaHystrixConfig.registerFallback(consumerConfig, new HelloServiceFallback()); // 也可以自定义 FallbackFactory 直接注入 FallbackFactory SofaHystrixConfig.registerFallbackFactory(consumerConfig, new HelloServiceFallbackFactory()); |
SOFARPC注册了JVM的ShutdownHook,用于实现优雅的资源清理。
这是一个服务注册中心,和ZooKeeper、Etcd等项目对比有自己的特点:
- 高度可扩容:数据分片存储,理论上可以支持无限水平扩容
- 低延迟:基于SOFABolt框架,实现TCP长连接下的变更推送。主流注册中心都是这种架构
- 高可用:在CAP中保证AP,放弃严格一致性,尽可能在网络分区时保证可用性。这和ZooKeeper、Etcd不同。需要注意的是,Envoy xDS协议也是最终一致性的
在架构上,SOFARegistry引入4种角色:
- Client:通过客户端JAR包接入注册中心
- SessionServer:直接处理Client的服务发布/订阅请求,可以无限扩容。客户端仅和SessionServer交互
- DataServer:负责存储服务的元数据,使用一致性哈希分片存储,支持多副本,可以无限扩容
- MetaServer:维护SessionServer、DataServer集群的一致性列表,在节点发生变更时发出通知
术语 | 说明 |
服务 Service |
通过网络提供的、具有特定业务逻辑处理能力的软件功能 |
服务提供者 Service Provider |
通过网络提供服务的计算机节点 |
服务消费者 Service Consumer |
通过网络调用服务的计算机节点。一个计算机节点可以既作为一些服务的提供者,又作为一些服务的消费者 |
服务发现 Service Discovery |
服务消费者获取服务提供者的网络地址的过程 |
服务注册中心 Service Registry |
一种提供服务发现功能的软件系统,帮助服务消费者获取服务提供者的网络地址 |
术语 | 说明 |
数据(Data) | 在服务发现场景下,特指服务提供者的网络地址及其它附加信息。其他场景下,也可以表示任意发布到 SOFARegistry 的信息 |
单元(Zone) | 单元化架构关键概念,在服务发现场景下,单元是一组发布与订阅的集合,发布及订阅服务时需指定单元名 |
发布者(Publisher) | 发布数据到 SOFARegistry 的节点。在服务发现场景下,服务提供者就是“服务提供者的网络地址及其它附加信息”的发布者 |
订阅者(Subscriber) | 从 SOFARegistry 订阅数据的节点。在服务发现场景下,服务消费者就是“服务提供者的网络地址及其它附加信息”的订阅者 |
数据标识(DataId) | 用来标识数据的字符串。在服务发现场景下,通常由服务接口名、协议、版本号等信息组成,作为服务的标识 |
分组标识(GroupId) | 用于为数据归类的字符串,可以作为数据标识的命名空间,即只有 DataId、GroupId、InstanceId 都相同的服务,才属于同一服务 |
实例 ID(InstanceId) | 实例 ID,可以作为数据标识的命名空间,即只有DataId、GroupId、InstanceId都相同的服务,才属于同一服务 |
会话服务器(SessionServer) | SOFARegistry 内部负责跟客户端建立 TCP 长连接、进行数据交互的一种服务器角色 |
数据服务器(DataServer) | SOFARegistry 内部负责数据存储的一种服务器角色。 |
元信息服务器(MetaServer) | SOFARegistry 内部基于 Raft 协议,负责集群内一致性协调的一种服务器角色。 |
支持独立部署、集成部署方式。后者比较简单,单节点部署,可以用于测试。
1 2 3 4 5 6 7 |
wget https://github.com/alipay/sofa-registry/releases/download/v5.2.0/registry-integration-5.2.0.tar.gz mkdir registry-integration tar -zxvf registry-integration-5.2.0.tar.gz -C registry-integration cd registry-integration # 启动脚本位于 bin/startup.sh |
启动服务器后,执行下面的命令查看各服务器端角色的健康状况:
1 2 3 4 5 6 7 8 9 10 11 |
# 查看meta角色的健康检测接口: curl http://localhost:9615/health/check # {"success":true,"message":"... raftStatus:Leader"} # 查看data角色的健康检测接口: curl http://localhost:9622/health/check # {"success":true,"message":"... status:WORKING"} # 查看session角色的健康检测接口: curl http://localhost:9603/health/check # {"success":true,"message":"..."} |
引入依赖:
1 2 3 4 |
<dependency> <groupId>com.alipay.sofa</groupId> <artifactId>registry-client-all</artifactId> </dependency> |
下面的代码演示了如何发布数据到SOFARegistry上:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// 构建客户端实例 RegistryClientConfig config = DefaultRegistryClientConfigBuilder.start() // 服务器连接地址,任意一个Session节点都可以 .setRegistryEndpoint("127.0.0.1").setRegistryEndpointPort(9603).build(); DefaultRegistryClient registryClient = new DefaultRegistryClient(config); registryClient.init(); // 构造发布者注册表 // dataId是此客户端发布的服务的唯一标识 String dataId = "com.alipay.test.demo.service:1.0@DEFAULT"; PublisherRegistration registration = new PublisherRegistration(dataId); // 将注册表注册进客户端并发布数据 registryClient.register(registration, "10.10.1.1:12200?xx=yy"); |
下面的代码演示了如何从SOFARegistry订阅数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// 构建客户端实例 RegistryClientConfig config = DefaultRegistryClientConfigBuilder.start() .setRegistryEndpoint("127.0.0.1").setRegistryEndpointPort(9603).build(); DefaultRegistryClient registryClient = new DefaultRegistryClient(config); registryClient.init(); // 创建 SubscriberDataObserver SubscriberDataObserver subscriberDataObserver = new SubscriberDataObserver() { public void handleData(String dataId, UserData userData) { // 在这里处理接收到的数据 System.out.println("receive data success, dataId: " + dataId + ", data: " + userData); } }; // 构造订阅者注册表 String dataId = "com.alipay.test.demo.service:1.0@DEFAULT"; SubscriberRegistration registration = new SubscriberRegistration(dataId, subscriberDataObserver); // 订阅的范围:zone, dataCenter, global registration.setScopeEnum(ScopeEnum.global); // 将注册表注册进客户端并订阅数据,订阅到的数据会以回调的方式通知 SubscriberDataObserver registryClient.register(registration); |
有数据更新后,服务器会推送并回调,你需要在回调中处理 UserData:
1 2 3 4 5 6 |
public interface UserData { // 以Zone分组的数据 Map<String, List> getZoneData(); // 当前Zone String getLocalZone(); } |
遵循OpenTracing规范的Tracer。可以将将一个Trace的链路信息打印到日志或发送给Zipkin进行展示。 特性包括:
- 基于Disruptor实现高性能的Trace日志落盘。支持两种打印类型:
- 摘要日志:每一次调用均会落地磁盘的日志
- 统计日志:每隔一定时间间隔进行统计输出的日志
- 支持日志自清除和轮换
- 集成SLF4J的MDC,修改日志配置即可输出当前Trace上下文的TraceId 和 SpanId
- 已开发多种开源项目的埋点:
- Spring MVC
- 基于标准JDBC接口的数据库连接池,包括DBCP、Druid、c3p0、tomcat、HikariCP、BoneCP
- HttpClient
- RestTemplate
- OkHttp
- Dubbo
- OpenFeign
- Redis、消息中间件的埋点仍然在开发中
术语 | 说明 | ||
TraceId |
在SOFATracer 中代表一次请求的唯一标识,此 ID 一般由集群中第一个处理请求的系统产生,并在分布式调用下通过网络传递到下一个被请求系统 TraceId的示例:
|
||
SpanId | SpanId 代表了本次请求在整个调用链路中的位置或者说层次,比如 A 系统在处理一个请求的过程中依次调用了 B,C,D 三个系统,那么这三次调用的的 SpanId 分别是:0.1,0.2,0.3。如果 B 系统继续调用了 E,F 两个系统,那么这两次调用的 SpanId 分别是:0.1.1,0.1.2 |
配置项 | 说明 |
logging.path |
日志输出目录 SOFATracer 会优先输出到 logging.path 目录下;如果没有配置日志输出目录,那默认输出到 ${user.home} |
com.alipay.sofa.tracer.disableDigestLog |
是否关闭所有集成 SOFATracer 组件摘要日志打印 默认false |
com.alipay.sofa.tracer.disableConfiguration[${logType}] |
关闭指定 ${logType} 的 SOFATracer 组件摘要日志打印。${logType}是指具体的日志类型,如:spring-mvc-digest.log 默认false |
com.alipay.sofa.tracer.tracerGlobalRollingPolicy |
SOFATracer 日志的滚动策略 .yyyy-MM-dd:按照天滚动,默认 |
com.alipay.sofa.tracer.tracerGlobalLogReserveDay |
SOFATracer 日志的保留天数 默认保留 7 天 |
com.alipay.sofa.tracer.statLogInterval |
统计日志的时间间隔,单位:秒 默认 60 秒统计日志输出一次 |
com.alipay.sofa.tracer.baggageMaxLength |
透传数据能够允许存放的最大长度 默认值 1024 |
com.alipay.sofa.tracer.zipkin.enabled |
是否开启 SOFATracer 远程上报数据到 Zipkin true:开启上报;false:关闭上报。默认不上报 |
com.alipay.sofa.tracer.zipkin.baseUrl |
SOFATracer 远程上报数据到 Zipkin 的地址,com.alipay.sofa.tracer.zipkin.enabled=true时配置此地址才有意义 格式:http://${host}:${port} |
com.alipay.sofa.tracer.springmvc.filterOrder | SOFATracer 集成在 SpringMVC 的 Filter 生效的 Order |
com.alipay.sofa.tracer.springmvc.urlPatterns |
SOFATracer 集成在 SpringMVC 的 Filter 生效的 URL Pattern 路径 默认/*,全部生效 |
SLF4J 提供了 MDC (Mapped Diagnostic Contexts)功能,支持用户定义和修改日志的输出格式以及内容。SOFATracer支持基于MDC来输出当前Trace上下文的TraceId、SpanId。
需要引入SLF4J的API,以及日志实现包的Maven依赖:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> </dependency> <!-- Logback --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </dependency> <!-- Log4j2 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> <!--SOFABoot没有管控log4j2 版本 --> <version>1.4.2.RELEASE</version> </dependency> |
配置日志输出的PatternLayout:
1 |
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %5p [%X{SOFA-TraceId}, %X{SOFA-SpanId}] -- %m%n</pattern> |
%X表示,在调用appender输出日志时,将此占位符替换为当前线程上下文(MDC)中的SOFA-TraceId、SOFA-SpanId变量。
如果用户启动新线程来处理业务,则基于线程本地变量传递的Trace信息就丢失了。为了将SOFATracer日志上下文从父线程传递到子线程,可以考虑用SofaTracerRunnable:
1 2 3 4 5 6 7 8 |
// 使用SOFATracer提供的包装器 Thread thread = new Thread(new SofaTracerRunnable(new Runnable() { @Override public void run() { // 异步业务逻辑 } })); thread.start(); |
基于java.util.concurrent.Callable发动新线程时类似:
1 2 3 4 5 6 7 8 9 10 11 12 |
ExecutorService executor = Executors.newCachedThreadPool(); // 使用SOFATracer提供的包装器 SofaTracerCallable<Object> sofaTracerSpanSofaTracerCallable = new SofaTracerCallable<Object>(new Callable<Object>() { @Override public Object call() throws Exception { // 异步业务逻辑 return ...; } }); Future<Object> futureResult = executor.submit(sofaTracerSpanSofaTracerCallable); // ... Object objectReturn = futureResult.get(); |
SOFATracer支持两种采样模式:
- 基于BitSet实现的固定采样率的采样模式
- 用户自定义实现采样的采样模式
进行以下配置即可:
1 2 3 4 |
# 采样率0-100之间 com.alipay.sofa.tracer.samplerPercentage=100 # 采样模式类型名称 com.alipay.sofa.tracer.samplerName=PercentageBasedSampler |
此项目解决的是监控领域的问题,提供指标的埋点、收集、加工、存储、查询等服务,分为客户端、服务器两个部分。
此项目已经快一年没有更新。
扩展了Istio项目,并做了以下改进:
- 将数据平面的Envoy替换为SOFAMosn
- 下沉Mixer的功能到数据平面,提升性能
- 扩展Pilot,支持更多的服务发现机制(服务注册表),包括SOFARPC、Dubbo
SOFARPC、Dubbo之类的入侵式框架可以在Istio中运行,但是无法对其进行任何管理、监控。SOFAMesh解决了这些痛点。
MOSN(Modular Observable Smart Network,模块化可观察智能网络…)是Envoy的替代品,其出现的主要原因不是Envoy不行,而是阿里系不愿引入C++技术栈。
MOSN增加了对SOFARPC、Dubbo协议的支持,后者仍然在开发中。
Leave a Reply