diff --git a/.github/workflows/cloud_code_scan.yml b/.github/workflows/cloud_code_scan.yml
index 7862061e8..f735fd3d9 100644
--- a/.github/workflows/cloud_code_scan.yml
+++ b/.github/workflows/cloud_code_scan.yml
@@ -1,13 +1,22 @@
name: Alipay Cloud Devops Codescan
on:
- push:
-
+ pull_request_target:
jobs:
- deployment:
+ stc: #安全扫描
runs-on: ubuntu-latest
steps:
- name: codeScan
- uses: layotto/alipay-cloud-devops-codescan@0.1.20230724
+ uses: layotto/alipay-cloud-devops-codescan@main
with:
parent_uid: ${{ secrets.ALI_PID }}
private_key: ${{ secrets.ALI_PK }}
+ scan_type: stc
+ sca: # 开源合规
+ runs-on: ubuntu-latest
+ steps:
+ - name: codeScan
+ uses: layotto/alipay-cloud-devops-codescan@main
+ with:
+ parent_uid: ${{ secrets.ALI_PID }}
+ private_key: ${{ secrets.ALI_PK }}
+ scan_type: sca
diff --git a/README.md b/README.md
index d6f4a0914..fca3ecbba 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,7 @@

[](https://github.com/sofastack/sofa-rpc/releases)
[](https://isitmaintained.com/project/sofastack/sofa-rpc "Percentage of issues still open")
+[](https://codeblitz.cloud.alipay.com/github/sofastack/sofa-rpc)
## Overview
diff --git a/README_zh_CN.md b/README_zh_CN.md
index 6a9fbfcc1..c172a3c9b 100644
--- a/README_zh_CN.md
+++ b/README_zh_CN.md
@@ -4,6 +4,7 @@
[](https://codecov.io/gh/sofastack/sofa-rpc)

[](https://github.com/sofastack/sofa-rpc/releases)
+[](https://codeblitz.cloud.alipay.com/github/sofastack/sofa-rpc)
SOFARPC 是一个高可扩展性、高性能、生产级的 Java RPC 框架。在蚂蚁金服 SOFARPC 已经经历了十多年及五代版本的发展。SOFARPC 致力于简化应用之间的 RPC 调用,为应用提供方便透明、稳定高效的点对点远程服务调用方案。为了用户和开发者方便的进行功能扩展,SOFARPC 提供了丰富的模型抽象和可扩展接口,包括过滤器、路由、负载均衡等等。同时围绕 SOFARPC 框架及其周边组件提供丰富的微服务治理方案。
diff --git a/all/pom.xml b/all/pom.xml
index bc7b13675..a35795632 100644
--- a/all/pom.xml
+++ b/all/pom.xml
@@ -6,7 +6,7 @@
com.alipay.sofasofa-rpc-all
- 5.11.0-SNAPSHOT
+ 5.14.0-SNAPSHOT${project.groupId}:${project.artifactId}
@@ -22,25 +22,25 @@
- The Ant Financial
- http://www.antfin.com/
+ SOFAStack
+ https://www.sofastack.tech/Geng Zhang
- zhanggeng.zg@antfin.com
- The Ant Financial
+ zhanggeng.zg@antgroup.com
+ SOFAStackWei Li
- lw111072@antfin.com
- The Ant Financial
+ lw111072@antgroup.com
+ SOFAStackZhiYuan Lei
- zhiyuan.lzy@antfin.com
- The Ant Financial
+ zhiyuan.lzy@antgroup.com
+ SOFAStack
@@ -56,16 +56,15 @@
1.8utf-81.7.21
- 1.3.2
- 3.28.0-GA
+ 1.4.0
+ 3.29.2-GA4.1.44.Final
- 3.4.0
+ 3.5.53.6.3.Final
- 1.6.6
- 3.0.8
- 1.4.1
+ 1.6.10
+ 3.1.61.9.8
- 6.3.0
+ 6.5.61.6.97.04.5.13
@@ -73,6 +72,8 @@
1.53.032.0.0-jre2.12.1
+ 0.4.1
+ 1.26.0
@@ -183,6 +184,11 @@
sofa-rpc-codec-jackson${project.version}
+
+ com.alipay.sofa
+ sofa-rpc-codec-sofa-fury
+ ${project.version}
+ com.alipay.sofasofa-rpc-fault-tolerance
@@ -238,6 +244,11 @@
sofa-rpc-registry-polaris${project.version}
+
+ com.alipay.sofa
+ sofa-rpc-registry-kubernetes
+ ${project.version}
+ com.alipay.sofasofa-rpc-remoting-bolt
@@ -273,11 +284,6 @@
sofa-rpc-tracer-opentracing-triple${project.version}
-
- com.alipay.sofa
- sofa-rpc-metrics-lookout
- ${project.version}
- com.alipay.sofasofa-rpc-metrics-micrometer
@@ -298,6 +304,16 @@
sofa-rpc-config-apollo${project.version}
+
+ com.alipay.sofa
+ sofa-rpc-config-zk
+ ${project.version}
+
+
+ com.alipay.sofa
+ sofa-rpc-config-nacos
+ ${project.version}
+ com.alipay.sofabolt
@@ -364,11 +380,6 @@
resteasy-jackson2-provider${resteasy.version}
-
- com.alipay.sofa.lookout
- lookout-api
- ${lookout.version}
- io.swaggerswagger-core
@@ -429,6 +440,16 @@
asm${asm.version}
+
+ org.furyio
+ fury-core
+ ${fury.version}
+
+
+ org.apache.commons
+ commons-compress
+ ${commons_compress_version}
+
@@ -519,10 +540,10 @@
com.alipay.sofa:sofa-rpc-codec-jacksoncom.alipay.sofa:sofa-rpc-codec-msgpackcom.alipay.sofa:sofa-rpc-codec-sofa-hessian
+ com.alipay.sofa:sofa-rpc-codec-sofa-furycom.alipay.sofa:sofa-rpc-fault-tolerancecom.alipay.sofa:sofa-rpc-fault-hystrixcom.alipay.sofa:sofa-rpc-log-common-tools
- com.alipay.sofa:sofa-rpc-metrics-lookoutcom.alipay.sofa:sofa-rpc-metrics-micrometercom.alipay.sofa:sofa-rpc-metrics-prometheuscom.alipay.sofa:sofa-rpc-registry-consul
@@ -533,6 +554,7 @@
com.alipay.sofa:sofa-rpc-registry-multicastcom.alipay.sofa:sofa-rpc-registry-sofacom.alipay.sofa:sofa-rpc-registry-polaris
+ com.alipay.sofa:sofa-rpc-registry-kubernetescom.alipay.sofa:sofa-rpc-remoting-boltcom.alipay.sofa:sofa-rpc-remoting-httpcom.alipay.sofa:sofa-rpc-remoting-resteasy
@@ -541,6 +563,8 @@
com.alipay.sofa:sofa-rpc-tracer-opentracing-resteasycom.alipay.sofa:sofa-rpc-tracer-opentracing-triplecom.alipay.sofa:sofa-rpc-config-apollo
+ com.alipay.sofa:sofa-rpc-config-zk
+ com.alipay.sofa:sofa-rpc-config-nacoscom.alipay.sofa:sofa-rpc-doc-swagger
diff --git a/bom/pom.xml b/bom/pom.xml
index 8d75765af..799d598dd 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -10,34 +10,36 @@
pom
- 5.11.0-SNAPSHOT
- 3.28.0-GA
+ 5.14.0-SNAPSHOT
+ 3.29.2-GA1.9.84.1.77.Final2.53.6.3.Final1.0.2.Final
- 3.4.10
+ 3.5.87.5.4.v20111024
- 4.0.1
+ 3.5.7
+ 4.3.00.22.0
- 2.6.9
+ 3.1.112.0.3
- 6.3.0
+ 6.5.61.2.21.6.97.032.0.0-jre0.16.0
- 3.4.0
+ 3.5.50.9.23.22.02.12.72.12.7.10.6.121.5.9
+ 0.4.11.53.0
@@ -47,17 +49,19 @@
2.12.11.7.21
- 1.2.3
+ 1.2.134.13.1
- 1.6.6
- 1.3.2
- 3.0.8
- 1.4.1
+ 1.6.10
+ 1.4.0
+ 3.1.6truetrue
+
+ 6.9.2
+ 1.26.0
@@ -297,28 +301,49 @@
msgpack${msgpack.version}
+
+ org.furyio
+ fury-core
+ ${fury.version}
+ org.apache.curator
- curator-recipes
+ curator-framework${curator.version}
+
+
+ org.apache.zookeeper
+ zookeeper
+
+ org.apache.curator
- curator-test
+ curator-x-discovery${curator.version}
- test
- zookeeperorg.apache.zookeeper
+ zookeeper
+
+
+
+
+ org.apache.zookeeper
+ zookeeper
+ ${zookeeper.version}
+
+
+ io.netty
+ netty
- com.alibaba
+ org.apache.dubbodubbo${dubbo.version}
@@ -439,24 +464,13 @@
commons-iocommons-io
- 2.7
+ 2.14.0com.alipay.sofatracer-core${tracer.version}
-
-
- com.alipay.sofa.lookout
- lookout-api
- ${lookout.version}
-
-
- com.alipay.sofa.lookout
- lookout-core
- ${lookout.version}
- io.micrometer
@@ -490,8 +504,34 @@
grpc-stub${grpc.version}
+
+
+ io.fabric8
+ kubernetes-client
+ ${fabric8_kubernetes_version}
+
+
+ io.fabric8
+ kubernetes-server-mock
+ test
+ ${fabric8_kubernetes_version}
+
+
+
+
+ org.apache.curator
+ curator-test
+ ${curator.version}
+
+
+ org.apache.zookeeper
+ zookeeper
+
+
+ test
+ junitjunit
@@ -563,6 +603,12 @@
0.16.0test
+
+
+ org.apache.commons
+ commons-compress
+ ${commons_compress_version}
+
diff --git a/bootstrap/bootstrap-api/src/main/java/com/alipay/sofa/rpc/bootstrap/DefaultClientProxyInvoker.java b/bootstrap/bootstrap-api/src/main/java/com/alipay/sofa/rpc/bootstrap/DefaultClientProxyInvoker.java
index afcfcc7af..0aadda9c9 100644
--- a/bootstrap/bootstrap-api/src/main/java/com/alipay/sofa/rpc/bootstrap/DefaultClientProxyInvoker.java
+++ b/bootstrap/bootstrap-api/src/main/java/com/alipay/sofa/rpc/bootstrap/DefaultClientProxyInvoker.java
@@ -133,6 +133,7 @@ protected void decorateRequest(SofaRequest request) {
// 额外属性通过HEAD传递给服务端
request.addRequestProp(RemotingConstants.HEAD_APP_NAME, consumerConfig.getAppName());
request.addRequestProp(RemotingConstants.HEAD_PROTOCOL, consumerConfig.getProtocol());
+ request.addRequestProp(RemotingConstants.HEAD_INVOKE_TYPE, request.getInvokeType());
customRequest(request, internalContext);
diff --git a/bootstrap/bootstrap-api/src/main/java/com/alipay/sofa/rpc/bootstrap/DefaultConsumerBootstrap.java b/bootstrap/bootstrap-api/src/main/java/com/alipay/sofa/rpc/bootstrap/DefaultConsumerBootstrap.java
index f6ba175a7..7e559d89b 100644
--- a/bootstrap/bootstrap-api/src/main/java/com/alipay/sofa/rpc/bootstrap/DefaultConsumerBootstrap.java
+++ b/bootstrap/bootstrap-api/src/main/java/com/alipay/sofa/rpc/bootstrap/DefaultConsumerBootstrap.java
@@ -20,6 +20,7 @@
import com.alipay.sofa.rpc.client.Cluster;
import com.alipay.sofa.rpc.client.ClusterFactory;
import com.alipay.sofa.rpc.client.ProviderGroup;
+import com.alipay.sofa.rpc.common.RpcConstants;
import com.alipay.sofa.rpc.common.SofaConfigs;
import com.alipay.sofa.rpc.common.SofaOptions;
import com.alipay.sofa.rpc.common.utils.CommonUtils;
@@ -28,9 +29,12 @@
import com.alipay.sofa.rpc.config.RegistryConfig;
import com.alipay.sofa.rpc.context.RpcRuntimeContext;
import com.alipay.sofa.rpc.core.exception.SofaRpcRuntimeException;
+import com.alipay.sofa.rpc.dynamic.ConfigChangedEvent;
+import com.alipay.sofa.rpc.dynamic.ConfigChangeType;
import com.alipay.sofa.rpc.dynamic.DynamicConfigKeys;
import com.alipay.sofa.rpc.dynamic.DynamicConfigManager;
import com.alipay.sofa.rpc.dynamic.DynamicConfigManagerFactory;
+import com.alipay.sofa.rpc.dynamic.DynamicUrl;
import com.alipay.sofa.rpc.ext.Extension;
import com.alipay.sofa.rpc.invoke.Invoker;
import com.alipay.sofa.rpc.listener.ConfigListener;
@@ -44,8 +48,10 @@
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
@@ -54,6 +60,7 @@
import java.util.concurrent.atomic.AtomicInteger;
import static com.alipay.sofa.rpc.common.RpcConstants.REGISTRY_PROTOCOL_DOMAIN;
+import static com.alipay.sofa.common.config.SofaConfigs.getOrDefault;
/**
* Default consumer bootstrap.
@@ -146,7 +153,8 @@ public T refer() {
// build cluster
cluster = ClusterFactory.getCluster(this);
// build listeners
- consumerConfig.setConfigListener(buildConfigListener(this));
+ ConfigListener configListener = buildConfigListener(this);
+ consumerConfig.setConfigListener(configListener);
consumerConfig.setProviderInfoListener(buildProviderInfoListener(this));
// init cluster
cluster.init();
@@ -156,13 +164,25 @@ public T refer() {
proxyIns = (T) ProxyFactory.buildProxy(consumerConfig.getProxy(), consumerConfig.getProxyClass(),
proxyInvoker);
- //动态配置
+ //请求级别动态配置参数
final String dynamicAlias = consumerConfig.getParameter(DynamicConfigKeys.DYNAMIC_ALIAS);
if (StringUtils.isNotBlank(dynamicAlias)) {
final DynamicConfigManager dynamicManager = DynamicConfigManagerFactory.getDynamicManager(
- consumerConfig.getAppName(), dynamicAlias);
+ consumerConfig.getAppName(), dynamicAlias);
dynamicManager.initServiceConfiguration(consumerConfig.getInterfaceId());
}
+
+ //接口级别动态配置参数
+ Boolean dynamicConfigRefreshEnable = getOrDefault(DynamicConfigKeys.DYNAMIC_REFRESH_ENABLE);
+ String configCenterAddress = getOrDefault(DynamicConfigKeys.CONFIG_CENTER_ADDRESS);
+ if (dynamicConfigRefreshEnable && StringUtils.isNotBlank(configCenterAddress)) {
+ DynamicUrl dynamicUrl = new DynamicUrl(configCenterAddress);
+ //启用接口级别动态配置
+ final DynamicConfigManager dynamicManager = DynamicConfigManagerFactory.getDynamicManager(
+ consumerConfig.getAppName(), dynamicUrl.getProtocol());
+ dynamicManager.addListener(consumerConfig.getInterfaceId(), configListener);
+ dynamicManager.initServiceConfiguration(consumerConfig.getInterfaceId(), configListener);
+ }
} catch (Exception e) {
if (cluster != null) {
cluster.destroy();
@@ -438,8 +458,47 @@ public void updateAllProviders(List groups) {
*/
private class ConsumerAttributeListener implements ConfigListener {
+ // 可以动态配置的选项
+ private final Set supportDynamicConfigKeys = new HashSet<>();
+ private final Map newValueMap = new HashMap<>();
+
+ ConsumerAttributeListener() {
+ supportDynamicConfigKeys.add(RpcConstants.CONFIG_KEY_TIMEOUT);
+ supportDynamicConfigKeys.add(RpcConstants.CONFIG_KEY_RETRIES);
+ supportDynamicConfigKeys.add(RpcConstants.CONFIG_KEY_LOADBALANCER);
+ }
+
+ @Override
+ public void process(ConfigChangedEvent event) {
+ // 清除上次的动态配置值缓存
+ consumerConfig.getDynamicConfigValueCache().clear();
+ // 获取对应配置项的默认值
+ for (String key : newValueMap.keySet()) {
+ if (consumerConfig.getConfigValueCache().get(key) != null) {
+ newValueMap.put(key, String.valueOf(consumerConfig.getConfigValueCache().get(key)));
+ } else {
+ newValueMap.put(key, null);
+ }
+ }
+ if (!event.getChangeType().equals(ConfigChangeType.DELETED)) {
+ // ADDED or MODIFIED
+ Map dynamicValueMap = event.getDynamicValueMap();
+ for (String key : dynamicValueMap.keySet()) {
+ String tempKey = key.lastIndexOf(".") == -1 ? key : key.substring(key.lastIndexOf(".") + 1);
+ if (supportDynamicConfigKeys.contains(tempKey)) {
+ String value = dynamicValueMap.get(key);
+ if (StringUtils.isNotBlank(value)) {
+ consumerConfig.getDynamicConfigValueCache().put(key, value);
+ newValueMap.put(key, value);
+ }
+ }
+ }
+ }
+ attrUpdated(newValueMap);
+ }
+
@Override
- public void configChanged(Map newValue) {
+ public void configChanged(Map newValueMap) {
}
@@ -452,7 +511,7 @@ public synchronized void attrUpdated(Map newValueMap) {
Map oldValues = new HashMap();
boolean rerefer = false;
try { // 检查是否有变化
- // 是否过滤map?
+ // 是否过滤map?
for (Map.Entry entry : newValues.entrySet()) {
String newValue = entry.getValue();
String oldValue = consumerConfig.queryAttribute(entry.getKey());
diff --git a/bootstrap/bootstrap-api/src/main/java/com/alipay/sofa/rpc/bootstrap/DefaultProviderBootstrap.java b/bootstrap/bootstrap-api/src/main/java/com/alipay/sofa/rpc/bootstrap/DefaultProviderBootstrap.java
index e7e46737b..0d7cff051 100644
--- a/bootstrap/bootstrap-api/src/main/java/com/alipay/sofa/rpc/bootstrap/DefaultProviderBootstrap.java
+++ b/bootstrap/bootstrap-api/src/main/java/com/alipay/sofa/rpc/bootstrap/DefaultProviderBootstrap.java
@@ -26,6 +26,9 @@
import com.alipay.sofa.rpc.config.ServerConfig;
import com.alipay.sofa.rpc.context.RpcRuntimeContext;
import com.alipay.sofa.rpc.core.exception.SofaRpcRuntimeException;
+import com.alipay.sofa.rpc.event.EventBus;
+import com.alipay.sofa.rpc.event.ProviderProcessorRegisterEvent;
+import com.alipay.sofa.rpc.event.ProviderProcessorUnRegistryEvent;
import com.alipay.sofa.rpc.ext.Extension;
import com.alipay.sofa.rpc.invoke.Invoker;
import com.alipay.sofa.rpc.listener.ConfigListener;
@@ -175,6 +178,9 @@ private void doExport() {
Server server = serverConfig.buildIfAbsent();
// 注册请求调用器
server.registerProcessor(providerConfig, providerProxyInvoker);
+ if (EventBus.isEnable(ProviderProcessorRegisterEvent.class)) {
+ EventBus.post(new ProviderProcessorRegisterEvent(providerConfig, serverConfig));
+ }
if (serverConfig.isAutoStart()) {
server.start();
}
@@ -306,6 +312,9 @@ public void unExport() {
if (server != null) {
try {
server.unRegisterProcessor(providerConfig, serverConfig.isAutoStart());
+ if (EventBus.isEnable(ProviderProcessorUnRegistryEvent.class)) {
+ EventBus.post(new ProviderProcessorUnRegistryEvent(providerConfig, serverConfig));
+ }
} catch (Exception e) {
if (LOGGER.isWarnEnabled(appName)) {
// TODO WARN
diff --git a/bootstrap/bootstrap-dubbo/pom.xml b/bootstrap/bootstrap-dubbo/pom.xml
index 09d8f8865..12645457a 100644
--- a/bootstrap/bootstrap-dubbo/pom.xml
+++ b/bootstrap/bootstrap-dubbo/pom.xml
@@ -20,7 +20,7 @@
- com.alibaba
+ org.apache.dubbodubbo
diff --git a/bootstrap/bootstrap-dubbo/src/main/java/com/alipay/sofa/rpc/bootstrap/dubbo/DubboConsumerBootstrap.java b/bootstrap/bootstrap-dubbo/src/main/java/com/alipay/sofa/rpc/bootstrap/dubbo/DubboConsumerBootstrap.java
index b86357544..1cc260735 100644
--- a/bootstrap/bootstrap-dubbo/src/main/java/com/alipay/sofa/rpc/bootstrap/dubbo/DubboConsumerBootstrap.java
+++ b/bootstrap/bootstrap-dubbo/src/main/java/com/alipay/sofa/rpc/bootstrap/dubbo/DubboConsumerBootstrap.java
@@ -16,7 +16,7 @@
*/
package com.alipay.sofa.rpc.bootstrap.dubbo;
-import com.alibaba.dubbo.config.ReferenceConfig;
+import org.apache.dubbo.config.ReferenceConfig;
import com.alipay.sofa.rpc.bootstrap.ConsumerBootstrap;
import com.alipay.sofa.rpc.client.Cluster;
import com.alipay.sofa.rpc.client.ProviderGroup;
@@ -100,7 +100,7 @@ private void copyCommon(ConsumerConfig consumerConfig, ReferenceConfig ref
private void copyApplication(ConsumerConfig consumerConfig, ReferenceConfig referenceConfig) {
ApplicationConfig applicationConfig = consumerConfig.getApplication();
- com.alibaba.dubbo.config.ApplicationConfig dubboConfig = new com.alibaba.dubbo.config.ApplicationConfig();
+ org.apache.dubbo.config.ApplicationConfig dubboConfig = new org.apache.dubbo.config.ApplicationConfig();
dubboConfig.setName(applicationConfig.getAppName());
referenceConfig.setApplication(dubboConfig);
}
@@ -140,11 +140,10 @@ private void copyConsumer(ConsumerConfig consumerConfig, ReferenceConfig r
private void copyMethods(ConsumerConfig consumerConfig, ReferenceConfig referenceConfig) {
Map methodConfigs = consumerConfig.getMethods();
if (CommonUtils.isNotEmpty(methodConfigs)) {
- List dubboMethodConfigs =
- new ArrayList();
+ List dubboMethodConfigs = new ArrayList<>();
for (Map.Entry entry : methodConfigs.entrySet()) {
MethodConfig methodConfig = entry.getValue();
- com.alibaba.dubbo.config.MethodConfig dubboMethodConfig = new com.alibaba.dubbo.config.MethodConfig();
+ org.apache.dubbo.config.MethodConfig dubboMethodConfig = new org.apache.dubbo.config.MethodConfig();
dubboMethodConfig.setName(methodConfig.getName());
dubboMethodConfig.setParameters(methodConfig.getParameters());
dubboMethodConfig.setTimeout(methodConfig.getTimeout());
diff --git a/bootstrap/bootstrap-dubbo/src/main/java/com/alipay/sofa/rpc/bootstrap/dubbo/DubboConvertor.java b/bootstrap/bootstrap-dubbo/src/main/java/com/alipay/sofa/rpc/bootstrap/dubbo/DubboConvertor.java
index a2f8958c6..4e627c8d9 100644
--- a/bootstrap/bootstrap-dubbo/src/main/java/com/alipay/sofa/rpc/bootstrap/dubbo/DubboConvertor.java
+++ b/bootstrap/bootstrap-dubbo/src/main/java/com/alipay/sofa/rpc/bootstrap/dubbo/DubboConvertor.java
@@ -29,19 +29,18 @@
public class DubboConvertor {
public static void copyRegistries(com.alipay.sofa.rpc.config.AbstractInterfaceConfig sofaConfig,
- com.alibaba.dubbo.config.AbstractInterfaceConfig dubboConfig) {
+ org.apache.dubbo.config.AbstractInterfaceConfig dubboConfig) {
List registryConfigs = sofaConfig.getRegistry();
if (CommonUtils.isNotEmpty(registryConfigs)) {
- List dubboRegistryConfigs =
- new ArrayList();
+ List dubboRegistryConfigs = new ArrayList<>();
for (RegistryConfig registryConfig : registryConfigs) {
// 生成并丢到缓存里
- com.alibaba.dubbo.config.RegistryConfig dubboRegistryConfig = DubboSingleton.REGISTRY_MAP
+ org.apache.dubbo.config.RegistryConfig dubboRegistryConfig = DubboSingleton.REGISTRY_MAP
.get(registryConfig);
if (dubboRegistryConfig == null) {
- dubboRegistryConfig = new com.alibaba.dubbo.config.RegistryConfig();
+ dubboRegistryConfig = new org.apache.dubbo.config.RegistryConfig();
copyRegistryFields(registryConfig, dubboRegistryConfig);
- com.alibaba.dubbo.config.RegistryConfig old = DubboSingleton.REGISTRY_MAP.putIfAbsent(
+ org.apache.dubbo.config.RegistryConfig old = DubboSingleton.REGISTRY_MAP.putIfAbsent(
registryConfig, dubboRegistryConfig);
if (old != null) {
dubboRegistryConfig = old;
@@ -58,7 +57,7 @@ public static void copyRegistries(com.alipay.sofa.rpc.config.AbstractInterfaceCo
}
public static void copyRegistryFields(com.alipay.sofa.rpc.config.RegistryConfig sofaRegistryConfig,
- com.alibaba.dubbo.config.RegistryConfig dubboRegistryConfig) {
+ org.apache.dubbo.config.RegistryConfig dubboRegistryConfig) {
dubboRegistryConfig.setAddress(sofaRegistryConfig.getAddress());
dubboRegistryConfig.setProtocol(sofaRegistryConfig.getProtocol());
dubboRegistryConfig.setRegister(sofaRegistryConfig.isRegister());
diff --git a/bootstrap/bootstrap-dubbo/src/main/java/com/alipay/sofa/rpc/bootstrap/dubbo/DubboProviderBootstrap.java b/bootstrap/bootstrap-dubbo/src/main/java/com/alipay/sofa/rpc/bootstrap/dubbo/DubboProviderBootstrap.java
index 0ce91a38a..324b8fff9 100644
--- a/bootstrap/bootstrap-dubbo/src/main/java/com/alipay/sofa/rpc/bootstrap/dubbo/DubboProviderBootstrap.java
+++ b/bootstrap/bootstrap-dubbo/src/main/java/com/alipay/sofa/rpc/bootstrap/dubbo/DubboProviderBootstrap.java
@@ -16,8 +16,8 @@
*/
package com.alipay.sofa.rpc.bootstrap.dubbo;
-import com.alibaba.dubbo.config.ProtocolConfig;
-import com.alibaba.dubbo.config.ServiceConfig;
+import org.apache.dubbo.config.ProtocolConfig;
+import org.apache.dubbo.config.ServiceConfig;
import com.alipay.sofa.rpc.bootstrap.ProviderBootstrap;
import com.alipay.sofa.rpc.common.RpcConstants;
import com.alipay.sofa.rpc.common.Version;
diff --git a/bootstrap/bootstrap-dubbo/src/main/java/com/alipay/sofa/rpc/bootstrap/dubbo/DubboSingleton.java b/bootstrap/bootstrap-dubbo/src/main/java/com/alipay/sofa/rpc/bootstrap/dubbo/DubboSingleton.java
index 6e1153b44..b3e55b792 100644
--- a/bootstrap/bootstrap-dubbo/src/main/java/com/alipay/sofa/rpc/bootstrap/dubbo/DubboSingleton.java
+++ b/bootstrap/bootstrap-dubbo/src/main/java/com/alipay/sofa/rpc/bootstrap/dubbo/DubboSingleton.java
@@ -16,12 +16,12 @@
*/
package com.alipay.sofa.rpc.bootstrap.dubbo;
-import com.alibaba.dubbo.config.DubboShutdownHook;
-import com.alibaba.dubbo.config.ProtocolConfig;
+import org.apache.dubbo.config.ProtocolConfig;
import com.alipay.sofa.rpc.base.Destroyable;
import com.alipay.sofa.rpc.config.RegistryConfig;
import com.alipay.sofa.rpc.config.ServerConfig;
import com.alipay.sofa.rpc.context.RpcRuntimeContext;
+import org.apache.dubbo.rpc.model.FrameworkModel;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -50,17 +50,17 @@ public void postDestroy() {
/**
* sofa.SeverConfig --> dubbo.ProtocolConfig
*/
- final static ConcurrentMap SERVER_MAP = new ConcurrentHashMap();
+ final static ConcurrentMap SERVER_MAP = new ConcurrentHashMap<>();
/**
* sofa.RegistryConfig --> dubbo.RegistryConfig
*/
- final static ConcurrentMap REGISTRY_MAP = new ConcurrentHashMap();
+ final static ConcurrentMap REGISTRY_MAP = new ConcurrentHashMap<>();
/**
* Destroy all dubbo resources
*/
public static void destroyAll() {
- DubboShutdownHook.getDubboShutdownHook().destroyAll();
+ FrameworkModel.defaultModel().destroy();
}
}
diff --git a/bootstrap/bootstrap-dubbo/src/test/java/com/alipay/sofa/rpc/bootstrap/dubbo/DubooServerTest.java b/bootstrap/bootstrap-dubbo/src/test/java/com/alipay/sofa/rpc/bootstrap/dubbo/DubooServerTest.java
index 985d7b525..a702ff663 100644
--- a/bootstrap/bootstrap-dubbo/src/test/java/com/alipay/sofa/rpc/bootstrap/dubbo/DubooServerTest.java
+++ b/bootstrap/bootstrap-dubbo/src/test/java/com/alipay/sofa/rpc/bootstrap/dubbo/DubooServerTest.java
@@ -17,7 +17,7 @@
package com.alipay.sofa.rpc.bootstrap.dubbo;
import com.alibaba.dubbo.common.Constants;
-import com.alibaba.dubbo.common.utils.ConfigUtils;
+import org.apache.dubbo.common.constants.CommonConstants;
import com.alibaba.dubbo.rpc.RpcContext;
import com.alibaba.dubbo.rpc.service.GenericService;
import com.alipay.sofa.rpc.bootstrap.dubbo.demo.DemoService;
@@ -32,6 +32,8 @@
import com.alipay.sofa.rpc.context.RpcInvokeContext;
import com.alipay.sofa.rpc.context.RpcRunningState;
import com.alipay.sofa.rpc.context.RpcRuntimeContext;
+import org.apache.dubbo.config.ConfigKeys;
+import org.apache.dubbo.config.context.ConfigMode;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
@@ -52,10 +54,50 @@ public class DubooServerTest {
ConsumerConfig consumerConfig;
+ private static String OLD_VALUE_SHUTDOWN_WAIT_KEY;
+ private static String OLD_VALUE_DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE;
+ private static String OLD_VALUE_DUBBO_CONFIG_MODE;
+
//dubbo close wait time
- @AfterClass
+ @BeforeClass
public static void before() {
- ConfigUtils.getProperties().put(Constants.SHUTDOWN_WAIT_KEY, "1");
+ RpcRunningState.setUnitTestMode(true);
+ OLD_VALUE_SHUTDOWN_WAIT_KEY = System.getProperty(CommonConstants.SHUTDOWN_WAIT_KEY);
+ OLD_VALUE_DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE = System
+ .getProperty(ConfigKeys.DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE);
+ OLD_VALUE_DUBBO_CONFIG_MODE = System.getProperty(ConfigKeys.DUBBO_CONFIG_MODE);
+
+ System.setProperty(CommonConstants.SHUTDOWN_WAIT_KEY, "1");
+ System.setProperty(ConfigKeys.DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE, "true");
+ System.setProperty(ConfigKeys.DUBBO_CONFIG_MODE, ConfigMode.IGNORE.name());
+ }
+
+ @AfterClass
+ public static void after() {
+ if (OLD_VALUE_SHUTDOWN_WAIT_KEY == null) {
+ System.clearProperty(CommonConstants.SHUTDOWN_WAIT_KEY);
+ } else {
+ System.setProperty(CommonConstants.SHUTDOWN_WAIT_KEY, OLD_VALUE_SHUTDOWN_WAIT_KEY);
+ }
+
+ if (OLD_VALUE_DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE == null) {
+ System.clearProperty(ConfigKeys.DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE);
+ } else {
+ System.setProperty(ConfigKeys.DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE,
+ OLD_VALUE_DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE);
+ }
+
+ if (OLD_VALUE_DUBBO_CONFIG_MODE == null) {
+ System.clearProperty(ConfigKeys.DUBBO_CONFIG_MODE);
+ } else {
+ System.setProperty(ConfigKeys.DUBBO_CONFIG_MODE, OLD_VALUE_DUBBO_CONFIG_MODE);
+ }
+ }
+
+ @After
+ public void afterMethod() {
+ RpcInternalContext.removeAllContext();
+ RpcInvokeContext.removeContext();
}
@Test
@@ -288,7 +330,7 @@ public void testWithParameterWithVersion() {
}
}
- @Test(expected = com.alibaba.dubbo.rpc.RpcException.class)
+ @Test(expected = org.apache.dubbo.rpc.RpcException.class)
//同步调用,直连,dubbo 消费没有指定dubbo服务版本version
public void testConsumerWithNoDubboServiceVersion() {
// 只有1个线程 执行
@@ -324,17 +366,4 @@ public void testConsumerWithNoDubboServiceVersion() {
Assert.assertTrue(result.equalsIgnoreCase("hello xxx"));
}
-
- @BeforeClass
- public static void adBeforeClass() {
- RpcRunningState.setUnitTestMode(true);
- }
-
- @After
- public void afterMethod() {
- DubboSingleton.destroyAll();
- RpcRuntimeContext.destroy();
- RpcInternalContext.removeAllContext();
- RpcInvokeContext.removeContext();
- }
}
\ No newline at end of file
diff --git a/bootstrap/bootstrap-triple/pom.xml b/bootstrap/bootstrap-triple/pom.xml
index 2d7e74c00..2eea9c3c2 100644
--- a/bootstrap/bootstrap-triple/pom.xml
+++ b/bootstrap/bootstrap-triple/pom.xml
@@ -19,7 +19,7 @@
- com.alibaba
+ org.apache.dubbodubbo
diff --git a/codec/codec-api/pom.xml b/codec/codec-api/pom.xml
index 47b42b4fe..921fa0e31 100644
--- a/codec/codec-api/pom.xml
+++ b/codec/codec-api/pom.xml
@@ -38,6 +38,11 @@
junittest
+
+
+ org.apache.commons
+ commons-compress
+
diff --git a/codec/codec-api/src/main/java/com/alipay/sofa/rpc/codec/bzip2/Bzip2RpcCompressor.java b/codec/codec-api/src/main/java/com/alipay/sofa/rpc/codec/bzip2/Bzip2RpcCompressor.java
new file mode 100644
index 000000000..86993b037
--- /dev/null
+++ b/codec/codec-api/src/main/java/com/alipay/sofa/rpc/codec/bzip2/Bzip2RpcCompressor.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.codec.bzip2;
+
+import com.alipay.sofa.rpc.codec.Compressor;
+import com.alipay.sofa.rpc.ext.Extension;
+import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
+import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * bzip2 compressor, faster compression efficiency
+ *
+ * @author chengming
+ * @version Bzip2RpcCompressor.java, v 0.1 2024年02月28日 10:45 AM chengming
+ * @link https://commons.apache.org/proper/commons-compress/
+ */
+@Extension(value = "bzip2", code = 3)
+public class Bzip2RpcCompressor implements Compressor {
+
+ @Override
+ public byte[] compress(byte[] src) {
+ if (null == src || 0 == src.length) {
+ return new byte[0];
+ }
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ BZip2CompressorOutputStream cos;
+ try {
+ cos = new BZip2CompressorOutputStream(out);
+ cos.write(src);
+ cos.close();
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+
+ return out.toByteArray();
+ }
+
+ @Override
+ public byte[] deCompress(byte[] src) {
+ if (null == src || 0 == src.length) {
+ return new byte[0];
+ }
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(src);
+ try {
+ BZip2CompressorInputStream unZip = new BZip2CompressorInputStream(in);
+ byte[] buffer = new byte[2048];
+ int n;
+ while ((n = unZip.read(buffer)) >= 0) {
+ out.write(buffer, 0, n);
+ }
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ return out.toByteArray();
+ }
+}
\ No newline at end of file
diff --git a/codec/codec-sofa-hessian/src/main/java/com/alipay/sofa/rpc/codec/sofahessian/BlackListFileLoader.java b/codec/codec-api/src/main/java/com/alipay/sofa/rpc/codec/common/BlackAndWhiteListFileLoader.java
similarity index 59%
rename from codec/codec-sofa-hessian/src/main/java/com/alipay/sofa/rpc/codec/sofahessian/BlackListFileLoader.java
rename to codec/codec-api/src/main/java/com/alipay/sofa/rpc/codec/common/BlackAndWhiteListFileLoader.java
index 8b48c7a7b..54f4ddf64 100644
--- a/codec/codec-sofa-hessian/src/main/java/com/alipay/sofa/rpc/codec/sofahessian/BlackListFileLoader.java
+++ b/codec/codec-api/src/main/java/com/alipay/sofa/rpc/codec/common/BlackAndWhiteListFileLoader.java
@@ -14,10 +14,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.alipay.sofa.rpc.codec.sofahessian;
+package com.alipay.sofa.rpc.codec.common;
-import com.alipay.sofa.rpc.common.SofaConfigs;
-import com.alipay.sofa.rpc.common.SofaOptions;
+import com.alipay.sofa.common.config.SofaConfigs;
import com.alipay.sofa.rpc.common.utils.StringUtils;
import com.alipay.sofa.rpc.log.Logger;
import com.alipay.sofa.rpc.log.LoggerFactory;
@@ -30,6 +29,8 @@
import java.util.LinkedList;
import java.util.List;
+import static com.alipay.sofa.rpc.common.config.RpcConfigKeys.SERIALIZE_BLACKLIST_OVERRIDE;
+import static com.alipay.sofa.rpc.common.config.RpcConfigKeys.SERIALIZE_WHITELIST_OVERRIDE;
import static com.alipay.sofa.rpc.common.utils.IOUtils.closeQuietly;
/**
@@ -37,21 +38,24 @@
*
* @author GengZhang
*/
-public class BlackListFileLoader {
+public class BlackAndWhiteListFileLoader {
- private static final Logger LOGGER = LoggerFactory.getLogger(BlackListFileLoader.class);
+ private static final Logger LOGGER = LoggerFactory
+ .getLogger(BlackAndWhiteListFileLoader.class);
- public static final List SOFA_SERIALIZE_BLACK_LIST = loadFile("/sofa-rpc/serialize_blacklist.txt");
+ public static final List SOFA_SERIALIZE_BLACK_LIST = loadBlackListFile("/sofa-rpc/serialize_blacklist.txt");
- static List loadFile(String path) {
- List blackPrefixList = new ArrayList();
+ public static final List SOFA_SERIALIZER_WHITE_LIST = loadWhiteListFile("/sofa-rpc/serialize_whitelist.txt");
+
+ public static List loadBlackListFile(String path) {
+ List blackPrefixList = new ArrayList<>();
InputStream input = null;
try {
- input = BlackListFileLoader.class.getResourceAsStream(path);
+ input = BlackAndWhiteListFileLoader.class.getResourceAsStream(path);
if (input != null) {
readToList(input, "UTF-8", blackPrefixList);
}
- String overStr = SofaConfigs.getStringValue(SofaOptions.CONFIG_SERIALIZE_BLACKLIST_OVERRIDE, "");
+ String overStr = SofaConfigs.getOrCustomDefault(SERIALIZE_BLACKLIST_OVERRIDE, "");
if (StringUtils.isNotBlank(overStr)) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Serialize blacklist will override with configuration: {}", overStr);
@@ -68,6 +72,31 @@ static List loadFile(String path) {
return blackPrefixList;
}
+ public static List loadWhiteListFile(String path) {
+ List whitePrefixList = new ArrayList<>();
+ InputStream input = null;
+ try {
+ input = BlackAndWhiteListFileLoader.class.getResourceAsStream(path);
+ if (input != null) {
+ readToList(input, "UTF-8", whitePrefixList);
+ }
+ String overStr = SofaConfigs.getOrCustomDefault(SERIALIZE_WHITELIST_OVERRIDE, "");
+ if (StringUtils.isNotBlank(overStr)) {
+ if (LOGGER.isInfoEnabled()) {
+ LOGGER.info("Serialize whitelist will override with configuration: {}", overStr);
+ }
+ overrideWhiteList(whitePrefixList, overStr);
+ }
+ } catch (Exception e) {
+ if (LOGGER.isErrorEnabled()) {
+ LOGGER.error(e.getMessage(), e);
+ }
+ } finally {
+ closeQuietly(input);
+ }
+ return whitePrefixList;
+ }
+
/**
* 读文件,将结果丢入List
*
@@ -100,12 +129,12 @@ private static void readToList(InputStream input, String encoding, List
/**
* Override blacklist with override string.
- *
- * @param originList Origin black list
+ *
+ * @param originList Origin black list
* @param overrideStr The override string
*/
- static void overrideBlackList(List originList, String overrideStr) {
- List adds = new LinkedList();
+ public static void overrideBlackList(List originList, String overrideStr) {
+ List adds = new LinkedList<>();
String[] overrideItems = StringUtils.splitWithCommaOrSemicolon(overrideStr);
for (String overrideItem : overrideItems) {
if (StringUtils.isNotBlank(overrideItem)) {
@@ -127,4 +156,19 @@ static void overrideBlackList(List originList, String overrideStr) {
originList.addAll(adds);
}
}
+
+ public static void overrideWhiteList(List originList, String overrideStr) {
+ List adds = new LinkedList<>();
+ String[] overrideItems = StringUtils.splitWithCommaOrSemicolon(overrideStr);
+ for (String overrideItem : overrideItems) {
+ if (StringUtils.isNotBlank(overrideItem)) {
+ if (!originList.contains(overrideItem)) {
+ adds.add(overrideItem);
+ }
+ }
+ }
+ if (adds.size() > 0) {
+ originList.addAll(adds);
+ }
+ }
}
diff --git a/codec/codec-api/src/main/java/com/alipay/sofa/rpc/codec/common/SerializeCheckStatus.java b/codec/codec-api/src/main/java/com/alipay/sofa/rpc/codec/common/SerializeCheckStatus.java
new file mode 100644
index 000000000..bcedf2101
--- /dev/null
+++ b/codec/codec-api/src/main/java/com/alipay/sofa/rpc/codec/common/SerializeCheckStatus.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.codec.common;
+
+/**
+ * @author Even
+ * @date 2024/1/5 19:12
+ */
+public enum SerializeCheckStatus {
+ /**
+ * Disable serialize check for all classes
+ */
+ DISABLE(0),
+
+ /**
+ * Only deny danger classes, warn if other classes are not in allow list
+ */
+ WARN(1),
+
+ /**
+ * Only allow classes in allow list, deny if other classes are not in allow list
+ */
+ STRICT(2);
+
+ private final int mode;
+
+ SerializeCheckStatus(int mode) {
+ this.mode = mode;
+ }
+
+ public int getSerializeCheckMode() {
+ return mode;
+ }
+}
diff --git a/codec/codec-api/src/main/java/com/alipay/sofa/rpc/codec/gzip/GzipRpcCompressor.java b/codec/codec-api/src/main/java/com/alipay/sofa/rpc/codec/gzip/GzipRpcCompressor.java
new file mode 100644
index 000000000..5f68209db
--- /dev/null
+++ b/codec/codec-api/src/main/java/com/alipay/sofa/rpc/codec/gzip/GzipRpcCompressor.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.codec.gzip;
+
+import com.alipay.sofa.rpc.codec.Compressor;
+import com.alipay.sofa.rpc.ext.Extension;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+/**
+ * @author chengming
+ * @version GzipRpcCompressor.java, v 0.1 2024年02月28日 11:25 AM chengming
+ */
+@Extension(value = "gzip", code = 4)
+public class GzipRpcCompressor implements Compressor {
+
+ @Override
+ public byte[] compress(byte[] src) {
+ if (null == src || 0 == src.length) {
+ return new byte[0];
+ }
+
+ ByteArrayOutputStream byteOutStream = new ByteArrayOutputStream();
+ try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteOutStream)) {
+ gzipOutputStream.write(src);
+ } catch (Exception exception) {
+ throw new IllegalStateException(exception);
+ }
+
+ return byteOutStream.toByteArray();
+ }
+
+ @Override
+ public byte[] deCompress(byte[] src) {
+ if (null == src || 0 == src.length) {
+ return new byte[0];
+ }
+
+ ByteArrayInputStream byteInStream = new ByteArrayInputStream(src);
+ ByteArrayOutputStream byteOutStream = new ByteArrayOutputStream();
+ try (GZIPInputStream gzipInputStream = new GZIPInputStream(byteInStream)) {
+ int readByteNum;
+ byte[] bufferArr = new byte[256];
+ while ((readByteNum = gzipInputStream.read(bufferArr)) >= 0) {
+ byteOutStream.write(bufferArr, 0, readByteNum);
+ }
+ } catch (Exception exception) {
+ throw new IllegalStateException(exception);
+ }
+
+ return byteOutStream.toByteArray();
+ }
+}
\ No newline at end of file
diff --git a/codec/codec-api/src/main/resources/META-INF/services/sofa-rpc/com.alipay.sofa.rpc.codec.Compressor b/codec/codec-api/src/main/resources/META-INF/services/sofa-rpc/com.alipay.sofa.rpc.codec.Compressor
index a0d34faa1..84b01a049 100644
--- a/codec/codec-api/src/main/resources/META-INF/services/sofa-rpc/com.alipay.sofa.rpc.codec.Compressor
+++ b/codec/codec-api/src/main/resources/META-INF/services/sofa-rpc/com.alipay.sofa.rpc.codec.Compressor
@@ -1 +1,3 @@
-snappy=com.alipay.sofa.rpc.codec.snappy.SnappyRpcCompressor
\ No newline at end of file
+snappy=com.alipay.sofa.rpc.codec.snappy.SnappyRpcCompressor
+bzip2=com.alipay.sofa.rpc.codec.bzip2.Bzip2RpcCompressor
+gzip=com.alipay.sofa.rpc.codec.gzip.GzipRpcCompressor
\ No newline at end of file
diff --git a/codec/codec-api/src/main/resources/sofa-rpc/serialize_blacklist.txt b/codec/codec-api/src/main/resources/sofa-rpc/serialize_blacklist.txt
new file mode 100644
index 000000000..413a5045c
--- /dev/null
+++ b/codec/codec-api/src/main/resources/sofa-rpc/serialize_blacklist.txt
@@ -0,0 +1,172 @@
+aj.org.objectweb.asm.
+br.com.anteros.
+bsh.
+ch.qos.logback.
+clojure.
+com.alibaba.citrus.springext.support.parser.
+com.alibaba.citrus.springext.util.SpringExtUtil.
+com.alibaba.druid.pool.
+com.alibaba.druid.stat.JdbcDataSourceStat
+com.alibaba.fastjson.annotation.
+com.alibaba.hotcode.internal.org.apache.commons.collections.functors.
+com.alipay.custrelation.service.model.redress.
+com.alipay.oceanbase.obproxy.druid.pool.
+com.caucho.hessian.test.TestCons
+com.caucho.naming.Qname
+com.ibatis.
+com.ibm.jtc.jax.xml.bind.v2.runtime.unmarshaller.
+com.ibm.xltxe.rnm1.xtq.bcel.util.
+com.mchange.
+com.mysql.cj.jdbc.admin.
+com.mysql.cj.jdbc.MysqlConnectionPoolDataSource
+com.mysql.cj.jdbc.MysqlDataSource
+com.mysql.cj.jdbc.MysqlXADataSource
+com.mysql.cj.log.
+com.mysql.jdbc.util.
+com.p6spy.engine.
+com.rometools.rome.feed.
+com.sun.
+com.taobao.eagleeye.wrapper.
+com.taobao.vipserver.commons.collections.functors.
+com.zaxxer.hikari.
+flex.messaging.util.concurrent.
+groovy.lang.
+java.awt.
+java.beans.
+java.net.InetAddress
+java.net.Socket
+java.net.URL
+java.rmi.
+java.security.
+java.util.EventListener
+java.util.jar.
+java.util.logging.
+java.util.prefs.
+java.util.ServiceLoader
+java.util.StringTokenizer
+javassist.
+javax.activation.
+javax.imageio.
+javax.management.
+javax.media.jai.remote.
+javax.naming.
+javax.net.
+javax.print.
+javax.script.
+javax.sound.
+javax.swing.
+javax.tools.
+javax.xml
+jdk.internal.
+jodd.db.connection.
+junit.
+net.bytebuddy.dynamic.loading.
+net.sf.cglib.
+net.sf.ehcache.hibernate.
+net.sf.ehcache.transaction.manager.
+ognl.
+oracle.jdbc.
+oracle.jms.aq.
+oracle.net.
+org.aoju.bus.proxy.provider.
+org.apache.activemq.ActiveMQConnectionFactory
+org.apache.activemq.ActiveMQXAConnectionFactory
+org.apache.activemq.jms.pool.
+org.apache.activemq.pool.
+org.apache.activemq.spring.
+org.apache.aries.transaction.
+org.apache.axis2.jaxws.spi.handler.
+org.apache.axis2.transport.jms.
+org.apache.bcel.
+org.apache.carbondata.core.scan.expression.
+org.apache.catalina.
+org.apache.cocoon.
+org.apache.commons.beanutils.
+org.apache.commons.codec.
+org.apache.commons.collections.comparators.
+org.apache.commons.collections.functors.
+org.apache.commons.collections.Transformer
+org.apache.commons.collections4.comparators.
+org.apache.commons.collections4.functors.
+org.apache.commons.collections4.Transformer
+org.apache.commons.configuration.
+org.apache.commons.configuration2.
+org.apache.commons.dbcp.
+org.apache.commons.fileupload.
+org.apache.commons.jelly.
+org.apache.commons.logging.
+org.apache.commons.proxy.
+org.apache.cxf.jaxrs.provider.
+org.apache.hadoop.shaded.com.zaxxer.hikari.
+org.apache.http.auth.
+org.apache.http.conn.
+org.apache.http.cookie.
+org.apache.http.impl.
+org.apache.ibatis.datasource.
+org.apache.ibatis.executor.
+org.apache.ibatis.javassist.
+org.apache.ibatis.ognl.
+org.apache.ibatis.parsing.
+org.apache.ibatis.reflection.
+org.apache.ibatis.scripting.
+org.apache.ignite.cache.
+org.apache.ignite.cache.jta.
+org.apache.log.output.db.
+org.apache.log4j.
+org.apache.logging.
+org.apache.myfaces.context.servlet.
+org.apache.myfaces.view.facelets.el.
+org.apache.openjpa.ee.
+org.apache.shiro.
+org.apache.tomcat.
+org.apache.velocity.
+org.apache.wicket.util.
+org.apache.xalan.
+org.apache.xbean.
+org.apache.xpath.
+org.apache.zookeeper.
+org.aspectj.
+org.codehaus.groovy.runtime.
+org.codehaus.jackson.
+org.datanucleus.store.rdbms.datasource.dbcp.datasources.
+org.dom4j.
+org.eclipse.jetty.
+org.geotools.filter.
+org.h2.jdbcx.
+org.h2.server.
+org.h2.value.
+org.hibernate.
+org.javasimon.
+org.jaxen.
+org.jboss.
+org.jdom.
+org.jdom2.transform.
+org.junit.
+org.logicalcobwebs.
+org.mockito.
+org.mortbay.jetty.
+org.mortbay.log.
+org.mozilla.javascript.
+org.objectweb.asm.
+org.osjava.sj.
+org.python.core.
+org.quartz.
+org.slf4j.
+org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator$PartiallyComparableAdvisorHolder
+org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor
+org.springframework.beans.factory.BeanFactory
+org.springframework.beans.factory.config.PropertyPathFactoryBean
+org.springframework.beans.factory.support.DefaultListableBeanFactory
+org.springframework.jndi.support.SimpleJndiBeanFactory
+org.springframework.orm.jpa.AbstractEntityManagerFactoryBean
+org.springframework.transaction.jta.JtaTransactionManager
+org.springframework.jndi.JndiObjectTargetSource
+org.springframework.beans.factory.config.MethodInvokingFactoryBean
+org.thymeleaf.
+org.yaml.snakeyaml.tokens.
+pstore.shaded.org.apache.commons.collections.
+sun.print.
+sun.rmi.server.
+sun.rmi.transport.
+weblogic.ejb20.internal.
+weblogic.jms.common.
\ No newline at end of file
diff --git a/codec/codec-api/src/test/java/com/alipay/sofa/rpc/codec/bzip2/Bzip2RpcCompressorTest.java b/codec/codec-api/src/test/java/com/alipay/sofa/rpc/codec/bzip2/Bzip2RpcCompressorTest.java
new file mode 100644
index 000000000..c82ebc2a1
--- /dev/null
+++ b/codec/codec-api/src/test/java/com/alipay/sofa/rpc/codec/bzip2/Bzip2RpcCompressorTest.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.codec.bzip2;
+
+import com.alipay.sofa.rpc.codec.Compressor;
+import com.alipay.sofa.rpc.ext.ExtensionLoaderFactory;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * @author chengming
+ * @version Bzip2RpcCompressorTest.java, v 0.1 2024年02月28日 2:19 PM chengming
+ */
+public class Bzip2RpcCompressorTest {
+
+ private static final String TEST_STR;
+
+ static {
+ StringBuilder builder = new StringBuilder();
+ int charNum = 1000000;
+ for (int i = 0; i < charNum; i++) {
+ builder.append("a");
+ }
+
+ TEST_STR = builder.toString();
+ }
+
+ @Test
+ public void testCompression() throws UnsupportedEncodingException {
+ Compressor compressor = ExtensionLoaderFactory.getExtensionLoader(Compressor.class).getExtension("bzip2");
+ Assert.assertTrue(compressor instanceof Bzip2RpcCompressor);
+
+ byte[] bs = compressor.compress(TEST_STR.getBytes("utf-8"));
+ String s1 = new String(compressor.deCompress(bs), "utf-8");
+ Assert.assertEquals(TEST_STR, s1);
+ }
+}
\ No newline at end of file
diff --git a/codec/codec-api/src/test/java/com/alipay/sofa/rpc/codec/gzip/GzipRpcCompressorTest.java b/codec/codec-api/src/test/java/com/alipay/sofa/rpc/codec/gzip/GzipRpcCompressorTest.java
new file mode 100644
index 000000000..694b14e78
--- /dev/null
+++ b/codec/codec-api/src/test/java/com/alipay/sofa/rpc/codec/gzip/GzipRpcCompressorTest.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.codec.gzip;
+
+import com.alipay.sofa.rpc.codec.Compressor;
+import com.alipay.sofa.rpc.ext.ExtensionLoaderFactory;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * @author chengming
+ * @version GzipRpcCompressorTest.java, v 0.1 2024年02月28日 2:09 PM chengming
+ */
+public class GzipRpcCompressorTest {
+
+ private static final String TEST_STR;
+
+ static {
+ StringBuilder builder = new StringBuilder();
+ int charNum = 1000000;
+ for (int i = 0; i < charNum; i++) {
+ builder.append("a");
+ }
+
+ TEST_STR = builder.toString();
+ }
+
+ @Test
+ public void testCompression() throws UnsupportedEncodingException {
+ Compressor compressor = ExtensionLoaderFactory.getExtensionLoader(Compressor.class).getExtension("gzip");
+ Assert.assertTrue(compressor instanceof GzipRpcCompressor);
+
+ byte[] bs = compressor.compress(TEST_STR.getBytes("utf-8"));
+ String s1 = new String(compressor.deCompress(bs), "utf-8");
+ Assert.assertEquals(TEST_STR, s1);
+ }
+
+}
diff --git a/codec/codec-jackson/src/main/java/com/alipay/sofa/rpc/codec/jackson/JacksonConfigKeys.java b/codec/codec-jackson/src/main/java/com/alipay/sofa/rpc/codec/jackson/JacksonConfigKeys.java
new file mode 100644
index 000000000..4ecd3e836
--- /dev/null
+++ b/codec/codec-jackson/src/main/java/com/alipay/sofa/rpc/codec/jackson/JacksonConfigKeys.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.codec.jackson;
+
+import com.alipay.sofa.common.config.ConfigKey;
+
+/**
+ *
+ * @author junyuan
+ * @version JacksonConfigKeys.java, v 0.1 2024-01-15 16:46 junyuan Exp $
+ */
+public class JacksonConfigKeys {
+ public static ConfigKey JACKSON_SER_FEATURE_ENABLE_LIST = ConfigKey
+ .build(
+ "sofa.rpc.codec.jackson.serialize.feature.enable.list",
+ "",
+ false,
+ "serialize feature to enable",
+ new String[] { "sofa_rpc_codec_jackson_serialize_feature_enable_list" });
+
+ public static ConfigKey JACKSON_SER_FEATURE_DISABLE_LIST = ConfigKey
+ .build(
+ "sofa.rpc.codec.jackson.serialize.feature.disable.list",
+ "",
+ false,
+ "serialize feature to disable",
+ new String[] { "sofa_rpc_codec_jackson_serialize_feature_disable_list" });
+
+ public static ConfigKey JACKSON_DES_FEATURE_ENABLE_LIST = ConfigKey
+ .build(
+ "sofa.rpc.codec.jackson.deserialize.feature.enable.list",
+ "",
+ false,
+ "deserialize feature to enable",
+ new String[] { "sofa_rpc_codec_jackson_deserialize_feature_disable_list" });
+
+ public static ConfigKey JACKSON_DES_FEATURE_DISABLE_LIST = ConfigKey
+ .build(
+ "sofa.rpc.codec.jackson.deserialize.feature.disable.list",
+ "",
+ false,
+ "deserialize feature to disable",
+ new String[] { "sofa_rpc_codec_jackson_deserialize_feature_disable_list" });
+}
\ No newline at end of file
diff --git a/codec/codec-jackson/src/main/java/com/alipay/sofa/rpc/codec/jackson/JacksonHelper.java b/codec/codec-jackson/src/main/java/com/alipay/sofa/rpc/codec/jackson/JacksonHelper.java
index b764a31e9..0078f7e36 100644
--- a/codec/codec-jackson/src/main/java/com/alipay/sofa/rpc/codec/jackson/JacksonHelper.java
+++ b/codec/codec-jackson/src/main/java/com/alipay/sofa/rpc/codec/jackson/JacksonHelper.java
@@ -18,6 +18,7 @@
import java.lang.reflect.Method;
import java.lang.reflect.Type;
+import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.alipay.sofa.rpc.common.utils.ClassUtils;
@@ -27,6 +28,8 @@
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
+import static com.alipay.sofa.rpc.common.utils.ClassLoaderUtils.getCurrentClassLoader;
+
/**
* @author zhiyuan.lzy
*/
@@ -37,12 +40,12 @@ public class JacksonHelper {
/**
* Request service and method cache {service+method:class}
*/
- private ConcurrentHashMap requestClassCache = new ConcurrentHashMap();
+ private ConcurrentHashMap> requestClassCache = new ConcurrentHashMap<>();
/**
* Response service and method cache {service+method:class}
*/
- private ConcurrentHashMap responseClassCache = new ConcurrentHashMap();
+ private ConcurrentHashMap> responseClassCache = new ConcurrentHashMap<>();
/**
* Fetch request class for cache according service and method
@@ -54,14 +57,30 @@ public class JacksonHelper {
public JavaType[] getReqClass(String service, String methodName) {
String key = buildMethodKey(service, methodName);
- Type[] reqClassList = requestClassCache.get(key);
+ Type[] reqClassList = getRequestCache().get(key);
if (reqClassList == null) {
//read interface and method from cache
String interfaceClass = ConfigUniqueNameGenerator.getInterfaceName(service);
Class clazz = ClassUtils.forName(interfaceClass, true);
loadClassToCache(key, clazz, methodName);
}
- return requestClassCache.get(key);
+ return getRequestCache().get(key);
+ }
+
+ private Map getRequestCache() {
+ return requestClassCache.computeIfAbsent(getCurrentClassLoader(), k -> new ConcurrentHashMap<>());
+ }
+
+ private Map getResponseCache() {
+ return responseClassCache.computeIfAbsent(getCurrentClassLoader(), k -> new ConcurrentHashMap<>());
+ }
+
+ public void clearCache(ClassLoader classLoader) {
+ if (classLoader == null) {
+ return;
+ }
+ requestClassCache.remove(classLoader);
+ responseClassCache.remove(classLoader);
}
/**
@@ -73,14 +92,14 @@ public JavaType[] getReqClass(String service, String methodName) {
*/
public JavaType getResClass(String service, String methodName) {
String key = service + "#" + methodName;
- JavaType reqType = responseClassCache.get(key);
+ JavaType reqType = getResponseCache().get(key);
if (reqType == null) {
// 读取接口里的方法参数和返回值
String interfaceClass = ConfigUniqueNameGenerator.getInterfaceName(service);
Class clazz = ClassUtils.forName(interfaceClass, true);
loadClassToCache(key, clazz, methodName);
}
- return responseClassCache.get(key);
+ return getResponseCache().get(key);
}
/**
@@ -122,7 +141,7 @@ private void loadClassToCache(String key, Class clazz, String methodName) {
JavaType javaType = mapper.getTypeFactory().constructType(parameterTypes[i]);
javaTypes[i] = javaType;
}
- requestClassCache.put(key, javaTypes);
+ getRequestCache().put(key, javaTypes);
// parse response types
Type resType = jsonMethod.getGenericReturnType();
@@ -130,6 +149,6 @@ private void loadClassToCache(String key, Class clazz, String methodName) {
throw new SofaRpcRuntimeException(LogCodes.getLog(LogCodes.ERROR_VOID_RETURN, "jackson", clazz.getName()));
}
JavaType resJavaType = mapper.getTypeFactory().constructType(resType);
- responseClassCache.put(key, resJavaType);
+ getResponseCache().put(key, resJavaType);
}
}
diff --git a/codec/codec-jackson/src/main/java/com/alipay/sofa/rpc/codec/jackson/JacksonSerializer.java b/codec/codec-jackson/src/main/java/com/alipay/sofa/rpc/codec/jackson/JacksonSerializer.java
index db7e593df..1f9ded9ff 100644
--- a/codec/codec-jackson/src/main/java/com/alipay/sofa/rpc/codec/jackson/JacksonSerializer.java
+++ b/codec/codec-jackson/src/main/java/com/alipay/sofa/rpc/codec/jackson/JacksonSerializer.java
@@ -16,8 +16,11 @@
*/
package com.alipay.sofa.rpc.codec.jackson;
+import com.alipay.sofa.common.config.ConfigKey;
+import com.alipay.sofa.common.config.SofaConfigs;
import com.alipay.sofa.rpc.codec.AbstractSerializer;
import com.alipay.sofa.rpc.common.RemotingConstants;
+import com.alipay.sofa.rpc.common.annotation.VisibleForTesting;
import com.alipay.sofa.rpc.common.utils.CodecUtils;
import com.alipay.sofa.rpc.common.utils.StringUtils;
import com.alipay.sofa.rpc.config.ConfigUniqueNameGenerator;
@@ -37,10 +40,13 @@
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
+import java.util.Set;
+import java.util.stream.Collectors;
/**
* Json serializer.
@@ -64,39 +70,96 @@
@Extension(value = "json", code = 12)
public class JacksonSerializer extends AbstractSerializer {
- private ObjectMapper mapper = new ObjectMapper();
+ private ObjectMapper mapper = new ObjectMapper();
- private JacksonHelper jacksonHelper = new JacksonHelper();
+ private JacksonHelper jacksonHelper = new JacksonHelper();
- private static final String DESERIALIZATIONFEATURE_PREFIX = "sofa.rpc.codec.jackson.DeserializationFeature.";
+ @Deprecated
+ private static final String DESERIALIZATION_FEATURE_PREFIX = "sofa.rpc.codec.jackson.DeserializationFeature.";
- private static final String SERIALIZATIONFEATURE_PREFIX = "sofa.rpc.codec.jackson.SerializationFeature.";
+ @Deprecated
+ private static final String SERIALIZATION_FEATURE_PREFIX = "sofa.rpc.codec.jackson.SerializationFeature.";
+ /**
+ * two ways to config:
+ *
+ *
the new config read from env or vm args sofa.rpc.codec.jackson.serialize.feature.enable.list=FAIL_ON_EMPTY_BEANS,FAIL_ON_NULL_FOR_PRIMITIVES
+ *
the deprecated config sofa.rpc.codec.jackson.DeserializationFeature.FAIL_ON_EMPTY_BEANS=true
+ *
+ */
public JacksonSerializer() {
+ Set serFeatures = Arrays.stream(SerializationFeature.values()).map(Enum::name).collect(Collectors.toSet());
+ Set desFeatures = Arrays.stream(DeserializationFeature.values()).map(Enum::name).collect(Collectors.toSet());
+
Properties properties = System.getProperties();
for (String key : properties.stringPropertyNames()) {
- if (key.startsWith(DESERIALIZATIONFEATURE_PREFIX)) {
- String enumName = StringUtils.substringAfter(key, DESERIALIZATIONFEATURE_PREFIX);
- for (DeserializationFeature df : DeserializationFeature.values()) {
- if (df.name().equals(enumName)) {
- boolean state = Boolean.parseBoolean(properties.getProperty(key));
- mapper.configure(df, state);
- break;
- }
+ if (key.startsWith(DESERIALIZATION_FEATURE_PREFIX)) {
+ String enumName = StringUtils.substringAfter(key, DESERIALIZATION_FEATURE_PREFIX);
+ if (desFeatures.contains(enumName)) {
+ boolean state = Boolean.parseBoolean(properties.getProperty(key));
+ mapper.configure(DeserializationFeature.valueOf(enumName), state);
}
}
- if (key.startsWith(SERIALIZATIONFEATURE_PREFIX)) {
- String enumName = StringUtils.substringAfter(key, SERIALIZATIONFEATURE_PREFIX);
- for (SerializationFeature sf : SerializationFeature.values()) {
- if (sf.name().equals(enumName)) {
- boolean state = Boolean.parseBoolean(properties.getProperty(key));
- mapper.configure(sf, state);
- break;
- }
+ if (key.startsWith(SERIALIZATION_FEATURE_PREFIX)) {
+ String enumName = StringUtils.substringAfter(key, SERIALIZATION_FEATURE_PREFIX);
+ if (serFeatures.contains(enumName)) {
+ boolean state = Boolean.parseBoolean(properties.getProperty(key));
+ mapper.configure(SerializationFeature.valueOf(enumName), state);
}
}
}
+
+ // 允许通过 sofa config 获取的配置覆盖上述环境变量配置
+ processJacksonSerFeature(mapper, serFeatures, desFeatures);
+ }
+
+ /**
+ *
+ * @param objectMapper
+ * @param serFeatures
+ * @param desFeatures
+ */
+ protected void processJacksonSerFeature(ObjectMapper objectMapper, Set serFeatures, Set desFeatures) {
+ processSerializeFeatures(objectMapper, JacksonConfigKeys.JACKSON_SER_FEATURE_ENABLE_LIST, true, serFeatures);
+ processSerializeFeatures(objectMapper, JacksonConfigKeys.JACKSON_SER_FEATURE_DISABLE_LIST, false, serFeatures);
+
+ processDeserializeFeatures(objectMapper, JacksonConfigKeys.JACKSON_DES_FEATURE_ENABLE_LIST, true, desFeatures);
+ processDeserializeFeatures(objectMapper, JacksonConfigKeys.JACKSON_DES_FEATURE_DISABLE_LIST, false, desFeatures);
+ }
+
+ /**
+ * 获取用户配置的 feature, 应该是逗号分割的 string
+ * 根据 String 获取对应的 SerializationFeature, 并配置到 object mapper
+ *
+ * @param objectMapper
+ * @param key
+ * @param enable
+ * @param featureSet
+ */
+ private void processSerializeFeatures(ObjectMapper objectMapper, ConfigKey key, boolean enable, Set featureSet) {
+ String serFeatureList = SofaConfigs.getOrDefault(key);
+ if (StringUtils.isBlank(serFeatureList)) {
+ return;
+ }
+ Arrays.stream(serFeatureList.split(",")).filter(featureSet::contains).forEach(str -> objectMapper.configure(SerializationFeature.valueOf(str), enable));
+ }
+
+ /**
+ * 获取用户配置的 feature, 应该是逗号分割的 string
+ * 根据 String 获取对应的 DeserializationFeature, 并配置到 object mapper
+ *
+ * @param objectMapper
+ * @param key
+ * @param enable
+ * @param featureSet
+ */
+ private void processDeserializeFeatures(ObjectMapper objectMapper,ConfigKey key, boolean enable, Set featureSet) {
+ String desFeatureList = SofaConfigs.getOrDefault(key);
+ if (StringUtils.isBlank(desFeatureList)) {
+ return;
+ }
+ Arrays.stream(desFeatureList.split(",")).filter(featureSet::contains).forEach(str -> objectMapper.configure(DeserializationFeature.valueOf(str), enable));
}
@Override
@@ -326,4 +389,13 @@ private void decodeSofaResponse(AbstractByteBuf data, SofaResponse sofaResponse,
sofaResponse.setAppResponse(result);
}
}
+
+ @VisibleForTesting
+ protected ObjectMapper getMapper() {
+ return this.mapper;
+ }
+
+ public void clearCache(ClassLoader classLoader) {
+ jacksonHelper.clearCache(classLoader);
+ }
}
diff --git a/codec/codec-jackson/src/test/java/com/alipay/sofa/rpc/codec/jackson/JacksonSerializerTest.java b/codec/codec-jackson/src/test/java/com/alipay/sofa/rpc/codec/jackson/JacksonSerializerTest.java
index eb4f84992..be218792a 100644
--- a/codec/codec-jackson/src/test/java/com/alipay/sofa/rpc/codec/jackson/JacksonSerializerTest.java
+++ b/codec/codec-jackson/src/test/java/com/alipay/sofa/rpc/codec/jackson/JacksonSerializerTest.java
@@ -33,7 +33,9 @@
import com.alipay.sofa.rpc.core.response.SofaResponse;
import com.alipay.sofa.rpc.transport.AbstractByteBuf;
import com.alipay.sofa.rpc.transport.ByteArrayWrapperByteBuf;
+import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
import org.junit.Assert;
import org.junit.Test;
@@ -504,6 +506,70 @@ public void testJacksonFeature() throws UnsupportedEncodingException {
}
+ @Test
+ public void testJacksonFeatureByConfigKey() {
+ // origin, should be FAIL_ON_UNKNOWN_PROPERTIES ture and FAIL_ON_EMPTY_BEANS true
+ JacksonSerializer origin = new JacksonSerializer();
+ ObjectMapper originMapper = origin.getMapper();
+ // originally false
+ Assert.assertFalse(originMapper.isEnabled(SerializationFeature.WRAP_ROOT_VALUE));
+ Assert.assertFalse(originMapper.isEnabled(SerializationFeature.INDENT_OUTPUT));
+
+ // originally true
+ Assert.assertTrue(originMapper.isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS));
+ Assert.assertTrue(originMapper.isEnabled(SerializationFeature.FAIL_ON_SELF_REFERENCES));
+
+ // originally false
+ Assert.assertFalse(originMapper.isEnabled(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES));
+ Assert.assertFalse(originMapper.isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS));
+
+ // originally true
+ Assert.assertTrue(originMapper.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
+ Assert.assertTrue(originMapper.isEnabled(DeserializationFeature.FAIL_ON_UNRESOLVED_OBJECT_IDS));
+
+ JacksonSerializer testSer = null;
+ try {
+ System.setProperty(JacksonConfigKeys.JACKSON_SER_FEATURE_ENABLE_LIST.getKey(),
+ SerializationFeature.WRAP_ROOT_VALUE + ","
+ + SerializationFeature.INDENT_OUTPUT);
+ System.setProperty(JacksonConfigKeys.JACKSON_SER_FEATURE_DISABLE_LIST.getKey(),
+ SerializationFeature.FAIL_ON_EMPTY_BEANS.name() + ","
+ + SerializationFeature.FAIL_ON_SELF_REFERENCES.name());
+ System.setProperty(JacksonConfigKeys.JACKSON_DES_FEATURE_ENABLE_LIST.getKey(),
+ DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES + ","
+ + DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS);
+ System.setProperty(JacksonConfigKeys.JACKSON_DES_FEATURE_DISABLE_LIST.getKey(),
+ DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES.name() + ","
+ + DeserializationFeature.FAIL_ON_UNRESOLVED_OBJECT_IDS.name());
+ // test ser after property set
+ testSer = new JacksonSerializer();
+
+ } finally {
+ System.clearProperty(JacksonConfigKeys.JACKSON_SER_FEATURE_ENABLE_LIST.getKey());
+ System.clearProperty(JacksonConfigKeys.JACKSON_SER_FEATURE_DISABLE_LIST.getKey());
+ System.clearProperty(JacksonConfigKeys.JACKSON_DES_FEATURE_ENABLE_LIST.getKey());
+ System.clearProperty(JacksonConfigKeys.JACKSON_DES_FEATURE_DISABLE_LIST.getKey());
+ }
+
+ ObjectMapper testMapper = testSer.getMapper();
+
+ // originally false, but enabled
+ Assert.assertTrue(testMapper.isEnabled(SerializationFeature.WRAP_ROOT_VALUE));
+ Assert.assertTrue(testMapper.isEnabled(SerializationFeature.INDENT_OUTPUT));
+
+ // originally true, but disabled
+ Assert.assertFalse(testMapper.isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS));
+ Assert.assertFalse(testMapper.isEnabled(SerializationFeature.FAIL_ON_SELF_REFERENCES));
+
+ // originally false, but enabled
+ Assert.assertTrue(testMapper.isEnabled(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES));
+ Assert.assertTrue(testMapper.isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS));
+
+ // originally true, but disabled
+ Assert.assertFalse(testMapper.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
+ Assert.assertFalse(testMapper.isEnabled(DeserializationFeature.FAIL_ON_UNRESOLVED_OBJECT_IDS));
+ }
+
@Test(expected = ClassCastException.class)
public void testGenericSofaRequestFail() throws Exception {
AbstractSerializer.clear();
diff --git a/codec/codec-sofa-fury/pom.xml b/codec/codec-sofa-fury/pom.xml
new file mode 100644
index 000000000..70150a8f9
--- /dev/null
+++ b/codec/codec-sofa-fury/pom.xml
@@ -0,0 +1,46 @@
+
+
+ 4.0.0
+
+
+ com.alipay.sofa
+ sofa-rpc-codec
+ ${revision}
+
+
+ sofa-rpc-codec-sofa-fury
+
+
+
+ com.alipay.sofa
+ sofa-rpc-codec-api
+
+
+ com.alipay.sofa
+ sofa-rpc-api
+
+
+ com.alipay.sofa
+ sofa-rpc-log
+
+
+
+
+ org.furyio
+ fury-core
+
+
+
+ org.slf4j
+ slf4j-log4j12
+ test
+
+
+ junit
+ junit
+ test
+
+
+
diff --git a/codec/codec-sofa-fury/src/main/java/com/alipay/sofa/rpc/codec/fury/FurySerializer.java b/codec/codec-sofa-fury/src/main/java/com/alipay/sofa/rpc/codec/fury/FurySerializer.java
new file mode 100644
index 000000000..3f8980623
--- /dev/null
+++ b/codec/codec-sofa-fury/src/main/java/com/alipay/sofa/rpc/codec/fury/FurySerializer.java
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.codec.fury;
+
+import com.alipay.sofa.common.config.SofaConfigs;
+import com.alipay.sofa.rpc.codec.AbstractSerializer;
+import com.alipay.sofa.rpc.codec.CustomSerializer;
+import com.alipay.sofa.rpc.codec.common.BlackAndWhiteListFileLoader;
+import com.alipay.sofa.rpc.codec.common.SerializeCheckStatus;
+import com.alipay.sofa.rpc.codec.fury.serialize.SofaRequestFurySerializer;
+import com.alipay.sofa.rpc.codec.fury.serialize.SofaResponseFurySerializer;
+import com.alipay.sofa.rpc.common.config.RpcConfigKeys;
+import com.alipay.sofa.rpc.core.exception.SofaRpcException;
+import com.alipay.sofa.rpc.core.request.SofaRequest;
+import com.alipay.sofa.rpc.core.response.SofaResponse;
+import com.alipay.sofa.rpc.ext.Extension;
+import com.alipay.sofa.rpc.transport.AbstractByteBuf;
+import com.alipay.sofa.rpc.transport.ByteArrayWrapperByteBuf;
+import io.fury.Fury;
+import io.fury.ThreadLocalFury;
+import io.fury.ThreadSafeFury;
+import io.fury.config.Language;
+import io.fury.memory.MemoryBuffer;
+import io.fury.resolver.AllowListChecker;
+
+import java.util.List;
+import java.util.Map;
+
+import static io.fury.config.CompatibleMode.COMPATIBLE;
+
+/**
+ * @author lipan
+ */
+@Extension(value = "fury2", code = 22)
+public class FurySerializer extends AbstractSerializer {
+
+ protected final ThreadSafeFury fury;
+
+ private final String checkerMode = SofaConfigs.getOrDefault(RpcConfigKeys.SERIALIZE_CHECKER_MODE);
+
+ public FurySerializer() {
+ fury = new ThreadLocalFury(classLoader -> {
+ Fury f = Fury.builder().withLanguage(Language.JAVA)
+ .withRefTracking(true)
+ .withCodegen(true)
+ .withNumberCompressed(true)
+ .withCompatibleMode(COMPATIBLE)
+ .requireClassRegistration(false)
+ .withClassLoader(classLoader)
+ .withAsyncCompilation(true)
+ .build();
+
+ // Do not use any configuration
+ if (checkerMode.equalsIgnoreCase(SerializeCheckStatus.DISABLE.name())) {
+ AllowListChecker noChecker = new AllowListChecker(AllowListChecker.CheckLevel.DISABLE);
+ f.getClassResolver().setClassChecker(noChecker);
+ return f;
+ } else if (checkerMode.equalsIgnoreCase(SerializeCheckStatus.WARN.name())) {
+ AllowListChecker blackListChecker = new AllowListChecker(AllowListChecker.CheckLevel.WARN);
+ List blackList = BlackAndWhiteListFileLoader.SOFA_SERIALIZE_BLACK_LIST;
+ // To setting checker
+ f.getClassResolver().setClassChecker(blackListChecker);
+ blackListChecker.addListener(f.getClassResolver());
+ // BlackList classes use wildcards
+ for (String key : blackList) {
+ blackListChecker.disallowClass(key + "*");
+ }
+ } else if (checkerMode.equalsIgnoreCase(SerializeCheckStatus.STRICT.name())) {
+ AllowListChecker blackAndWhiteListChecker = new AllowListChecker(AllowListChecker.CheckLevel.STRICT);
+ List whiteList = BlackAndWhiteListFileLoader.SOFA_SERIALIZER_WHITE_LIST;
+ // To setting checker
+ f.getClassResolver().setClassChecker(blackAndWhiteListChecker);
+ blackAndWhiteListChecker.addListener(f.getClassResolver());
+ // WhiteList classes use wildcards
+ for (String key : whiteList) {
+ blackAndWhiteListChecker.allowClass(key + "*");
+ }
+ List blackList = BlackAndWhiteListFileLoader.SOFA_SERIALIZE_BLACK_LIST;
+ // To setting checker
+ f.getClassResolver().setClassChecker(blackAndWhiteListChecker);
+ blackAndWhiteListChecker.addListener(f.getClassResolver());
+ // BlackList classes use wildcards
+ for (String key : blackList) {
+ blackAndWhiteListChecker.disallowClass(key + "*");
+ }
+ }
+ f.register(SofaRequest.class);
+ f.register(SofaResponse.class);
+ f.register(SofaRpcException.class);
+ return f;
+ });
+ addCustomSerializer(SofaRequest.class, new SofaRequestFurySerializer(fury));
+ addCustomSerializer(SofaResponse.class, new SofaResponseFurySerializer(fury));
+ }
+
+ @Override
+ public AbstractByteBuf encode(final Object object, final Map context) throws SofaRpcException {
+ if (object == null) {
+ throw buildSerializeError("Unsupported null message!");
+ }
+ ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+ try {
+ fury.setClassLoader(contextClassLoader);
+ CustomSerializer customSerializer = getObjCustomSerializer(object);
+ if (customSerializer != null) {
+ return customSerializer.encodeObject(object, context);
+ } else {
+ MemoryBuffer writeBuffer = MemoryBuffer.newHeapBuffer(32);
+ writeBuffer.writerIndex(0);
+ fury.serialize(writeBuffer, object);
+ return new ByteArrayWrapperByteBuf(writeBuffer.getBytes(0, writeBuffer.writerIndex()));
+ }
+ } catch (Exception e) {
+ throw buildSerializeError(e.getMessage(), e);
+ } finally {
+ fury.clearClassLoader(contextClassLoader);
+ }
+ }
+
+ @Override
+ public Object decode(final AbstractByteBuf data, final Class clazz, final Map context)
+ throws SofaRpcException {
+ if (data.readableBytes() <= 0 || clazz == null) {
+ throw buildDeserializeError("Deserialized array is empty.");
+ }
+ ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+ try {
+ fury.setClassLoader(contextClassLoader);
+ CustomSerializer customSerializer = getCustomSerializer(clazz);
+ if (customSerializer != null) {
+ return customSerializer.decodeObject(data, context);
+ } else {
+ MemoryBuffer readBuffer = MemoryBuffer.fromByteArray(data.array());
+ return fury.deserialize(readBuffer);
+ }
+ } catch (Exception e) {
+ throw buildDeserializeError(e.getMessage(), e);
+ } finally {
+ fury.clearClassLoader(contextClassLoader);
+ }
+ }
+
+ @Override
+ public void decode(final AbstractByteBuf data, final Object template, final Map context)
+ throws SofaRpcException {
+ if (template == null) {
+ throw buildDeserializeError("template is null!");
+ }
+ ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+ try {
+ fury.setClassLoader(contextClassLoader);
+ CustomSerializer customSerializer = getObjCustomSerializer(template);
+ if (customSerializer != null) {
+ customSerializer.decodeObjectByTemplate(data, context, template);
+ } else {
+ throw buildDeserializeError("Only support decode from SofaRequest and SofaResponse template");
+ }
+ } catch (Exception e) {
+ throw buildDeserializeError(e.getMessage(), e);
+ } finally {
+ fury.clearClassLoader(contextClassLoader);
+ }
+ }
+
+}
diff --git a/codec/codec-sofa-fury/src/main/java/com/alipay/sofa/rpc/codec/fury/serialize/SofaRequestFurySerializer.java b/codec/codec-sofa-fury/src/main/java/com/alipay/sofa/rpc/codec/fury/serialize/SofaRequestFurySerializer.java
new file mode 100644
index 000000000..b0543ced6
--- /dev/null
+++ b/codec/codec-sofa-fury/src/main/java/com/alipay/sofa/rpc/codec/fury/serialize/SofaRequestFurySerializer.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.codec.fury.serialize;
+
+import com.alipay.sofa.rpc.codec.CustomSerializer;
+import com.alipay.sofa.rpc.common.RemotingConstants;
+import com.alipay.sofa.rpc.config.ConfigUniqueNameGenerator;
+import com.alipay.sofa.rpc.core.exception.RpcErrorType;
+import com.alipay.sofa.rpc.core.exception.SofaRpcException;
+import com.alipay.sofa.rpc.core.request.SofaRequest;
+import com.alipay.sofa.rpc.transport.AbstractByteBuf;
+import com.alipay.sofa.rpc.transport.ByteArrayWrapperByteBuf;
+import io.fury.ThreadSafeFury;
+import io.fury.memory.MemoryBuffer;
+
+import java.util.Map;
+
+/**
+ * @author Even
+ * @date 2024/1/4 19:29
+ */
+public class SofaRequestFurySerializer implements CustomSerializer {
+
+ private final ThreadSafeFury fury;
+
+ public SofaRequestFurySerializer(ThreadSafeFury fury) {
+ this.fury = fury;
+ }
+
+ @Override
+ public AbstractByteBuf encodeObject(SofaRequest object, Map context) throws SofaRpcException {
+ try {
+ MemoryBuffer writeBuffer = MemoryBuffer.newHeapBuffer(32);
+ writeBuffer.writerIndex(0);
+
+ // 根据SerializeType信息决定序列化器
+ boolean genericSerialize = context != null &&
+ isGenericRequest(context.get(RemotingConstants.HEAD_GENERIC_TYPE));
+ if (genericSerialize) {
+ // TODO support generic call
+ throw new SofaRpcException(RpcErrorType.CLIENT_SERIALIZE, "Generic call is not supported for now.");
+ }
+ fury.serialize(writeBuffer, object);
+ final Object[] args = object.getMethodArgs();
+ fury.serialize(writeBuffer, args);
+
+ return new ByteArrayWrapperByteBuf(writeBuffer.getBytes(0, writeBuffer.writerIndex()));
+ } catch (Exception e) {
+ throw new SofaRpcException(RpcErrorType.CLIENT_SERIALIZE, e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public SofaRequest decodeObject(AbstractByteBuf data, Map context) throws SofaRpcException {
+ MemoryBuffer readBuffer = MemoryBuffer.fromByteArray(data.array());
+ try {
+ SofaRequest sofaRequest = (SofaRequest) fury.deserialize(readBuffer);
+ String targetServiceName = sofaRequest.getTargetServiceUniqueName();
+ if (targetServiceName == null) {
+ throw new SofaRpcException(RpcErrorType.SERVER_DESERIALIZE, "Target service name of request is null!");
+ }
+ String interfaceName = ConfigUniqueNameGenerator.getInterfaceName(targetServiceName);
+ sofaRequest.setInterfaceName(interfaceName);
+ final Object[] args = (Object[]) fury.deserialize(readBuffer);
+ sofaRequest.setMethodArgs(args);
+ return sofaRequest;
+ } catch (Exception e) {
+ throw new SofaRpcException(RpcErrorType.SERVER_DESERIALIZE, e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public void decodeObjectByTemplate(AbstractByteBuf data, Map context, SofaRequest template)
+ throws SofaRpcException {
+ if (data.readableBytes() <= 0) {
+ throw new SofaRpcException(RpcErrorType.SERVER_DESERIALIZE, "Deserialized array is empty.");
+ }
+ try {
+ MemoryBuffer readBuffer = MemoryBuffer.fromByteArray(data.array());
+ SofaRequest tmp = (SofaRequest) fury.deserialize(readBuffer);
+ String targetServiceName = tmp.getTargetServiceUniqueName();
+ if (targetServiceName == null) {
+ throw new SofaRpcException(RpcErrorType.SERVER_DESERIALIZE, "Target service name of request is null!");
+ }
+ // copy values to template
+ template.setMethodName(tmp.getMethodName());
+ template.setMethodArgSigs(tmp.getMethodArgSigs());
+ template.setTargetServiceUniqueName(tmp.getTargetServiceUniqueName());
+ template.setTargetAppName(tmp.getTargetAppName());
+ template.addRequestProps(tmp.getRequestProps());
+ String interfaceName = ConfigUniqueNameGenerator.getInterfaceName(targetServiceName);
+ template.setInterfaceName(interfaceName);
+ final Object[] args = (Object[]) fury.deserialize(readBuffer);
+ template.setMethodArgs(args);
+ } catch (Exception e) {
+ throw new SofaRpcException(RpcErrorType.SERVER_DESERIALIZE, e.getMessage(), e);
+ }
+ }
+
+ protected boolean isGenericRequest(String serializeType) {
+ return serializeType != null && !serializeType.equals(RemotingConstants.SERIALIZE_FACTORY_NORMAL);
+ }
+
+}
diff --git a/codec/codec-sofa-fury/src/main/java/com/alipay/sofa/rpc/codec/fury/serialize/SofaResponseFurySerializer.java b/codec/codec-sofa-fury/src/main/java/com/alipay/sofa/rpc/codec/fury/serialize/SofaResponseFurySerializer.java
new file mode 100644
index 000000000..5983be113
--- /dev/null
+++ b/codec/codec-sofa-fury/src/main/java/com/alipay/sofa/rpc/codec/fury/serialize/SofaResponseFurySerializer.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.codec.fury.serialize;
+
+import com.alipay.sofa.rpc.codec.CustomSerializer;
+import com.alipay.sofa.rpc.common.RemotingConstants;
+import com.alipay.sofa.rpc.core.exception.RpcErrorType;
+import com.alipay.sofa.rpc.core.exception.SofaRpcException;
+import com.alipay.sofa.rpc.core.response.SofaResponse;
+import com.alipay.sofa.rpc.transport.AbstractByteBuf;
+import com.alipay.sofa.rpc.transport.ByteArrayWrapperByteBuf;
+import io.fury.ThreadSafeFury;
+import io.fury.memory.MemoryBuffer;
+
+import java.util.Map;
+
+/**
+ * @author Even
+ * @date 2024/1/4 19:30
+ */
+public class SofaResponseFurySerializer implements CustomSerializer {
+
+ private final ThreadSafeFury fury;
+
+ public SofaResponseFurySerializer(ThreadSafeFury fury) {
+ this.fury = fury;
+ }
+
+ @Override
+ public AbstractByteBuf encodeObject(SofaResponse object, Map context) throws SofaRpcException {
+ try {
+ MemoryBuffer writeBuffer = MemoryBuffer.newHeapBuffer(32);
+ writeBuffer.writerIndex(0);
+ fury.serialize(writeBuffer, object);
+ return new ByteArrayWrapperByteBuf(writeBuffer.getBytes(0, writeBuffer.writerIndex()));
+ } catch (Exception e) {
+ throw new SofaRpcException(RpcErrorType.SERVER_DESERIALIZE, e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public SofaResponse decodeObject(AbstractByteBuf data, Map context) throws SofaRpcException {
+ MemoryBuffer readBuffer = MemoryBuffer.fromByteArray(data.array());
+ try {
+ boolean genericSerialize = context != null && isGenericResponse(
+ context.get(RemotingConstants.HEAD_GENERIC_TYPE));
+ if (genericSerialize) {
+ // TODO support generic call
+ throw new SofaRpcException(RpcErrorType.CLIENT_SERIALIZE, "Generic call is not supported for now.");
+ }
+ return (SofaResponse) fury.deserialize(readBuffer);
+ } catch (Exception e) {
+ throw new SofaRpcException(RpcErrorType.CLIENT_SERIALIZE, e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public void decodeObjectByTemplate(AbstractByteBuf data, Map context, SofaResponse template)
+ throws SofaRpcException {
+ if (data.readableBytes() <= 0) {
+ throw new SofaRpcException(RpcErrorType.CLIENT_SERIALIZE, "Deserialized array is empty.");
+ }
+ try {
+ MemoryBuffer readBuffer = MemoryBuffer.fromByteArray(data.array());
+ // 根据SerializeType信息决定序列化器
+ boolean genericSerialize = context != null && isGenericResponse(
+ context.get(RemotingConstants.HEAD_GENERIC_TYPE));
+ if (genericSerialize) {
+ // TODO support generic call
+ throw new SofaRpcException(RpcErrorType.CLIENT_SERIALIZE, "Generic call is not supported for now.");
+ } else {
+ SofaResponse tmp = (SofaResponse) fury.deserialize(readBuffer);
+ // copy values to template
+ template.setErrorMsg(tmp.getErrorMsg());
+ template.setAppResponse(tmp.getAppResponse());
+ template.setResponseProps(tmp.getResponseProps());
+ }
+ } catch (Exception e) {
+ throw new SofaRpcException(RpcErrorType.CLIENT_SERIALIZE, e.getMessage(), e);
+ }
+ }
+
+ protected boolean isGenericResponse(String serializeType) {
+ return serializeType != null && serializeType.equals(RemotingConstants.SERIALIZE_FACTORY_GENERIC);
+ }
+}
diff --git a/codec/codec-sofa-fury/src/main/resources/META-INF/services/sofa-rpc/com.alipay.sofa.rpc.codec.Serializer b/codec/codec-sofa-fury/src/main/resources/META-INF/services/sofa-rpc/com.alipay.sofa.rpc.codec.Serializer
new file mode 100644
index 000000000..dc727a0dd
--- /dev/null
+++ b/codec/codec-sofa-fury/src/main/resources/META-INF/services/sofa-rpc/com.alipay.sofa.rpc.codec.Serializer
@@ -0,0 +1 @@
+fury2=com.alipay.sofa.rpc.codec.fury.FurySerializer
\ No newline at end of file
diff --git a/codec/codec-sofa-fury/src/test/java/com/alipay/sofa/rpc/codec/fury/FurySerializerTest.java b/codec/codec-sofa-fury/src/test/java/com/alipay/sofa/rpc/codec/fury/FurySerializerTest.java
new file mode 100644
index 000000000..fc4fde8cc
--- /dev/null
+++ b/codec/codec-sofa-fury/src/test/java/com/alipay/sofa/rpc/codec/fury/FurySerializerTest.java
@@ -0,0 +1,289 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.codec.fury;
+
+import com.alipay.sofa.rpc.codec.Serializer;
+import com.alipay.sofa.rpc.codec.fury.model.blacklist.BlackListClass;
+import com.alipay.sofa.rpc.codec.fury.model.none.NoneClassHasBlackClass;
+import com.alipay.sofa.rpc.codec.fury.model.whitelist.DemoRequest;
+import com.alipay.sofa.rpc.codec.fury.model.whitelist.DemoResponse;
+import com.alipay.sofa.rpc.codec.fury.model.whitelist.WhiteClassHasBlackClass;
+import com.alipay.sofa.rpc.codec.fury.model.whitelist.DemoService;
+import com.alipay.sofa.rpc.common.RemotingConstants;
+import com.alipay.sofa.rpc.common.RpcConstants;
+import com.alipay.sofa.rpc.core.exception.SofaRpcException;
+import com.alipay.sofa.rpc.core.invoke.SofaResponseCallback;
+import com.alipay.sofa.rpc.core.request.RequestBase;
+import com.alipay.sofa.rpc.core.request.SofaRequest;
+import com.alipay.sofa.rpc.core.response.SofaResponse;
+import com.alipay.sofa.rpc.ext.ExtensionLoaderFactory;
+import com.alipay.sofa.rpc.transport.AbstractByteBuf;
+import com.alipay.sofa.rpc.transport.ByteArrayWrapperByteBuf;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author lipan
+ */
+public class FurySerializerTest {
+
+ private final FurySerializer serializer = (FurySerializer) ExtensionLoaderFactory.getExtensionLoader(
+ Serializer.class).getExtension("fury2");
+
+ @Test
+ public void encodeAndDecode() {
+ try {
+ serializer.encode(null, null);
+ Assert.fail();
+ } catch (Exception e) {
+
+ }
+
+ DemoRequest demoRequest = new DemoRequest();
+ demoRequest.setName("a");
+ AbstractByteBuf byteBuf = serializer.encode(demoRequest, null);
+ DemoRequest req2 = (DemoRequest) serializer.decode(byteBuf, DemoRequest.class, null);
+ Assert.assertEquals(demoRequest.getName(), req2.getName());
+
+ AbstractByteBuf data = serializer.encode("xxx", null);
+ String dst = (String) serializer.decode(data, String.class, null);
+ Assert.assertEquals("xxx", dst);
+
+ try {
+ serializer.decode(data, null, null);
+ Assert.fail();
+ } catch (Exception e) {
+ }
+
+ try {
+ serializer.decode(data, "", null);
+ Assert.fail();
+ } catch (Exception e) {
+
+ }
+ }
+
+ @Test
+ public void testSofaRequest() throws Exception {
+ SofaRequest request = buildRequest();
+ AbstractByteBuf data = serializer.encode(request, null);
+
+ serializer.encode("123456", null);
+ SofaRequest decode = (SofaRequest) serializer.decode(data, SofaRequest.class, null);
+ assertEqualsSofaRequest(request, decode);
+
+ SofaRequest newRequest = new SofaRequest();
+ serializer.decode(data, newRequest, null);
+ assertEqualsSofaRequest(request, newRequest);
+
+ // null request
+ newRequest = new SofaRequest();
+ try {
+ serializer.decode(new ByteArrayWrapperByteBuf(new byte[0]), newRequest, null);
+ Assert.fail();
+ } catch (Exception e) {
+
+ }
+ }
+
+ @Test
+ public void testSofaResponse() throws Exception {
+ SofaResponse response = new SofaResponse();
+ final DemoResponse demoAppResponse = new DemoResponse();
+ demoAppResponse.setWord("result");
+ response.setAppResponse(demoAppResponse);
+ AbstractByteBuf data = serializer.encode(response, null);
+
+ try {
+ serializer.decode(data, null, null);
+ Assert.fail();
+ } catch (Exception e) {
+
+ }
+
+ try {
+ serializer.decode(data, new Object(), null);
+ Assert.fail();
+ } catch (Exception e) {
+
+ }
+
+ SofaResponse decode = (SofaResponse) serializer.decode(data, SofaResponse.class, null);
+ Assert.assertFalse(decode.isError());
+ Assert.assertEquals(response.getAppResponse(), decode.getAppResponse());
+ Assert.assertEquals("result", ((DemoResponse) decode.getAppResponse()).getWord());
+
+ // success response
+ SofaResponse newResponse = new SofaResponse();
+ serializer.decode(data, newResponse, null);
+ Assert.assertFalse(newResponse.isError());
+ Assert.assertEquals(response.getAppResponse(), newResponse.getAppResponse());
+ Assert.assertEquals("result", ((DemoResponse) newResponse.getAppResponse()).getWord());
+
+ // null response
+ newResponse = new SofaResponse();
+ try {
+ serializer.decode(new ByteArrayWrapperByteBuf(new byte[0]), newResponse, null);
+ Assert.fail();
+ } catch (Exception e) {
+
+ }
+ // error response
+ response = new SofaResponse();
+ response.setErrorMsg("1233");
+ data = serializer.encode(response, null);
+ newResponse = new SofaResponse();
+ serializer.decode(data, newResponse, null);
+ Assert.assertTrue(newResponse.isError());
+ Assert.assertEquals(response.getErrorMsg(), newResponse.getErrorMsg());
+ }
+
+ private SofaRequest buildRequest() throws NoSuchMethodException {
+ SofaRequest request = new SofaRequest();
+ request.setInterfaceName(DemoService.class.getName());
+ request.setMethodName("say");
+ request.setMethod(DemoService.class.getMethod("say", DemoRequest.class));
+ final DemoRequest demoRequest = new DemoRequest();
+ demoRequest.setName("name");
+ request.setMethodArgs(new Object[] { demoRequest });
+ request.setMethodArgSigs(new String[] { DemoRequest.class.getCanonicalName() });
+ request.setTargetServiceUniqueName(DemoService.class.getName() + ":1.0");
+ request.setTargetAppName("targetApp");
+ request.setSerializeType((byte) 22);
+ request.setTimeout(1024);
+ request.setInvokeType(RpcConstants.INVOKER_TYPE_SYNC);
+ Map map = new HashMap();
+ map.put("a", "xxx");
+ map.put("b", "yyy");
+ request.addRequestProp(RemotingConstants.RPC_TRACE_NAME, map);
+ request.setSofaResponseCallback(new SofaResponseCallback() {
+ @Override
+ public void onAppResponse(Object appResponse, String methodName, RequestBase request) {
+
+ }
+
+ @Override
+ public void onAppException(Throwable throwable, String methodName, RequestBase request) {
+
+ }
+
+ @Override
+ public void onSofaException(SofaRpcException sofaException, String methodName, RequestBase request) {
+
+ }
+ });
+ return request;
+ }
+
+ private void assertEqualsSofaRequest(SofaRequest request, SofaRequest decode) {
+ Assert.assertEquals(decode.getInterfaceName(), request.getInterfaceName());
+ Assert.assertEquals(decode.getMethodName(), request.getMethodName());
+ Assert.assertArrayEquals(decode.getMethodArgSigs(), request.getMethodArgSigs());
+ Assert.assertEquals(decode.getMethodArgs().length, request.getMethodArgs().length);
+ Assert.assertEquals("name", ((DemoRequest) decode.getMethodArgs()[0]).getName());
+ Assert.assertEquals(decode.getTargetServiceUniqueName(), request.getTargetServiceUniqueName());
+ Assert.assertEquals(decode.getTargetAppName(), request.getTargetAppName());
+ Assert.assertEquals(decode.getRequestProp(RemotingConstants.RPC_TRACE_NAME),
+ request.getRequestProp(RemotingConstants.RPC_TRACE_NAME));
+ }
+
+ @Test
+ public void testChecker() throws Exception {
+ // default fury checkMode is STRICT
+ WhiteClassHasBlackClass whiteClassNullBlackClass = new WhiteClassHasBlackClass();
+ NoneClassHasBlackClass noneClassNullBlackClass = new NoneClassHasBlackClass();
+
+ BlackListClass blackListClass = new BlackListClass();
+ WhiteClassHasBlackClass whiteClassHasBlackClass = new WhiteClassHasBlackClass();
+ whiteClassHasBlackClass.setBlackListClass(blackListClass);
+ NoneClassHasBlackClass noneClassHasBlackClass = new NoneClassHasBlackClass();
+ noneClassHasBlackClass.setBlackListClass(blackListClass);
+
+ try {
+ serializer.encode(noneClassNullBlackClass, null);
+ Assert.fail();
+ } catch (Exception e) {
+
+ }
+
+ try {
+ serializer.encode(noneClassHasBlackClass, null);
+ Assert.fail();
+ } catch (Exception e) {
+
+ }
+
+ try {
+ serializer.encode(blackListClass, null);
+ Assert.fail();
+ } catch (Exception e) {
+
+ }
+
+ serializer.encode(whiteClassNullBlackClass, null);
+ try {
+ serializer.encode(whiteClassHasBlackClass, null);
+ Assert.fail();
+ } catch (Exception e) {
+
+ }
+
+ // test change fury checkMode to blacklist
+ System.getProperties().put("sofa.rpc.codec.serialize.checkMode", "WARN");
+ FurySerializer furySerializer = new FurySerializer();
+
+ furySerializer.encode(noneClassNullBlackClass, null);
+
+ try {
+ furySerializer.encode(noneClassHasBlackClass, null);
+ Assert.fail();
+ } catch (Exception e) {
+
+ }
+
+ try {
+ //Not registered this class
+ furySerializer.encode(blackListClass, null);
+ Assert.fail();
+ } catch (Exception e) {
+
+ }
+
+ furySerializer.encode(whiteClassNullBlackClass, null);
+ try {
+ furySerializer.encode(whiteClassHasBlackClass, null);
+ Assert.fail();
+ } catch (Exception e) {
+
+ }
+ System.getProperties().remove("sofa.rpc.codec.serialize.checkMode");
+
+ // test change fury checkMode to none
+ System.getProperties().put("sofa.rpc.codec.serialize.checkMode", "DISABLE");
+ FurySerializer noneFurySerializer = new FurySerializer();
+ noneFurySerializer.encode(noneClassNullBlackClass, null);
+ noneFurySerializer.encode(noneClassHasBlackClass, null);
+ noneFurySerializer.encode(blackListClass, null);
+ noneFurySerializer.encode(whiteClassNullBlackClass, null);
+ noneFurySerializer.encode(whiteClassHasBlackClass, null);
+ System.getProperties().remove("sofa.rpc.codec.serialize.checkMode");
+ }
+
+}
\ No newline at end of file
diff --git a/codec/codec-sofa-fury/src/test/java/com/alipay/sofa/rpc/codec/fury/model/blacklist/BlackListClass.java b/codec/codec-sofa-fury/src/test/java/com/alipay/sofa/rpc/codec/fury/model/blacklist/BlackListClass.java
new file mode 100644
index 000000000..78281a910
--- /dev/null
+++ b/codec/codec-sofa-fury/src/test/java/com/alipay/sofa/rpc/codec/fury/model/blacklist/BlackListClass.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.codec.fury.model.blacklist;
+
+/**
+ * @author lipan
+ */
+public class BlackListClass {
+ private String test;
+}
diff --git a/codec/codec-sofa-fury/src/test/java/com/alipay/sofa/rpc/codec/fury/model/none/NoneClassHasBlackClass.java b/codec/codec-sofa-fury/src/test/java/com/alipay/sofa/rpc/codec/fury/model/none/NoneClassHasBlackClass.java
new file mode 100644
index 000000000..8ddaaa9a8
--- /dev/null
+++ b/codec/codec-sofa-fury/src/test/java/com/alipay/sofa/rpc/codec/fury/model/none/NoneClassHasBlackClass.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.codec.fury.model.none;
+
+import com.alipay.sofa.rpc.codec.fury.model.blacklist.BlackListClass;
+
+/**
+ * @author Even
+ * @date 2024/1/2 11:33
+ */
+public class NoneClassHasBlackClass {
+
+ private BlackListClass blackListClass;
+
+ public BlackListClass getBlackListClass() {
+ return blackListClass;
+ }
+
+ public void setBlackListClass(BlackListClass blackListClass) {
+ this.blackListClass = blackListClass;
+ }
+}
diff --git a/codec/codec-sofa-fury/src/test/java/com/alipay/sofa/rpc/codec/fury/model/whitelist/DemoRequest.java b/codec/codec-sofa-fury/src/test/java/com/alipay/sofa/rpc/codec/fury/model/whitelist/DemoRequest.java
new file mode 100644
index 000000000..7b9bb7129
--- /dev/null
+++ b/codec/codec-sofa-fury/src/test/java/com/alipay/sofa/rpc/codec/fury/model/whitelist/DemoRequest.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.codec.fury.model.whitelist;
+
+/**
+ * @author lipan
+ */
+public class DemoRequest {
+
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
\ No newline at end of file
diff --git a/codec/codec-sofa-fury/src/test/java/com/alipay/sofa/rpc/codec/fury/model/whitelist/DemoResponse.java b/codec/codec-sofa-fury/src/test/java/com/alipay/sofa/rpc/codec/fury/model/whitelist/DemoResponse.java
new file mode 100644
index 000000000..ffca06616
--- /dev/null
+++ b/codec/codec-sofa-fury/src/test/java/com/alipay/sofa/rpc/codec/fury/model/whitelist/DemoResponse.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.codec.fury.model.whitelist;
+
+/**
+ * @author lipan
+ */
+public class DemoResponse {
+
+ private String word;
+
+ public String getWord() {
+ return word;
+ }
+
+ public void setWord(String word) {
+ this.word = word;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof DemoResponse))
+ return false;
+
+ DemoResponse that = (DemoResponse) o;
+
+ return word != null ? word.equals(that.word) : that.word == null;
+ }
+
+ @Override
+ public int hashCode() {
+ return word != null ? word.hashCode() : 0;
+ }
+}
\ No newline at end of file
diff --git a/test/test-integration/src/test/java/com/alipay/sofa/rpc/metrics/lookout/LookoutService.java b/codec/codec-sofa-fury/src/test/java/com/alipay/sofa/rpc/codec/fury/model/whitelist/DemoService.java
similarity index 66%
rename from test/test-integration/src/test/java/com/alipay/sofa/rpc/metrics/lookout/LookoutService.java
rename to codec/codec-sofa-fury/src/test/java/com/alipay/sofa/rpc/codec/fury/model/whitelist/DemoService.java
index c3501e83a..604473b7b 100644
--- a/test/test-integration/src/test/java/com/alipay/sofa/rpc/metrics/lookout/LookoutService.java
+++ b/codec/codec-sofa-fury/src/test/java/com/alipay/sofa/rpc/codec/fury/model/whitelist/DemoService.java
@@ -14,20 +14,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.alipay.sofa.rpc.metrics.lookout;
+package com.alipay.sofa.rpc.codec.fury.model.whitelist;
+
+import java.util.List;
+import java.util.Map;
/**
- *
- * @author LiWei.Liangen
+ * @author lipan
*/
-public interface LookoutService {
-
- String saySync(String string) throws InterruptedException;
+public interface DemoService {
- String sayFuture(String string) throws InterruptedException;
+ public DemoResponse say(DemoRequest demoRequest);
- String sayCallback(String string) throws InterruptedException;
+ public DemoResponse say2(DemoRequest demoRequest, Map ctx, int id);
- String sayOneway(String string) throws InterruptedException;
+ public List say3(List list);
}
\ No newline at end of file
diff --git a/codec/codec-sofa-fury/src/test/java/com/alipay/sofa/rpc/codec/fury/model/whitelist/WhiteClassHasBlackClass.java b/codec/codec-sofa-fury/src/test/java/com/alipay/sofa/rpc/codec/fury/model/whitelist/WhiteClassHasBlackClass.java
new file mode 100644
index 000000000..10e3943fb
--- /dev/null
+++ b/codec/codec-sofa-fury/src/test/java/com/alipay/sofa/rpc/codec/fury/model/whitelist/WhiteClassHasBlackClass.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.codec.fury.model.whitelist;
+
+import com.alipay.sofa.rpc.codec.fury.model.blacklist.BlackListClass;
+
+/**
+ * @author lipan
+ */
+public class WhiteClassHasBlackClass {
+
+ private BlackListClass blackListClass;
+
+ public BlackListClass getBlackListClass() {
+ return blackListClass;
+ }
+
+ public void setBlackListClass(BlackListClass blackListClass) {
+ this.blackListClass = blackListClass;
+ }
+}
diff --git a/codec/codec-sofa-fury/src/test/resources/log4j.xml b/codec/codec-sofa-fury/src/test/resources/log4j.xml
new file mode 100644
index 000000000..e95634f16
--- /dev/null
+++ b/codec/codec-sofa-fury/src/test/resources/log4j.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/codec/codec-sofa-fury/src/test/resources/sofa-rpc/rpc-config.json b/codec/codec-sofa-fury/src/test/resources/sofa-rpc/rpc-config.json
new file mode 100644
index 000000000..5b573549a
--- /dev/null
+++ b/codec/codec-sofa-fury/src/test/resources/sofa-rpc/rpc-config.json
@@ -0,0 +1,4 @@
+{
+ "rpc.config.order": 999,
+ "logger.impl": "com.alipay.sofa.rpc.log.SLF4JLoggerImpl"
+}
\ No newline at end of file
diff --git a/codec/codec-sofa-fury/src/test/resources/sofa-rpc/serialize_blacklist.txt b/codec/codec-sofa-fury/src/test/resources/sofa-rpc/serialize_blacklist.txt
new file mode 100644
index 000000000..991e2a54d
--- /dev/null
+++ b/codec/codec-sofa-fury/src/test/resources/sofa-rpc/serialize_blacklist.txt
@@ -0,0 +1 @@
+com.alipay.sofa.rpc.codec.fury.model.blacklist.
\ No newline at end of file
diff --git a/codec/codec-sofa-fury/src/test/resources/sofa-rpc/serialize_whitelist.txt b/codec/codec-sofa-fury/src/test/resources/sofa-rpc/serialize_whitelist.txt
new file mode 100644
index 000000000..6f1718654
--- /dev/null
+++ b/codec/codec-sofa-fury/src/test/resources/sofa-rpc/serialize_whitelist.txt
@@ -0,0 +1 @@
+com.alipay.sofa.rpc.codec.fury.model.whitelist.
\ No newline at end of file
diff --git a/codec/codec-sofa-hessian/src/main/java/com/alipay/sofa/rpc/codec/sofahessian/SingleClassLoaderSofaSerializerFactory.java b/codec/codec-sofa-hessian/src/main/java/com/alipay/sofa/rpc/codec/sofahessian/SingleClassLoaderSofaSerializerFactory.java
index d79ddf610..8e0a8489c 100644
--- a/codec/codec-sofa-hessian/src/main/java/com/alipay/sofa/rpc/codec/sofahessian/SingleClassLoaderSofaSerializerFactory.java
+++ b/codec/codec-sofa-hessian/src/main/java/com/alipay/sofa/rpc/codec/sofahessian/SingleClassLoaderSofaSerializerFactory.java
@@ -27,6 +27,9 @@
import com.caucho.hessian.io.Serializer;
import com.caucho.hessian.io.SerializerFactory;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
import static com.alipay.hessian.generic.io.GenericDeserializer.ARRAY_PREFIX;
import static com.alipay.sofa.rpc.codec.sofahessian.serialize.GenericCustomThrowableDeterminer.isGenericThrowException;
@@ -40,8 +43,14 @@ public class SingleClassLoaderSofaSerializerFactory extends SerializerFactory {
/**
* logger for this class
*/
- private static final Logger LOGGER = LoggerFactory
- .getLogger(SingleClassLoaderSofaSerializerFactory.class);
+ private static final Logger LOGGER = LoggerFactory
+ .getLogger(SingleClassLoaderSofaSerializerFactory.class);
+
+ private final Map> _typeNotFoundMap = new ConcurrentHashMap<>(8);
+ private static final Object NOT_FOUND = new Object();
+ private final boolean dynamicLoadEnable = Boolean.parseBoolean(System.getProperty(
+ DYNAMIC_LOAD_ENABLE_KEY,
+ Boolean.FALSE.toString()));
@Override
protected Serializer getDefaultSerializer(Class cl) {
@@ -73,14 +82,24 @@ public Deserializer getDeserializer(String type) throws HessianProtocolException
Deserializer subDeserializer = getDeserializer(type.substring(1));
deserializer = new ArrayDeserializer(subDeserializer);
} else {
+ ClassLoader appClassLoader = Thread.currentThread().getContextClassLoader();
try {
- ClassLoader appClassLoader = Thread.currentThread().getContextClassLoader();
+ if (!dynamicLoadEnable) {
+ Map typeMap = _typeNotFoundMap.get(appClassLoader);
+ if (typeMap != null) {
+ if (typeMap.containsKey(type)) {
+ return null;
+ }
+ }
+ }
Class> cl = Class.forName(type, true, appClassLoader);
deserializer = getDeserializer(cl);
} catch (Exception e) {
if (e instanceof ClassNotFoundException) {
- LOGGER.errorWithApp(null, LogCodes.getLog(LogCodes.ERROR_DECODE_CLASS_NOT_FOUND,
- getClass().getName(), type, Thread.currentThread().getContextClassLoader()));
+ if (!dynamicLoadEnable) {
+ _typeNotFoundMap.computeIfAbsent(appClassLoader, k -> new ConcurrentHashMap<>(8)).put(type, NOT_FOUND);
+ }
+ LOGGER.errorWithApp(null, LogCodes.getLog(LogCodes.ERROR_DECODE_CLASS_NOT_FOUND, getClass().getName(), type, appClassLoader));
} else {
LOGGER.errorWithApp(null, e.toString(), e);
}
diff --git a/codec/codec-sofa-hessian/src/main/java/com/alipay/sofa/rpc/codec/sofahessian/SofaHessianSerializer.java b/codec/codec-sofa-hessian/src/main/java/com/alipay/sofa/rpc/codec/sofahessian/SofaHessianSerializer.java
index ae7eac3f0..06ec4a4a6 100644
--- a/codec/codec-sofa-hessian/src/main/java/com/alipay/sofa/rpc/codec/sofahessian/SofaHessianSerializer.java
+++ b/codec/codec-sofa-hessian/src/main/java/com/alipay/sofa/rpc/codec/sofahessian/SofaHessianSerializer.java
@@ -19,6 +19,7 @@
import com.alipay.hessian.ClassNameResolver;
import com.alipay.hessian.NameBlackListFilter;
import com.alipay.sofa.rpc.codec.AbstractSerializer;
+import com.alipay.sofa.rpc.codec.common.BlackAndWhiteListFileLoader;
import com.alipay.sofa.rpc.codec.sofahessian.serialize.CustomHessianSerializer;
import com.alipay.sofa.rpc.codec.sofahessian.serialize.SofaRequestHessianSerializer;
import com.alipay.sofa.rpc.codec.sofahessian.serialize.SofaResponseHessianSerializer;
@@ -95,7 +96,7 @@ public SofaHessianSerializer() {
if (RpcConfigs.getBooleanValue(RpcOptions.SERIALIZE_BLACKLIST_ENABLE) &&
SofaConfigs.getBooleanValue(SofaOptions.CONFIG_SERIALIZE_BLACKLIST, true)) {
ClassNameResolver resolver = new ClassNameResolver();
- resolver.addFilter(new NameBlackListFilter(BlackListFileLoader.SOFA_SERIALIZE_BLACK_LIST, 8192));
+ resolver.addFilter(new NameBlackListFilter(BlackAndWhiteListFileLoader.SOFA_SERIALIZE_BLACK_LIST, 8192));
serializerFactory.setClassNameResolver(resolver);
genericSerializerFactory.setClassNameResolver(resolver);
} else {
diff --git a/codec/codec-sofa-hessian/src/main/resources/sofa-rpc/serialize_blacklist.txt b/codec/codec-sofa-hessian/src/main/resources/sofa-rpc/serialize_blacklist.txt
deleted file mode 100644
index 754494cd5..000000000
--- a/codec/codec-sofa-hessian/src/main/resources/sofa-rpc/serialize_blacklist.txt
+++ /dev/null
@@ -1,53 +0,0 @@
-clojure.core$constantly
-clojure.main$eval_opt
-com.alibaba.citrus.springext.support.parser.AbstractNamedProxyBeanDefinitionParser$ProxyTargetFactory
-com.alibaba.citrus.springext.support.parser.AbstractNamedProxyBeanDefinitionParser$ProxyTargetFactoryImpl
-com.alibaba.citrus.springext.util.SpringExtUtil.AbstractProxy
-com.alipay.custrelation.service.model.redress.Pair
-com.caucho.hessian.test.TestCons
-com.mchange.v2.c3p0.JndiRefForwardingDataSource
-com.mchange.v2.c3p0.WrapperConnectionPoolDataSource
-com.rometools.rome.feed.impl.EqualsBean
-com.rometools.rome.feed.impl.ToStringBean
-com.sun.jndi.rmi.registry.BindingEnumeration
-com.sun.jndi.toolkit.dir.LazySearchEnumerationImpl
-com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
-com.sun.rowset.JdbcRowSetImpl
-com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data
-java.rmi.server.UnicastRemoteObject
-java.security.SignedObject
-java.util.ServiceLoader$LazyIterator
-javax.imageio.ImageIO$ContainsFilter
-javax.imageio.spi.ServiceRegistry
-javax.management.BadAttributeValueExpException
-javax.naming.InitialContext
-javax.naming.spi.ObjectFactory
-javax.script.ScriptEngineManager
-javax.sound.sampled.AudioFormat$Encoding
-javax.sound.sampled.AudioFileFormat
-org.apache.carbondata.core.scan.expression.ExpressionResult
-org.apache.commons.dbcp.datasources.SharedPoolDataSource
-org.apache.ibatis.executor.loader.AbstractSerialStateHolder
-org.apache.ibatis.executor.loader.CglibSerialStateHolder
-org.apache.ibatis.executor.loader.JavassistSerialStateHolder
-org.apache.ibatis.executor.loader.cglib.CglibProxyFactory
-org.apache.ibatis.executor.loader.javassist.JavassistSerialStateHolder
-org.apache.tomcat.dbcp.dbcp.datasources.SharedPoolDataSource
-org.apache.wicket.util.upload.DiskFileItem
-org.apache.xalan.xsltc.trax.TemplatesImpl
-org.apache.xbean.naming.context.ContextUtil$ReadOnlyBinding
-org.apache.xpath.XPathContext
-org.eclipse.jetty.util.log.LoggerLog
-org.geotools.filter.ConstantExpression
-org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator$PartiallyComparableAdvisorHolder
-org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor
-org.springframework.beans.factory.BeanFactory
-org.springframework.beans.factory.config.PropertyPathFactoryBean
-org.springframework.beans.factory.support.DefaultListableBeanFactory
-org.springframework.jndi.support.SimpleJndiBeanFactory
-org.springframework.orm.jpa.AbstractEntityManagerFactoryBean
-org.springframework.transaction.jta.JtaTransactionManager
-org.yaml.snakeyaml.tokens.DirectiveToken
-sun.rmi.server.UnicastRef
-javax.management.ImmutableDescriptor
-org.springframework.jndi.JndiObjectTargetSource
\ No newline at end of file
diff --git a/codec/codec-sofa-hessian/src/test/java/com/alipay/sofa/rpc/codec/sofahessian/BlackListFileLoaderTest.java b/codec/codec-sofa-hessian/src/test/java/com/alipay/sofa/rpc/codec/sofahessian/BlackAndWhiteListFileLoaderTest.java
similarity index 71%
rename from codec/codec-sofa-hessian/src/test/java/com/alipay/sofa/rpc/codec/sofahessian/BlackListFileLoaderTest.java
rename to codec/codec-sofa-hessian/src/test/java/com/alipay/sofa/rpc/codec/sofahessian/BlackAndWhiteListFileLoaderTest.java
index df934b6a1..05d279e4f 100644
--- a/codec/codec-sofa-hessian/src/test/java/com/alipay/sofa/rpc/codec/sofahessian/BlackListFileLoaderTest.java
+++ b/codec/codec-sofa-hessian/src/test/java/com/alipay/sofa/rpc/codec/sofahessian/BlackAndWhiteListFileLoaderTest.java
@@ -17,6 +17,7 @@
package com.alipay.sofa.rpc.codec.sofahessian;
import com.alipay.hessian.NameBlackListFilter;
+import com.alipay.sofa.rpc.codec.common.BlackAndWhiteListFileLoader;
import com.alipay.sofa.rpc.common.SofaOptions;
import org.junit.Assert;
import org.junit.Test;
@@ -29,17 +30,17 @@
*
* @author GengZhang
*/
-public class BlackListFileLoaderTest {
+public class BlackAndWhiteListFileLoaderTest {
@Test
public void testAll() throws Exception {
- List blacks = BlackListFileLoader.SOFA_SERIALIZE_BLACK_LIST;
+ List blacks = BlackAndWhiteListFileLoader.SOFA_SERIALIZE_BLACK_LIST;
Assert.assertNotNull(blacks);
String s = System.getProperty(SofaOptions.CONFIG_SERIALIZE_BLACKLIST_OVERRIDE);
try {
System.setProperty(SofaOptions.CONFIG_SERIALIZE_BLACKLIST_OVERRIDE, "-java.net.Socket");
- blacks = BlackListFileLoader.loadFile("/sofa-rpc/serialize_blacklist.txt");
+ blacks = BlackAndWhiteListFileLoader.loadBlackListFile("/sofa-rpc/serialize_blacklist.txt");
} finally {
if (s != null) {
System.setProperty(SofaOptions.CONFIG_SERIALIZE_BLACKLIST_OVERRIDE, s);
@@ -69,51 +70,51 @@ public void testAll() throws Exception {
@Test
public void overrideBlackList() {
List origin = buildOriginList();
- BlackListFileLoader.overrideBlackList(origin, "-*");
+ BlackAndWhiteListFileLoader.overrideBlackList(origin, "-*");
Assert.assertTrue(origin.size() == 0);
origin = buildOriginList();
- BlackListFileLoader.overrideBlackList(origin, "!*");
+ BlackAndWhiteListFileLoader.overrideBlackList(origin, "!*");
Assert.assertTrue(origin.size() == 0);
origin = buildOriginList();
- BlackListFileLoader.overrideBlackList(origin, "-default");
+ BlackAndWhiteListFileLoader.overrideBlackList(origin, "-default");
Assert.assertTrue(origin.size() == 0);
origin = buildOriginList();
- BlackListFileLoader.overrideBlackList(origin, "!default");
+ BlackAndWhiteListFileLoader.overrideBlackList(origin, "!default");
Assert.assertTrue(origin.size() == 0);
origin = buildOriginList();
- BlackListFileLoader.overrideBlackList(origin, "-*,-com.xxx");
+ BlackAndWhiteListFileLoader.overrideBlackList(origin, "-*,-com.xxx");
Assert.assertTrue(origin.size() == 0);
origin = buildOriginList();
- BlackListFileLoader.overrideBlackList(origin, "aaa,-*,-com.xxx");
+ BlackAndWhiteListFileLoader.overrideBlackList(origin, "aaa,-*,-com.xxx");
Assert.assertTrue(origin.size() == 1);
origin = buildOriginList();
- BlackListFileLoader.overrideBlackList(origin, "-*,aaa");
+ BlackAndWhiteListFileLoader.overrideBlackList(origin, "-*,aaa");
Assert.assertTrue(origin.size() == 1);
origin = buildOriginList();
- BlackListFileLoader.overrideBlackList(origin, "-com.xxx");
+ BlackAndWhiteListFileLoader.overrideBlackList(origin, "-com.xxx");
Assert.assertTrue(origin.size() == 2);
origin = buildOriginList();
- BlackListFileLoader.overrideBlackList(origin, "-com.xxx,-com.yyy");
+ BlackAndWhiteListFileLoader.overrideBlackList(origin, "-com.xxx,-com.yyy");
Assert.assertTrue(origin.size() == 1);
origin = buildOriginList();
- BlackListFileLoader.overrideBlackList(origin, "com.xxx,-com.yyy");
+ BlackAndWhiteListFileLoader.overrideBlackList(origin, "com.xxx,-com.yyy");
Assert.assertTrue(origin.size() == 2);
origin = buildOriginList();
- BlackListFileLoader.overrideBlackList(origin, "com.aaa,-com.yyy");
+ BlackAndWhiteListFileLoader.overrideBlackList(origin, "com.aaa,-com.yyy");
Assert.assertTrue(origin.size() == 3);
origin = buildOriginList();
- BlackListFileLoader.overrideBlackList(origin, "com.aaa");
+ BlackAndWhiteListFileLoader.overrideBlackList(origin, "com.aaa");
Assert.assertTrue(origin.size() == 4);
origin = buildOriginList();
- BlackListFileLoader.overrideBlackList(origin, "com.xxx;com.yyy;com.zzz");
+ BlackAndWhiteListFileLoader.overrideBlackList(origin, "com.xxx;com.yyy;com.zzz");
Assert.assertTrue(origin.size() == 3);
origin = buildOriginList();
- BlackListFileLoader.overrideBlackList(origin, "com.aaa,com.bbb,com.ccc");
+ BlackAndWhiteListFileLoader.overrideBlackList(origin, "com.aaa,com.bbb,com.ccc");
Assert.assertTrue(origin.size() == 6);
}
diff --git a/codec/codec-sofa-hessian/src/test/java/com/alipay/sofa/rpc/codec/sofahessian/SingleClassLoaderSofaSerializerFactoryTest.java b/codec/codec-sofa-hessian/src/test/java/com/alipay/sofa/rpc/codec/sofahessian/SingleClassLoaderSofaSerializerFactoryTest.java
index 727f1ff0b..26bef4434 100644
--- a/codec/codec-sofa-hessian/src/test/java/com/alipay/sofa/rpc/codec/sofahessian/SingleClassLoaderSofaSerializerFactoryTest.java
+++ b/codec/codec-sofa-hessian/src/test/java/com/alipay/sofa/rpc/codec/sofahessian/SingleClassLoaderSofaSerializerFactoryTest.java
@@ -16,13 +16,17 @@
*/
package com.alipay.sofa.rpc.codec.sofahessian;
+import com.caucho.hessian.io.Deserializer;
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
+import com.caucho.hessian.io.SerializerFactory;
+import org.junit.Assert;
import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
@@ -63,4 +67,35 @@ public void testAll() throws IOException {
assertEquals(a1[i], a2[i]);
}
+ @Test
+ public void testDynamicLoadDisabled() throws Exception {
+ SingleClassLoaderSofaSerializerFactory factory = new SingleClassLoaderSofaSerializerFactory();
+ Field field = SingleClassLoaderSofaSerializerFactory.class.getDeclaredField("_typeNotFoundMap");
+ field.setAccessible(true);
+ Map> _typeNotFoundMap = (Map>) field
+ .get(factory);
+ Assert.assertEquals(0, _typeNotFoundMap.size());
+ Deserializer deserializer = factory.getDeserializer("mock.xxx.MockObject");
+ Assert.assertNull(deserializer);
+ Assert.assertEquals(1, _typeNotFoundMap.size());
+ }
+
+ @Test
+ public void testDynamicLoadEnabled() throws Exception {
+ try {
+ System.setProperty(SerializerFactory.DYNAMIC_LOAD_ENABLE_KEY, "true");
+ SingleClassLoaderSofaSerializerFactory factory = new SingleClassLoaderSofaSerializerFactory();
+ Field field = SingleClassLoaderSofaSerializerFactory.class.getDeclaredField("_typeNotFoundMap");
+ field.setAccessible(true);
+ Map> _typeNotFoundMap = (Map>) field
+ .get(factory);
+ Assert.assertEquals(0, _typeNotFoundMap.size());
+ Deserializer deserializer = factory.getDeserializer("mock.xxx.MockObject");
+ Assert.assertNull(deserializer);
+ Assert.assertEquals(0, _typeNotFoundMap.size());
+ } finally {
+ System.clearProperty(SerializerFactory.DYNAMIC_LOAD_ENABLE_KEY);
+ }
+ }
+
}
\ No newline at end of file
diff --git a/codec/pom.xml b/codec/pom.xml
index 2ca5db8c2..25814b811 100644
--- a/codec/pom.xml
+++ b/codec/pom.xml
@@ -20,6 +20,7 @@
codec-protostuffcodec-sofa-hessiancodec-msgpack
+ codec-sofa-fury
diff --git a/config/config-apollo/pom.xml b/config/config-apollo/pom.xml
index f3a7879a8..1e292688c 100644
--- a/config/config-apollo/pom.xml
+++ b/config/config-apollo/pom.xml
@@ -29,7 +29,7 @@
com.ctrip.framework.apolloapollo-client
- 1.4.0
+ 2.1.0
diff --git a/config/config-apollo/src/main/java/com/alipay/sofa/rpc/dynamic/apollo/ApolloDynamicConfigManager.java b/config/config-apollo/src/main/java/com/alipay/sofa/rpc/dynamic/apollo/ApolloDynamicConfigManager.java
index 35367d5ca..d7aec05c5 100644
--- a/config/config-apollo/src/main/java/com/alipay/sofa/rpc/dynamic/apollo/ApolloDynamicConfigManager.java
+++ b/config/config-apollo/src/main/java/com/alipay/sofa/rpc/dynamic/apollo/ApolloDynamicConfigManager.java
@@ -16,13 +16,30 @@
*/
package com.alipay.sofa.rpc.dynamic.apollo;
+import com.alipay.sofa.common.config.SofaConfigs;
import com.alipay.sofa.rpc.auth.AuthRuleGroup;
+import com.alipay.sofa.rpc.common.utils.StringUtils;
+import com.alipay.sofa.rpc.dynamic.ConfigChangeType;
+import com.alipay.sofa.rpc.dynamic.ConfigChangedEvent;
import com.alipay.sofa.rpc.dynamic.DynamicConfigKeyHelper;
+import com.alipay.sofa.rpc.dynamic.DynamicConfigKeys;
import com.alipay.sofa.rpc.dynamic.DynamicConfigManager;
import com.alipay.sofa.rpc.dynamic.DynamicHelper;
import com.alipay.sofa.rpc.ext.Extension;
+import com.alipay.sofa.rpc.listener.ConfigListener;
+import com.alipay.sofa.rpc.log.Logger;
+import com.alipay.sofa.rpc.log.LoggerFactory;
import com.ctrip.framework.apollo.Config;
+import com.ctrip.framework.apollo.ConfigChangeListener;
import com.ctrip.framework.apollo.ConfigService;
+import com.ctrip.framework.apollo.enums.PropertyChangeType;
+import com.ctrip.framework.apollo.model.ConfigChange;
+
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArraySet;
/**
* @author bystander
@@ -34,41 +51,88 @@
@Extension(value = "apollo", override = true)
public class ApolloDynamicConfigManager extends DynamicConfigManager {
- private Config config;
+ private final static Logger LOGGER = LoggerFactory.getLogger(ApolloDynamicConfigManager.class);
+
+ private static final String APOLLO_APPID_KEY = "app.id";
+
+ private static final String APOLLO_ADDR_KEY = "apollo.meta";
+
+ private static final String APOLLO_CLUSTER_KEY = "apollo.cluster";
+
+ private static final String APOLLO_PARAM_APPID_KEY = "appId";
+
+ private static final String APOLLO_PARAM_CLUSTER_KEY = "cluster";
+
+ private static final String APOLLO_PARAM_NAMESPACE_KEY = "namespace";
+
+ private static final String APOLLO_PROTOCOL_PREFIX = "http://";
+
+ private final ConcurrentMap watchListenerMap = new ConcurrentHashMap<>();
+
+ private final Config config;
protected ApolloDynamicConfigManager(String appName) {
- super(appName);
- config = ConfigService.getAppConfig();
+ super(appName, SofaConfigs.getOrCustomDefault(DynamicConfigKeys.CONFIG_CENTER_ADDRESS, ""));
+ if (getDynamicUrl() != null) {
+ if (StringUtils.isNotBlank(getDynamicUrl().getParam(APOLLO_PARAM_APPID_KEY))) {
+ System.setProperty(APOLLO_APPID_KEY, getDynamicUrl().getParam(APOLLO_PARAM_APPID_KEY));
+ }
+ if (StringUtils.isNotBlank(getDynamicUrl().getAddress())) {
+ System.setProperty(APOLLO_ADDR_KEY, APOLLO_PROTOCOL_PREFIX + getDynamicUrl().getAddress());
+ }
+ if (StringUtils.isNotBlank(getDynamicUrl().getParam(APOLLO_PARAM_CLUSTER_KEY))) {
+ System.setProperty(APOLLO_CLUSTER_KEY, getDynamicUrl().getParam(APOLLO_PARAM_CLUSTER_KEY));
+ }
+ if (StringUtils.isNotBlank(getDynamicUrl().getParam(APOLLO_PARAM_NAMESPACE_KEY))) {
+ config = ConfigService.getConfig(getDynamicUrl().getParam(APOLLO_PARAM_NAMESPACE_KEY));
+ } else {
+ config = ConfigService.getAppConfig();
+ }
+ } else {
+ config = ConfigService.getAppConfig();
+ }
}
@Override
public void initServiceConfiguration(String service) {
- //TODO not now
+ // TODO 暂不支持
+ }
+
+ @Override
+ public void initServiceConfiguration(String service, ConfigListener listener) {
+ try {
+ String rawConfig = config.getProperty(service, "");
+ if (StringUtils.isNotBlank(rawConfig)) {
+ listener.process(new ConfigChangedEvent(service, rawConfig));
+ }
+ } catch (Exception e) {
+ LOGGER.error("Init service configuration error", e);
+ }
}
@Override
public String getProviderServiceProperty(String service, String key) {
return config.getProperty(DynamicConfigKeyHelper.buildProviderServiceProKey(service, key),
- DynamicHelper.DEFAULT_DYNAMIC_VALUE);
+ DynamicHelper.DEFAULT_DYNAMIC_VALUE);
}
@Override
public String getConsumerServiceProperty(String service, String key) {
return config.getProperty(DynamicConfigKeyHelper.buildConsumerServiceProKey(service, key),
- DynamicHelper.DEFAULT_DYNAMIC_VALUE);
+ DynamicHelper.DEFAULT_DYNAMIC_VALUE);
}
@Override
public String getProviderMethodProperty(String service, String method, String key) {
return config.getProperty(DynamicConfigKeyHelper.buildProviderMethodProKey(service, method, key),
- DynamicHelper.DEFAULT_DYNAMIC_VALUE);
+ DynamicHelper.DEFAULT_DYNAMIC_VALUE);
}
@Override
public String getConsumerMethodProperty(String service, String method, String key) {
return config.getProperty(DynamicConfigKeyHelper.buildConsumerMethodProKey(service, method, key),
- DynamicHelper.DEFAULT_DYNAMIC_VALUE);
+ DynamicHelper.DEFAULT_DYNAMIC_VALUE);
}
@@ -77,4 +141,40 @@ public AuthRuleGroup getServiceAuthRule(String service) {
//TODO 暂不支持
return null;
}
+
+ @Override
+ public void addListener(String key, ConfigListener listener) {
+ ApolloListener apolloListener = watchListenerMap.computeIfAbsent(key, k -> new ApolloListener());
+ apolloListener.addListener(listener);
+ config.addChangeListener(apolloListener, Collections.singleton(key));
+ }
+
+ public static class ApolloListener implements ConfigChangeListener {
+
+ private final Set listeners = new CopyOnWriteArraySet<>();
+
+ @Override
+ public void onChange(com.ctrip.framework.apollo.model.ConfigChangeEvent changeEvent) {
+ for (String key : changeEvent.changedKeys()) {
+ ConfigChange change = changeEvent.getChange(key);
+ ConfigChangedEvent event =
+ new ConfigChangedEvent(key, change.getNewValue(), getChangeType(change));
+ listeners.forEach(listener -> listener.process(event));
+ }
+ }
+
+ private ConfigChangeType getChangeType(ConfigChange change) {
+ if (change.getChangeType() == PropertyChangeType.DELETED) {
+ return ConfigChangeType.DELETED;
+ }
+ if (change.getChangeType() == PropertyChangeType.ADDED) {
+ return ConfigChangeType.ADDED;
+ }
+ return ConfigChangeType.MODIFIED;
+ }
+
+ void addListener(ConfigListener configListener) {
+ this.listeners.add(configListener);
+ }
+ }
}
\ No newline at end of file
diff --git a/config/config-apollo/src/test/java/com/alipay/sofa/rpc/dynamic/apollo/ApolloDynamicConfigManagerTest.java b/config/config-apollo/src/test/java/com/alipay/sofa/rpc/dynamic/apollo/ApolloDynamicConfigManagerTest.java
index 5fbb4daf0..2b08e90d3 100644
--- a/config/config-apollo/src/test/java/com/alipay/sofa/rpc/dynamic/apollo/ApolloDynamicConfigManagerTest.java
+++ b/config/config-apollo/src/test/java/com/alipay/sofa/rpc/dynamic/apollo/ApolloDynamicConfigManagerTest.java
@@ -16,6 +16,8 @@
*/
package com.alipay.sofa.rpc.dynamic.apollo;
+import com.alipay.sofa.rpc.dynamic.DynamicConfigManager;
+import com.alipay.sofa.rpc.dynamic.DynamicConfigManagerFactory;
import com.alipay.sofa.rpc.dynamic.DynamicHelper;
import com.alipay.sofa.rpc.log.Logger;
import com.alipay.sofa.rpc.log.LoggerFactory;
@@ -24,10 +26,11 @@
public class ApolloDynamicConfigManagerTest {
- private final static Logger logger = LoggerFactory
- .getLogger(ApolloDynamicConfigManagerTest.class);
+ private final static Logger logger = LoggerFactory
+ .getLogger(ApolloDynamicConfigManagerTest.class);
- private ApolloDynamicConfigManager apolloDynamicConfigManager = new ApolloDynamicConfigManager("test");
+ private DynamicConfigManager apolloDynamicConfigManager = DynamicConfigManagerFactory.getDynamicManager("test",
+ "apollo");
@Test
public void getProviderServiceProperty() {
@@ -37,17 +40,19 @@ public void getProviderServiceProperty() {
@Test
public void getConsumerServiceProperty() {
+ String result = apolloDynamicConfigManager.getConsumerServiceProperty("serviceName", "timeout");
+ Assert.assertEquals(DynamicHelper.DEFAULT_DYNAMIC_VALUE, result);
}
@Test
public void getProviderMethodProperty() {
+ String result = apolloDynamicConfigManager.getProviderMethodProperty("serviceName", "methodName", "timeout");
+ Assert.assertEquals(DynamicHelper.DEFAULT_DYNAMIC_VALUE, result);
}
@Test
public void getConsumerMethodProperty() {
- }
-
- @Test
- public void getServiceAuthRule() {
+ String result = apolloDynamicConfigManager.getConsumerMethodProperty("serviceName", "methodName", "timeout");
+ Assert.assertEquals(DynamicHelper.DEFAULT_DYNAMIC_VALUE, result);
}
}
\ No newline at end of file
diff --git a/metrics/metrics-lookout/pom.xml b/config/config-nacos/pom.xml
similarity index 82%
rename from metrics/metrics-lookout/pom.xml
rename to config/config-nacos/pom.xml
index d8696fa0b..4e0d8740b 100644
--- a/metrics/metrics-lookout/pom.xml
+++ b/config/config-nacos/pom.xml
@@ -2,37 +2,34 @@
+ 4.0.0
+
- sofa-rpc-metricscom.alipay.sofa
+ sofa-rpc-config${revision}
- 4.0.0
- sofa-rpc-metrics-lookout
+ sofa-rpc-config-nacos
-
-
com.alipay.sofasofa-rpc-log-common-tools
- ${project.parent.version}
-
-
- com.alipay.sofa.lookout
- lookout-api
+ com.alipay.sofa
+ sofa-rpc-log
-
-
- com.alipay.sofa.lookout
- lookout-core
- test
+ com.alipay.sofa
+ sofa-rpc-api
+
+ com.alibaba.nacos
+ nacos-client
+ org.slf4jslf4j-log4j12
@@ -43,6 +40,7 @@
junittest
+
@@ -73,7 +71,7 @@
maven-compiler-plugin${maven.compiler.source}
- ${maven.compiler.target}
+ ${maven.compiler.source}${project.build.sourceEncoding}
@@ -97,8 +95,10 @@
${skipTests}
+
**/*Test.java
+
once
diff --git a/config/config-nacos/src/main/java/com/alipay/sofa/rpc/dynamic/nacos/NacosDynamicConfigManager.java b/config/config-nacos/src/main/java/com/alipay/sofa/rpc/dynamic/nacos/NacosDynamicConfigManager.java
new file mode 100644
index 000000000..366681927
--- /dev/null
+++ b/config/config-nacos/src/main/java/com/alipay/sofa/rpc/dynamic/nacos/NacosDynamicConfigManager.java
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.dynamic.nacos;
+
+import com.alibaba.nacos.api.PropertyKeyConst;
+import com.alibaba.nacos.api.config.listener.AbstractSharedListener;
+import com.alipay.sofa.common.config.SofaConfigs;
+import com.alipay.sofa.rpc.auth.AuthRuleGroup;
+import com.alipay.sofa.rpc.common.utils.StringUtils;
+import com.alipay.sofa.rpc.dynamic.ConfigChangeType;
+import com.alipay.sofa.rpc.dynamic.ConfigChangedEvent;
+import com.alipay.sofa.rpc.dynamic.DynamicConfigKeyHelper;
+import com.alipay.sofa.rpc.dynamic.DynamicConfigKeys;
+import com.alipay.sofa.rpc.dynamic.DynamicConfigManager;
+import com.alipay.sofa.rpc.dynamic.DynamicHelper;
+import com.alipay.sofa.rpc.ext.Extension;
+import com.alibaba.nacos.api.config.ConfigService;
+import com.alibaba.nacos.api.config.ConfigFactory;
+import com.alipay.sofa.rpc.listener.ConfigListener;
+import com.alipay.sofa.rpc.log.Logger;
+import com.alipay.sofa.rpc.log.LoggerFactory;
+
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+/**
+ * @author Narziss
+ * @version NaocsDynamicConfigManager.java, v 0.1 2024年07月26日 09:37 Narziss
+ */
+
+@Extension(value = "nacos", override = true)
+public class NacosDynamicConfigManager extends DynamicConfigManager {
+
+ private final static Logger LOGGER = LoggerFactory.getLogger(NacosDynamicConfigManager.class);
+
+ private static final long DEFAULT_TIMEOUT = 5000;
+
+ private final String group;
+
+ private final ConcurrentMap watchListenerMap = new ConcurrentHashMap<>();
+
+ private ConfigService configService;
+
+ protected NacosDynamicConfigManager(String appName) {
+ super(appName, SofaConfigs.getOrCustomDefault(DynamicConfigKeys.CONFIG_CENTER_ADDRESS, "nacos://127.0.0.1:8848"));
+ group = appName;
+ Properties nacosConfig = new Properties();
+ nacosConfig.put(PropertyKeyConst.SERVER_ADDR, getDynamicUrl().getAddress());
+ nacosConfig.putAll(getDynamicUrl().getParams());
+ try {
+ configService = ConfigFactory.createConfigService(nacosConfig);
+ } catch (Exception e) {
+ LOGGER.error("Failed to create ConfigService", e);
+ }
+ }
+
+ @Override
+ public void initServiceConfiguration(String service) {
+ // TODO 暂不支持
+ }
+
+ @Override
+ public void initServiceConfiguration(String service, ConfigListener listener) {
+ try {
+ String rawConfig = configService.getConfig(service, group, DEFAULT_TIMEOUT);
+ if (!StringUtils.isEmpty(rawConfig)) {
+ listener.process(new ConfigChangedEvent(service, rawConfig));
+ }
+ } catch (Exception e) {
+ LOGGER.error("Failed to getConfig for key:{}, group:{}", service, group, e);
+ }
+ }
+
+ @Override
+ public String getProviderServiceProperty(String service, String key) {
+ try {
+ String configValue = configService.getConfig(
+ DynamicConfigKeyHelper.buildProviderServiceProKey(service, key),
+ group, DEFAULT_TIMEOUT);
+ return configValue != null ? configValue : DynamicHelper.DEFAULT_DYNAMIC_VALUE;
+ } catch (Exception e) {
+ return DynamicHelper.DEFAULT_DYNAMIC_VALUE;
+ }
+ }
+
+ @Override
+ public String getConsumerServiceProperty(String service, String key) {
+ try {
+ String configValue = configService.getConfig(
+ DynamicConfigKeyHelper.buildConsumerServiceProKey(service, key),
+ group, DEFAULT_TIMEOUT);
+ return configValue != null ? configValue : DynamicHelper.DEFAULT_DYNAMIC_VALUE;
+ } catch (Exception e) {
+ return DynamicHelper.DEFAULT_DYNAMIC_VALUE;
+ }
+ }
+
+ @Override
+ public String getProviderMethodProperty(String service, String method, String key) {
+ try {
+ String configValue = configService.getConfig(
+ DynamicConfigKeyHelper.buildProviderMethodProKey(service, method, key),
+ group, DEFAULT_TIMEOUT);
+ return configValue != null ? configValue : DynamicHelper.DEFAULT_DYNAMIC_VALUE;
+ } catch (Exception e) {
+ return DynamicHelper.DEFAULT_DYNAMIC_VALUE;
+ }
+ }
+
+ @Override
+ public String getConsumerMethodProperty(String service, String method, String key) {
+ try {
+ String configValue = configService.getConfig(
+ DynamicConfigKeyHelper.buildConsumerMethodProKey(service, method, key),
+ group, DEFAULT_TIMEOUT);
+ return configValue != null ? configValue : DynamicHelper.DEFAULT_DYNAMIC_VALUE;
+ } catch (Exception e) {
+ return DynamicHelper.DEFAULT_DYNAMIC_VALUE;
+ }
+ }
+
+ @Override
+ public AuthRuleGroup getServiceAuthRule(String service) {
+ //TODO 暂不支持
+ return null;
+ }
+
+ @Override
+ public void addListener(String key, ConfigListener listener) {
+ NacosConfigListener nacosConfigListener = watchListenerMap.computeIfAbsent(
+ key, k -> createTargetListener(key));
+ nacosConfigListener.addListener(listener);
+ try {
+ configService.addListener(key, group, nacosConfigListener);
+ } catch (Exception e) {
+ LOGGER.error("Failed to add listener for key:{}, group:{}", key, group, e);
+ }
+ }
+
+ private NacosConfigListener createTargetListener(String key) {
+ NacosConfigListener configListener = new NacosConfigListener();
+ configListener.fillContext(key, group);
+ return configListener;
+ }
+
+ public static class NacosConfigListener extends AbstractSharedListener {
+
+ private final Set listeners = new CopyOnWriteArraySet<>();
+
+ /**
+ * cache data to store old value
+ */
+ private final Map cacheData = new ConcurrentHashMap<>();
+
+ /**
+ * receive config change event
+ *
+ * @param dataId data ID
+ * @param group group
+ * @param configInfo content
+ */
+ @Override
+ public void innerReceive(String dataId, String group, String configInfo) {
+ String oldValue = cacheData.get(dataId);
+ ConfigChangedEvent event =
+ new ConfigChangedEvent(dataId, configInfo, getChangeType(configInfo, oldValue));
+ if (configInfo == null) {
+ cacheData.remove(dataId);
+ } else {
+ cacheData.put(dataId, configInfo);
+ }
+ listeners.forEach(listener -> listener.process(event));
+ }
+
+ void addListener(ConfigListener configListener) {
+
+ this.listeners.add(configListener);
+ }
+
+ private ConfigChangeType getChangeType(String configInfo, String oldValue) {
+ if (StringUtils.isBlank(configInfo)) {
+ return ConfigChangeType.DELETED;
+ }
+ if (StringUtils.isBlank(oldValue)) {
+ return ConfigChangeType.ADDED;
+ }
+ return ConfigChangeType.MODIFIED;
+ }
+ }
+}
\ No newline at end of file
diff --git a/config/config-nacos/src/main/resources/META-INF/services/sofa-rpc/com.alipay.sofa.rpc.dynamic.DynamicConfigManager b/config/config-nacos/src/main/resources/META-INF/services/sofa-rpc/com.alipay.sofa.rpc.dynamic.DynamicConfigManager
new file mode 100644
index 000000000..c055a88ae
--- /dev/null
+++ b/config/config-nacos/src/main/resources/META-INF/services/sofa-rpc/com.alipay.sofa.rpc.dynamic.DynamicConfigManager
@@ -0,0 +1,2 @@
+nacos=com.alipay.sofa.rpc.dynamic.nacos.NacosDynamicConfigManager
+
diff --git a/config/config-nacos/src/test/java/com/alipay/sofa/rpc/dynamic/nacos/NacosDynamicConfigManagerTest.java b/config/config-nacos/src/test/java/com/alipay/sofa/rpc/dynamic/nacos/NacosDynamicConfigManagerTest.java
new file mode 100644
index 000000000..f880cdf7b
--- /dev/null
+++ b/config/config-nacos/src/test/java/com/alipay/sofa/rpc/dynamic/nacos/NacosDynamicConfigManagerTest.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.dynamic.nacos;
+
+import com.alipay.sofa.rpc.dynamic.DynamicConfigManager;
+import com.alipay.sofa.rpc.dynamic.DynamicConfigManagerFactory;
+import com.alipay.sofa.rpc.dynamic.DynamicHelper;
+import com.alipay.sofa.rpc.log.Logger;
+import com.alipay.sofa.rpc.log.LoggerFactory;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class NacosDynamicConfigManagerTest {
+
+ private final static Logger logger = LoggerFactory
+ .getLogger(NacosDynamicConfigManagerTest.class);
+
+ private DynamicConfigManager nacosDynamicConfigManager = DynamicConfigManagerFactory.getDynamicManager("test",
+ "nacos");
+
+ @Test
+ public void getProviderServiceProperty() {
+ String result = nacosDynamicConfigManager.getProviderServiceProperty("serviceName", "timeout");
+ Assert.assertEquals(DynamicHelper.DEFAULT_DYNAMIC_VALUE, result);
+ }
+
+ @Test
+ public void getConsumerServiceProperty() {
+ String result = nacosDynamicConfigManager.getConsumerServiceProperty("serviceName", "timeout");
+ Assert.assertEquals(DynamicHelper.DEFAULT_DYNAMIC_VALUE, result);
+ }
+
+ @Test
+ public void getProviderMethodProperty() {
+ String result = nacosDynamicConfigManager.getProviderMethodProperty("serviceName", "methodName", "timeout");
+ Assert.assertEquals(DynamicHelper.DEFAULT_DYNAMIC_VALUE, result);
+ }
+
+ @Test
+ public void getConsumerMethodProperty() {
+ String result = nacosDynamicConfigManager.getConsumerMethodProperty("serviceName", "methodName", "timeout");
+ Assert.assertEquals(DynamicHelper.DEFAULT_DYNAMIC_VALUE, result);
+ }
+}
\ No newline at end of file
diff --git a/config/config-nacos/src/test/resources/log4j.xml b/config/config-nacos/src/test/resources/log4j.xml
new file mode 100644
index 000000000..e95634f16
--- /dev/null
+++ b/config/config-nacos/src/test/resources/log4j.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config/config-zk/pom.xml b/config/config-zk/pom.xml
new file mode 100644
index 000000000..8701b83b0
--- /dev/null
+++ b/config/config-zk/pom.xml
@@ -0,0 +1,117 @@
+
+
+ 4.0.0
+
+
+ com.alipay.sofa
+ sofa-rpc-config
+ ${revision}
+
+
+ sofa-rpc-config-zk
+
+
+
+ com.alipay.sofa
+ sofa-rpc-log-common-tools
+
+
+ com.alipay.sofa
+ sofa-rpc-log
+
+
+ com.alipay.sofa
+ sofa-rpc-api
+
+
+
+ org.apache.curator
+ curator-framework
+
+
+ org.apache.curator
+ curator-x-discovery
+
+
+ org.apache.zookeeper
+ zookeeper
+
+
+
+ org.slf4j
+ slf4j-log4j12
+ test
+
+
+ junit
+ junit
+ test
+
+
+
+
+
+
+ src/main/java
+
+
+ src/main/resources
+ false
+
+ **/**
+
+
+
+ src/test/java
+
+
+ src/test/resources
+ false
+
+ **/**
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ ${maven.compiler.source}
+ ${maven.compiler.source}
+ ${project.build.sourceEncoding}
+
+
+
+ org.apache.maven.plugins
+ maven-install-plugin
+
+ ${module.install.skip}
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+
+ ${module.deploy.skip}
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ ${skipTests}
+
+
+ **/*Test.java
+
+
+ once
+
+
+
+
+
diff --git a/config/config-zk/src/main/java/com/alipay/sofa/rpc/dynamic/zk/ZookeeperDynamicConfigManager.java b/config/config-zk/src/main/java/com/alipay/sofa/rpc/dynamic/zk/ZookeeperDynamicConfigManager.java
new file mode 100644
index 000000000..b1539eeb0
--- /dev/null
+++ b/config/config-zk/src/main/java/com/alipay/sofa/rpc/dynamic/zk/ZookeeperDynamicConfigManager.java
@@ -0,0 +1,231 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.dynamic.zk;
+
+import com.alipay.sofa.common.config.SofaConfigs;
+import com.alipay.sofa.rpc.auth.AuthRuleGroup;
+import com.alipay.sofa.rpc.common.RpcConstants;
+import com.alipay.sofa.rpc.common.utils.StringUtils;
+import com.alipay.sofa.rpc.dynamic.ConfigChangeType;
+import com.alipay.sofa.rpc.dynamic.ConfigChangedEvent;
+import com.alipay.sofa.rpc.dynamic.DynamicConfigKeyHelper;
+import com.alipay.sofa.rpc.dynamic.DynamicConfigKeys;
+import com.alipay.sofa.rpc.dynamic.DynamicConfigManager;
+import com.alipay.sofa.rpc.dynamic.DynamicHelper;
+import com.alipay.sofa.rpc.ext.Extension;
+import com.alipay.sofa.rpc.listener.ConfigListener;
+import com.alipay.sofa.rpc.log.Logger;
+import com.alipay.sofa.rpc.log.LoggerFactory;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.framework.recipes.cache.*;
+import org.apache.curator.retry.ExponentialBackoffRetry;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import static com.alipay.sofa.common.config.SofaConfigs.getOrDefault;
+import static com.alipay.sofa.rpc.common.utils.StringUtils.CONTEXT_SEP;
+
+/**
+ * @author Narziss
+ * @version ZookeeperDynamicConfigManager.java, v 0.1 2024年07月20日 09:23 Narziss
+ */
+
+@Extension(value = "zookeeper", override = true)
+public class ZookeeperDynamicConfigManager extends DynamicConfigManager {
+
+ private final static Logger LOGGER = LoggerFactory
+ .getLogger(ZookeeperDynamicConfigManager.class);
+
+ private final CuratorFramework zkClient;
+
+ private final String rootPath;
+ private final ConcurrentMap watchListenerMap = new ConcurrentHashMap<>();
+ private final ConcurrentMap configMap = new ConcurrentHashMap<>();
+
+ protected ZookeeperDynamicConfigManager(String appName) {
+ super(appName, SofaConfigs.getOrCustomDefault(DynamicConfigKeys.CONFIG_CENTER_ADDRESS, "zookeeper://127.0.0.1:2181"));
+ rootPath = CONTEXT_SEP + DynamicConfigKeys.CONFIG_NODE + CONTEXT_SEP + appName;
+ zkClient = CuratorFrameworkFactory.builder()
+ .connectString(getDynamicUrl().getAddress())
+ .retryPolicy(new ExponentialBackoffRetry(1000, 3))
+ .namespace(DynamicConfigKeys.DEFAULT_NAMESPACE)
+ .build();
+ zkClient.start();
+
+ if (!getOrDefault(DynamicConfigKeys.DYNAMIC_REFRESH_ENABLE)) {
+ PathChildrenCache cache = new PathChildrenCache(zkClient, rootPath, true);
+ cache.getListenable().addListener(new PathChildrenCacheListener() {
+ @Override
+ public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
+ switch (event.getType()) {
+ case CHILD_ADDED:
+ case CHILD_UPDATED:
+ String key = event.getData().getPath().substring(rootPath.length() + 1);
+ String value = new String(event.getData().getData());
+ configMap.put(key, value);
+ LOGGER.info("Receive zookeeper event: " + "type=[" + event.getType() + "] key=[" + key + "] value=[" + value + "]");
+ break;
+ case CHILD_REMOVED:
+ key = event.getData().getPath().substring(rootPath.length() + 1);
+ configMap.remove(key);
+ LOGGER.info("Receive zookeeper event: " + "type=[" + event.getType() + "] key=[" + key + "]");
+ break;
+ default:
+ break;
+ }
+ }
+ });
+ try {
+ cache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
+ } catch (Exception e) {
+ LOGGER.error("setupPathChildrenCache error", e);
+ }
+ }
+ }
+
+ @Override
+ public void initServiceConfiguration(String service) {
+ // TODO 暂不支持
+ }
+
+ @Override
+ public void initServiceConfiguration(String service, ConfigListener listener) {
+ try {
+ String path = rootPath + CONTEXT_SEP + service;
+ if (zkClient.checkExists().forPath(path) != null) {
+ byte[] bytes = zkClient.getData().forPath(rootPath + CONTEXT_SEP + service);
+ String rawConfig = new String(bytes, RpcConstants.DEFAULT_CHARSET);
+ if (!StringUtils.isEmpty(rawConfig)) {
+ listener.process(new ConfigChangedEvent(service, rawConfig));
+ }
+ }
+ } catch (Exception e) {
+ LOGGER.error("Failed to init service configuration for service: " + service, e);
+ }
+
+ }
+
+ @Override
+ public String getProviderServiceProperty(String service, String key) {
+ try {
+ String configValue = configMap.get(DynamicConfigKeyHelper.buildProviderServiceProKey(service, key));
+ return configValue != null ? configValue : DynamicHelper.DEFAULT_DYNAMIC_VALUE;
+ } catch (Exception e) {
+ return DynamicHelper.DEFAULT_DYNAMIC_VALUE;
+ }
+ }
+
+ @Override
+ public String getConsumerServiceProperty(String service, String key) {
+ try {
+ String configValue = configMap.get(DynamicConfigKeyHelper.buildConsumerServiceProKey(service, key));
+ return configValue != null ? configValue : DynamicHelper.DEFAULT_DYNAMIC_VALUE;
+ } catch (Exception e) {
+ return DynamicHelper.DEFAULT_DYNAMIC_VALUE;
+ }
+ }
+
+ @Override
+ public String getProviderMethodProperty(String service, String method, String key) {
+ try {
+ String configValue = configMap.get(DynamicConfigKeyHelper.buildProviderMethodProKey(service, method, key));
+ return configValue != null ? configValue : DynamicHelper.DEFAULT_DYNAMIC_VALUE;
+ } catch (Exception e) {
+ return DynamicHelper.DEFAULT_DYNAMIC_VALUE;
+ }
+ }
+
+ @Override
+ public String getConsumerMethodProperty(String service, String method, String key) {
+ try {
+ String configValue = configMap.get(DynamicConfigKeyHelper.buildConsumerMethodProKey(service, method, key));
+ return configValue != null ? configValue : DynamicHelper.DEFAULT_DYNAMIC_VALUE;
+ } catch (Exception e) {
+ return DynamicHelper.DEFAULT_DYNAMIC_VALUE;
+ }
+
+ }
+
+ @Override
+ public AuthRuleGroup getServiceAuthRule(String service) {
+ //TODO 暂不支持
+ return null;
+ }
+
+ @Override
+ public void addListener(String key, ConfigListener listener) {
+ String pathKey = rootPath + CONTEXT_SEP + key;
+
+ ZookeeperConfigListener zookeeperConfigListener = watchListenerMap.computeIfAbsent(
+ key, k -> createTargetListener(pathKey));
+
+ zookeeperConfigListener.addListener(listener);
+ }
+
+ private ZookeeperConfigListener createTargetListener(String pathKey) {
+ ZookeeperConfigListener configListener = new ZookeeperConfigListener(pathKey);
+ return configListener;
+ }
+
+ public class ZookeeperConfigListener implements NodeCacheListener {
+
+ private final String pathKey;
+ private final Set listeners = new CopyOnWriteArraySet<>();
+ private final NodeCache nodeCache;
+
+ public ZookeeperConfigListener(String pathKey) {
+ this.pathKey = pathKey;
+ this.nodeCache = new NodeCache(zkClient, pathKey);
+ nodeCache.getListenable().addListener(this);
+ try {
+ nodeCache.start();
+ } catch (Exception e) {
+ LOGGER.error("Failed to add listener for path:{}", pathKey, e);
+ }
+ }
+
+ public void addListener(ConfigListener configListener) {
+ listeners.add(configListener);
+ }
+
+ @Override
+ public void nodeChanged() throws Exception {
+ ChildData childData = nodeCache.getCurrentData();
+ String content = null;
+ ConfigChangeType changeType;
+ if (childData == null) {
+ changeType = ConfigChangeType.DELETED;
+
+ } else if (childData.getStat().getVersion() == 0) {
+ content = new String(childData.getData(), RpcConstants.DEFAULT_CHARSET);
+ changeType = ConfigChangeType.ADDED;
+ } else {
+ content = new String(childData.getData(), RpcConstants.DEFAULT_CHARSET);
+ changeType = ConfigChangeType.MODIFIED;
+ }
+ ConfigChangedEvent configChangeEvent = new ConfigChangedEvent(pathKey, (String) content, changeType);
+ listeners.forEach(listener -> listener.process(configChangeEvent));
+
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/config/config-zk/src/main/resources/META-INF/services/sofa-rpc/com.alipay.sofa.rpc.dynamic.DynamicConfigManager b/config/config-zk/src/main/resources/META-INF/services/sofa-rpc/com.alipay.sofa.rpc.dynamic.DynamicConfigManager
new file mode 100644
index 000000000..5f9a8243e
--- /dev/null
+++ b/config/config-zk/src/main/resources/META-INF/services/sofa-rpc/com.alipay.sofa.rpc.dynamic.DynamicConfigManager
@@ -0,0 +1 @@
+zookeeper=com.alipay.sofa.rpc.dynamic.zk.ZookeeperDynamicConfigManager
diff --git a/config/config-zk/src/test/java/com/alipay/sofa/rpc/dynamic/zk/ZookeeperDynamicConfigManagerTest.java b/config/config-zk/src/test/java/com/alipay/sofa/rpc/dynamic/zk/ZookeeperDynamicConfigManagerTest.java
new file mode 100644
index 000000000..9ccc3bf9f
--- /dev/null
+++ b/config/config-zk/src/test/java/com/alipay/sofa/rpc/dynamic/zk/ZookeeperDynamicConfigManagerTest.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.dynamic.zk;
+
+import com.alipay.sofa.rpc.dynamic.DynamicConfigManager;
+import com.alipay.sofa.rpc.dynamic.DynamicConfigManagerFactory;
+import com.alipay.sofa.rpc.dynamic.DynamicHelper;
+import com.alipay.sofa.rpc.log.Logger;
+import com.alipay.sofa.rpc.log.LoggerFactory;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ZookeeperDynamicConfigManagerTest {
+
+ private final static Logger logger = LoggerFactory
+ .getLogger(ZookeeperDynamicConfigManager.class);
+
+ private DynamicConfigManager zookeeperDynamicConfigManager = DynamicConfigManagerFactory.getDynamicManager(
+ "test", "zookeeper");
+
+ @Test
+ public void getProviderServiceProperty() {
+ String result = zookeeperDynamicConfigManager.getProviderServiceProperty("serviceName", "timeout");
+ Assert.assertEquals(DynamicHelper.DEFAULT_DYNAMIC_VALUE, result);
+ }
+
+ @Test
+ public void getConsumerServiceProperty() {
+ String result = zookeeperDynamicConfigManager.getConsumerServiceProperty("serviceName", "timeout");
+ Assert.assertEquals(DynamicHelper.DEFAULT_DYNAMIC_VALUE, result);
+ }
+
+ @Test
+ public void getProviderMethodProperty() {
+ String result = zookeeperDynamicConfigManager.getProviderMethodProperty("serviceName", "methodName", "timeout");
+ Assert.assertEquals(DynamicHelper.DEFAULT_DYNAMIC_VALUE, result);
+ }
+
+ @Test
+ public void getConsumerMethodProperty() {
+ String result = zookeeperDynamicConfigManager.getConsumerMethodProperty("serviceName", "methodName", "timeout");
+ Assert.assertEquals(DynamicHelper.DEFAULT_DYNAMIC_VALUE, result);
+ }
+}
\ No newline at end of file
diff --git a/config/config-zk/src/test/resources/log4j.xml b/config/config-zk/src/test/resources/log4j.xml
new file mode 100644
index 000000000..e95634f16
--- /dev/null
+++ b/config/config-zk/src/test/resources/log4j.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config/pom.xml b/config/pom.xml
index 7d0caacb8..2e380e67e 100644
--- a/config/pom.xml
+++ b/config/pom.xml
@@ -15,6 +15,8 @@
config-apollo
+ config-zk
+ config-nacos
diff --git a/core-impl/client/src/main/java/com/alipay/sofa/rpc/client/AbstractCluster.java b/core-impl/client/src/main/java/com/alipay/sofa/rpc/client/AbstractCluster.java
index 33fc4cc2a..81d4cfa40 100644
--- a/core-impl/client/src/main/java/com/alipay/sofa/rpc/client/AbstractCluster.java
+++ b/core-impl/client/src/main/java/com/alipay/sofa/rpc/client/AbstractCluster.java
@@ -210,19 +210,22 @@ public void updateProviders(ProviderGroup providerGroup) {
checkProviderInfo(providerGroup);
ProviderGroup oldProviderGroup = addressHolder.getProviderGroup(providerGroup.getName());
if (ProviderHelper.isEmpty(providerGroup)) {
+ boolean previouslyEmpty = ProviderHelper.isEmpty(oldProviderGroup);
addressHolder.updateProviders(providerGroup);
- if (!ProviderHelper.isEmpty(oldProviderGroup)) {
+ if (!previouslyEmpty) {
if (LOGGER.isWarnEnabled(consumerConfig.getAppName())) {
LOGGER.warnWithApp(consumerConfig.getAppName(), "Provider list is emptied, may be all " +
"providers has been closed, or this consumer has been add to blacklist");
- closeTransports();
}
+ closeTransports();
}
} else {
addressHolder.updateProviders(providerGroup);
connectionHolder.updateProviders(providerGroup);
}
if (EventBus.isEnable(ProviderInfoUpdateEvent.class)) {
+ // see: https://github.com/sofastack/sofa-rpc/issues/1442
+ // there is a swallow copy problem which makes the oldProviderGroup same as providerGroup
ProviderInfoUpdateEvent event = new ProviderInfoUpdateEvent(consumerConfig, oldProviderGroup, providerGroup);
EventBus.post(event);
}
@@ -686,7 +689,7 @@ private SofaResponse buildEmptyResponse(SofaRequest request) {
* @return 调用超时
*/
private int resolveTimeout(SofaRequest request, ConsumerConfig consumerConfig, ProviderInfo providerInfo) {
- // 动态配置优先
+ // 请求级别动态配置优先
final String dynamicAlias = consumerConfig.getParameter(DynamicConfigKeys.DYNAMIC_ALIAS);
if (StringUtils.isNotBlank(dynamicAlias)) {
String dynamicTimeout = null;
@@ -704,6 +707,7 @@ private int resolveTimeout(SofaRequest request, ConsumerConfig consumerConfig, P
return Integer.parseInt(dynamicTimeout);
}
}
+
// 先去调用级别配置
Integer timeout = request.getTimeout();
if (timeout == null || timeout <= 0) {
diff --git a/core-impl/client/src/main/java/com/alipay/sofa/rpc/client/AllConnectConnectionHolder.java b/core-impl/client/src/main/java/com/alipay/sofa/rpc/client/AllConnectConnectionHolder.java
index 8f9a2522b..856ded84d 100644
--- a/core-impl/client/src/main/java/com/alipay/sofa/rpc/client/AllConnectConnectionHolder.java
+++ b/core-impl/client/src/main/java/com/alipay/sofa/rpc/client/AllConnectConnectionHolder.java
@@ -554,13 +554,16 @@ public ClientTransport getAvailableClientTransport(ProviderInfo providerInfo) {
transport = uninitializedConnections.get(providerInfo);
if (transport != null) {
// 未初始化则初始化
- synchronized (this) {
+ providerLock.lock();
+ try {
transport = uninitializedConnections.get(providerInfo);
if (transport != null) {
initClientTransport(consumerConfig.getInterfaceId(), providerInfo, transport);
uninitializedConnections.remove(providerInfo);
}
return getAvailableClientTransport(providerInfo);
+ } finally {
+ providerLock.unlock();
}
}
diff --git a/core-impl/client/src/main/java/com/alipay/sofa/rpc/client/lb/AutoLoadBalancer.java b/core-impl/client/src/main/java/com/alipay/sofa/rpc/client/lb/AutoLoadBalancer.java
index 9bf01c75e..27d92a90d 100644
--- a/core-impl/client/src/main/java/com/alipay/sofa/rpc/client/lb/AutoLoadBalancer.java
+++ b/core-impl/client/src/main/java/com/alipay/sofa/rpc/client/lb/AutoLoadBalancer.java
@@ -53,7 +53,7 @@ public AutoLoadBalancer(ConsumerBootstrap consumerBootstrap) {
@Override
protected ProviderInfo doSelect(SofaRequest request, List providerInfos) {
- // 动态配置优先
+ // 请求级别动态配置优先
final String dynamicAlias = consumerConfig.getParameter(DynamicConfigKeys.DYNAMIC_ALIAS);
if (StringUtils.isNotBlank(dynamicAlias)) {
String dynamicLoadBalancer = null;
diff --git a/core-impl/client/src/test/java/com/alipay/sofa/rpc/client/ClusterProviderUpdateTest.java b/core-impl/client/src/test/java/com/alipay/sofa/rpc/client/ClusterProviderUpdateTest.java
new file mode 100644
index 000000000..240fb73e3
--- /dev/null
+++ b/core-impl/client/src/test/java/com/alipay/sofa/rpc/client/ClusterProviderUpdateTest.java
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.client;
+
+import com.alipay.sofa.rpc.bootstrap.ConsumerBootstrap;
+import com.alipay.sofa.rpc.config.ConsumerConfig;
+import com.alipay.sofa.rpc.core.exception.SofaRpcException;
+import com.alipay.sofa.rpc.core.request.SofaRequest;
+import com.alipay.sofa.rpc.core.response.SofaResponse;
+import com.alipay.sofa.rpc.transport.ClientTransport;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ *
+ * @author junyuan
+ * @version ClusterProviderUpdateTest.java, v 0.1 2024-10-11 11:04 junyuan Exp $
+ */
+public class ClusterProviderUpdateTest {
+ private static final AbstractCluster cluster;
+
+ static {
+ ConsumerBootstrap bootstrap = new ConsumerBootstrap(new ConsumerConfig()) {
+ @Override
+ public Object refer() {
+ return null;
+ }
+
+ @Override
+ public void unRefer() {
+
+ }
+
+ @Override
+ public Object getProxyIns() {
+ return null;
+ }
+
+ @Override
+ public Cluster getCluster() {
+ return null;
+ }
+
+ @Override
+ public List subscribe() {
+ return null;
+ }
+
+ @Override
+ public boolean isSubscribed() {
+ return false;
+ }
+ };
+
+ cluster = new AbstractCluster(bootstrap) {
+ @Override
+ protected SofaResponse doInvoke(SofaRequest msg) throws SofaRpcException {
+ return null;
+ }
+ };
+
+ cluster.addressHolder = new SingleGroupAddressHolder(null);
+ cluster.connectionHolder = new TestUseConnectionHolder(null);
+ }
+
+ @Test
+ public void testUpdateProvider() {
+ String groupName = "testUpdateProvider-Group";
+ List providerList = Arrays.asList(
+ ProviderHelper.toProviderInfo("127.0.0.1:12200"),
+ ProviderHelper.toProviderInfo("127.0.0.1:12201"),
+ ProviderHelper.toProviderInfo("127.0.0.1:12202"));
+ ProviderGroup g = new ProviderGroup(groupName, providerList);
+ cluster.updateProviders(g);
+
+ Assert.assertEquals(cluster.currentProviderList().size(), providerList.size());
+
+ cluster.updateProviders(new ProviderGroup(groupName, null));
+
+ Assert.assertTrue(cluster.getAddressHolder().getProviderGroup(groupName).isEmpty());
+ Assert.assertEquals( 1, ((TestUseConnectionHolder)cluster.connectionHolder).calledCloseAllClientTransports.get());
+ }
+
+
+ private static class TestUseConnectionHolder extends ConnectionHolder {
+ Set connections = new HashSet<>();
+
+ AtomicInteger calledCloseAllClientTransports = new AtomicInteger();
+
+ /**
+ * 构造函数
+ *
+ * @param consumerBootstrap 服务消费者配置
+ */
+ protected TestUseConnectionHolder(ConsumerBootstrap consumerBootstrap) {
+ super(consumerBootstrap);
+ }
+
+ @Override
+ public void closeAllClientTransports(DestroyHook destroyHook) {
+ calledCloseAllClientTransports.getAndIncrement();
+ }
+
+ @Override
+ public ConcurrentMap getAvailableConnections() {
+ return null;
+ }
+
+ @Override
+ public List getAvailableProviders() {
+ return null;
+ }
+
+ @Override
+ public ClientTransport getAvailableClientTransport(ProviderInfo providerInfo) {
+ return null;
+ }
+
+ @Override
+ public boolean isAvailableEmpty() {
+ return false;
+ }
+
+ @Override
+ public Collection currentProviderList() {
+ return null;
+ }
+
+ @Override
+ public void setUnavailable(ProviderInfo providerInfo, ClientTransport transport) {
+
+ }
+
+ @Override
+ public void addProvider(ProviderGroup providerGroup) {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public void removeProvider(ProviderGroup providerGroup) {
+ throw new UnsupportedOperationException("not implemented");
+
+ }
+
+ @Override
+ public void updateProviders(ProviderGroup providerGroup) {
+ for (ProviderInfo i : providerGroup.getProviderInfos()) {
+ connections.add(i);
+ }
+ }
+
+ @Override
+ public void updateAllProviders(List providerGroups) {
+
+ }
+
+ @Override
+ public void destroy() {
+
+ }
+
+ @Override
+ public void destroy(DestroyHook hook) {
+
+ }
+
+ @Override
+ public void init() {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/core-impl/filter/src/main/java/com/alipay/sofa/rpc/filter/BeanIdMatchFilter.java b/core-impl/filter/src/main/java/com/alipay/sofa/rpc/filter/BeanIdMatchFilter.java
index c4c5214b1..40b14b715 100644
--- a/core-impl/filter/src/main/java/com/alipay/sofa/rpc/filter/BeanIdMatchFilter.java
+++ b/core-impl/filter/src/main/java/com/alipay/sofa/rpc/filter/BeanIdMatchFilter.java
@@ -21,6 +21,8 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
/**
* 规则id的配置形式:a,b,!c,d
@@ -49,18 +51,21 @@ public abstract class BeanIdMatchFilter extends Filter {
private List excludeId;
private volatile boolean formatComplete;
- private final Object formatLock = new Object();
+ protected final Lock lock = new ReentrantLock();
@Override
public boolean needToLoad(FilterInvoker invoker) {
AbstractInterfaceConfig config = invoker.config;
String invokerId = config.getId();
if (!formatComplete) {
- synchronized (formatLock) {
+ lock.lock();
+ try {
if (!formatComplete) {
formatId(idRule);
formatComplete = true;
}
+ } finally {
+ lock.unlock();
}
}
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/client/RouterChain.java b/core/api/src/main/java/com/alipay/sofa/rpc/client/RouterChain.java
index 41eeb5a69..e841d99ab 100644
--- a/core/api/src/main/java/com/alipay/sofa/rpc/client/RouterChain.java
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/client/RouterChain.java
@@ -143,7 +143,7 @@ public static RouterChain buildConsumerChain(ConsumerBootstrap consumerBootstrap
List> extensionRouters = new ArrayList>();
List routerAliases = consumerConfig.getRouter();
if (CommonUtils.isNotEmpty(routerAliases)) {
- for (String routerAlias : routerAliases) {
+ routerAliases.stream().distinct().forEach(routerAlias -> {
if (startsWithExcludePrefix(routerAlias)) { // 排除用的特殊字符
excludes.add(routerAlias.substring(1));
} else {
@@ -152,7 +152,7 @@ public static RouterChain buildConsumerChain(ConsumerBootstrap consumerBootstrap
extensionRouters.add(extensionRouter);
}
}
- }
+ });
}
// 解析自动加载的router
if (!excludes.contains(StringUtils.ALL) && !excludes.contains(StringUtils.DEFAULT)) { // 配了-*和-default表示不加载内置
@@ -162,7 +162,6 @@ public static RouterChain buildConsumerChain(ConsumerBootstrap consumerBootstrap
}
}
}
- excludes = null; // 不需要了
// 按order从小到大排序
if (extensionRouters.size() > 1) {
extensionRouters.sort(Comparator.comparingInt(ExtensionClass::getOrder));
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/codec/AbstractSerializer.java b/core/api/src/main/java/com/alipay/sofa/rpc/codec/AbstractSerializer.java
index 0b9ebc0eb..3c3a0b378 100644
--- a/core/api/src/main/java/com/alipay/sofa/rpc/codec/AbstractSerializer.java
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/codec/AbstractSerializer.java
@@ -33,6 +33,8 @@ public abstract class AbstractSerializer implements Serializer {
protected static Map genericServiceMap = new ConcurrentHashMap<>();
+ protected Map customSerializers = new ConcurrentHashMap<>();
+
protected SofaRpcException buildSerializeError(String message) {
return new SofaRpcException(getErrorCode(true), LogCodes.getLog(LogCodes.ERROR_SERIALIZER, message));
}
@@ -73,4 +75,19 @@ public static void registerGenericService(String serviceName, String className)
public static void clear() {
genericServiceMap.clear();
}
+
+ protected CustomSerializer getObjCustomSerializer(Object obj) {
+ if (obj == null) {
+ return null;
+ }
+ return getCustomSerializer(obj.getClass());
+ }
+
+ protected CustomSerializer getCustomSerializer(Class clazz) {
+ return customSerializers.get(clazz);
+ }
+
+ public void addCustomSerializer(Class clazz, CustomSerializer serializerManager) {
+ customSerializers.put(clazz, serializerManager);
+ }
}
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/codec/CustomSerializer.java b/core/api/src/main/java/com/alipay/sofa/rpc/codec/CustomSerializer.java
new file mode 100644
index 000000000..42a4f6398
--- /dev/null
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/codec/CustomSerializer.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.codec;
+
+import com.alipay.sofa.rpc.core.exception.SofaRpcException;
+import com.alipay.sofa.rpc.transport.AbstractByteBuf;
+
+import java.util.Map;
+
+/**
+ * @author leizhiyuan
+ */
+public interface CustomSerializer {
+
+ /**
+ * 序列化
+ *
+ * @param object 对象
+ * @param context 上下文
+ * @return 序列化后的对象
+ * @throws SofaRpcException 序列化异常
+ */
+ public AbstractByteBuf encodeObject(T object, Map context) throws SofaRpcException;
+
+ /**
+ * 反序列化,只有类型,返回对象
+ *
+ * @param data 原始字节数组
+ * @param context 上下文
+ * @return 反序列化后的对象
+ * @throws SofaRpcException 序列化异常
+ */
+ public T decodeObject(AbstractByteBuf data, Map context) throws SofaRpcException;
+
+ /**
+ * 反序列化,已有数据,填充字段
+ *
+ * @param data 原始字节数组
+ * @param template 模板对象
+ * @param context 上下文
+ * @throws SofaRpcException 序列化异常
+ */
+ public void decodeObjectByTemplate(AbstractByteBuf data, Map context, T template)
+ throws SofaRpcException;
+
+}
\ No newline at end of file
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/common/RpcConstants.java b/core/api/src/main/java/com/alipay/sofa/rpc/common/RpcConstants.java
index b09cdfe7f..0f25bb8b4 100644
--- a/core/api/src/main/java/com/alipay/sofa/rpc/common/RpcConstants.java
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/common/RpcConstants.java
@@ -121,6 +121,22 @@ public class RpcConstants {
* 调用方式:future
*/
public static final String INVOKER_TYPE_FUTURE = "future";
+ /**
+ * 调用方式:一元调用
+ */
+ public static final String INVOKER_TYPE_UNARY = "unary";
+ /**
+ * 调用方式:客户端流
+ */
+ public static final String INVOKER_TYPE_CLIENT_STREAMING = "client_stream";
+ /**
+ * 调用方式:服务端流
+ */
+ public static final String INVOKER_TYPE_SERVER_STREAMING = "server_stream";
+ /**
+ * 调用方式:双向流
+ */
+ public static final String INVOKER_TYPE_BI_STREAMING = "bi_stream";
/**
* Hessian序列化 [不推荐]
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/common/RpcOptions.java b/core/api/src/main/java/com/alipay/sofa/rpc/common/RpcOptions.java
index c083625cc..2abdcdfc8 100644
--- a/core/api/src/main/java/com/alipay/sofa/rpc/common/RpcOptions.java
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/common/RpcOptions.java
@@ -122,6 +122,10 @@ public class RpcOptions {
* 默认Tracer实现
*/
public static final String DEFAULT_TRACER = "default.tracer";
+ /**
+ * 默认filter实现
+ */
+ public static final String DEFAULT_FILTERS = "default.filters";
/**
* 注册中心发现服务(保存注册中心地址的服务)的地址
@@ -376,6 +380,13 @@ public class RpcOptions {
*/
public static final String CONSUMER_REJECTED_EXECUTION_POLICY = "consumer.rejected.execution.policy";
+ /**
+ * 需要解析的 routers
+ *
+ * @since 5.13.0
+ */
+ public static final String CONSUMER_ROUTERS = "consumer.routers";
+
/**
* 默认回调线程池最小
*/
@@ -408,6 +419,7 @@ public class RpcOptions {
* 默认 grpc maxInboundMessageSize大小
*/
public static final String TRANSPORT_GRPC_MAX_INBOUND_MESSAGE_SIZE = "transport.grpc.maxInboundMessageSize";
+
/**
* 最大IO的buffer大小
*/
@@ -518,6 +530,7 @@ public class RpcOptions {
/**
* Whether to close lookout collection.
*/
+ @Deprecated
public static final String LOOKOUT_COLLECT_DISABLE = "lookout.collect.disable";
/**
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/common/Version.java b/core/api/src/main/java/com/alipay/sofa/rpc/common/Version.java
index f64663161..388f0ce58 100644
--- a/core/api/src/main/java/com/alipay/sofa/rpc/common/Version.java
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/common/Version.java
@@ -27,16 +27,16 @@ public final class Version {
/**
* 当前RPC版本,例如:5.6.7
*/
- public static final String VERSION = "5.11.0";
+ public static final String VERSION = "5.14.0";
/**
* 当前RPC版本,例如: 5.6.7 对应 50607
*/
- public static final int RPC_VERSION = 51100;
+ public static final int RPC_VERSION = 51400;
/**
* 当前Build版本,每次发布修改
*/
- public static final String BUILD_VERSION = "5.11.0_20230720235253";
+ public static final String BUILD_VERSION = "5.14.0_20241101180137";
}
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/common/threadpool/SofaExecutorFactory.java b/core/api/src/main/java/com/alipay/sofa/rpc/common/threadpool/SofaExecutorFactory.java
new file mode 100644
index 000000000..847fe4d39
--- /dev/null
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/common/threadpool/SofaExecutorFactory.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.common.threadpool;
+
+import com.alipay.sofa.rpc.config.ServerConfig;
+import com.alipay.sofa.rpc.ext.Extensible;
+
+import java.util.concurrent.Executor;
+
+/**
+ *
+ * @author junyuan
+ * @version SofaExecutorFactory.java, v 0.1 2023年12月13日 20:02 junyuan Exp $
+ */
+@Extensible
+public interface SofaExecutorFactory {
+
+ Executor createExecutor(String namePrefix, ServerConfig serverConfig);
+}
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/common/threadpool/ThreadPoolConstant.java b/core/api/src/main/java/com/alipay/sofa/rpc/common/threadpool/ThreadPoolConstant.java
new file mode 100644
index 000000000..5a040640b
--- /dev/null
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/common/threadpool/ThreadPoolConstant.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.common.threadpool;
+
+/**
+ *
+ * @author junyuan
+ * @version ThreadPoolConstant.java, v 0.1 2023年12月12日 14:01 junyuan Exp $
+ */
+public class ThreadPoolConstant {
+ public static final String DEFAULT_THREAD_NAME_PREFIX = "SOFA-RPC-DEFAULT";
+ public static final String BIZ_THREAD_NAME_PREFIX = "SEV-BOLT-BIZ-";
+
+ public static final String TYPE_PREFIX_TREAD = "T";
+
+ public static final String TYPE_PREFIX_VIRTUAL_TREAD = "VT";
+
+}
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/common/threadpool/extension/CachedThreadPoolFactory.java b/core/api/src/main/java/com/alipay/sofa/rpc/common/threadpool/extension/CachedThreadPoolFactory.java
new file mode 100644
index 000000000..06a05223a
--- /dev/null
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/common/threadpool/extension/CachedThreadPoolFactory.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.common.threadpool.extension;
+
+import com.alipay.sofa.rpc.common.struct.NamedThreadFactory;
+import com.alipay.sofa.rpc.common.threadpool.SofaExecutorFactory;
+import com.alipay.sofa.rpc.config.ServerConfig;
+import com.alipay.sofa.rpc.ext.Extension;
+import com.alipay.sofa.rpc.server.BusinessPool;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ *
+ * @author junyuan
+ * @version DefaultThreadPoolFactory.java, v 0.1 2023年12月11日 20:55 junyuan Exp $
+ */
+@Extension(value = "cached")
+public class CachedThreadPoolFactory implements SofaExecutorFactory {
+
+ @Override
+ public Executor createExecutor(String namePrefix, ServerConfig serverConfig) {
+ ThreadPoolExecutor executor = BusinessPool.initPool(serverConfig);
+ executor.setThreadFactory(new NamedThreadFactory(namePrefix, serverConfig.isDaemon()));
+ return executor;
+ }
+}
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/common/threadpool/extension/VirtualThreadPoolFactory.java b/core/api/src/main/java/com/alipay/sofa/rpc/common/threadpool/extension/VirtualThreadPoolFactory.java
new file mode 100644
index 000000000..a16e835b0
--- /dev/null
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/common/threadpool/extension/VirtualThreadPoolFactory.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.common.threadpool.extension;
+
+import com.alipay.sofa.rpc.common.threadpool.SofaExecutorFactory;
+import com.alipay.sofa.common.thread.virtual.SofaVirtualThreadFactory;
+import com.alipay.sofa.rpc.common.threadpool.ThreadPoolConstant;
+import com.alipay.sofa.rpc.config.ServerConfig;
+import com.alipay.sofa.rpc.ext.Extension;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ *
+ * @author junyuan
+ * @version VirtualThreadPoolFactory.java, v 0.1 2023年12月12日 11:11 junyuan Exp $
+ */
+@Extension(value = "virtual")
+public class VirtualThreadPoolFactory implements SofaExecutorFactory {
+
+ /**
+ * 系统全局线程池计数器
+ */
+ private static final AtomicInteger POOL_COUNT = new AtomicInteger();
+
+ @Override
+ public Executor createExecutor(String namePrefix, ServerConfig serverConfig) {
+ // virtual thread does not support any configs now
+ return SofaVirtualThreadFactory.ofExecutorService(buildNamePrefix(namePrefix));
+ }
+
+ /**
+ * refine virtual thread name
+ * SOFA-originInput-0-VT123
+ * @param namePrefix
+ * @return
+ */
+ private String buildNamePrefix(String namePrefix) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("SOFA-");
+ sb.append(namePrefix);
+ sb.append("-");
+ sb.append(POOL_COUNT.getAndIncrement());
+ sb.append("-");
+ sb.append(ThreadPoolConstant.TYPE_PREFIX_VIRTUAL_TREAD);
+ return sb.toString();
+ }
+}
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/config/AbstractInterfaceConfig.java b/core/api/src/main/java/com/alipay/sofa/rpc/config/AbstractInterfaceConfig.java
index 336292369..f3a236e09 100644
--- a/core/api/src/main/java/com/alipay/sofa/rpc/config/AbstractInterfaceConfig.java
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/config/AbstractInterfaceConfig.java
@@ -42,7 +42,9 @@
import java.util.concurrent.ConcurrentHashMap;
import static com.alipay.sofa.rpc.common.RpcConfigs.getBooleanValue;
+import static com.alipay.sofa.rpc.common.RpcConfigs.getListValue;
import static com.alipay.sofa.rpc.common.RpcConfigs.getStringValue;
+import static com.alipay.sofa.rpc.common.RpcOptions.DEFAULT_FILTERS;
import static com.alipay.sofa.rpc.common.RpcOptions.DEFAULT_GROUP;
import static com.alipay.sofa.rpc.common.RpcOptions.DEFAULT_PROXY;
import static com.alipay.sofa.rpc.common.RpcOptions.DEFAULT_SERIALIZATION;
@@ -106,7 +108,8 @@ public abstract class AbstractInterfaceConfig filter;
+ protected List filter = new ArrayList(
+ getListValue(DEFAULT_FILTERS));
/**
* 注册中心配置,可配置多个
@@ -212,6 +215,11 @@ public abstract class AbstractInterfaceConfig configValueCache = null;
+ /**
+ * 动态配置的方法名称和方法参数配置的map
+ */
+ protected transient volatile Map dynamicConfigValueCache = new ConcurrentHashMap<>();
+
/**
* 代理接口类,和T对应,主要针对泛化调用
*/
@@ -361,6 +369,18 @@ public S setFilter(List filter) {
return castThis();
}
+ /**
+ * add filter
+ *
+ * @param filter the add filter
+ */
+ public void addFilter(List filter) {
+ if(this.filter == null) {
+ filter = new ArrayList<>();
+ }
+ this.filter.addAll(filter);
+ }
+
/**
* Gets registry.
*
@@ -688,6 +708,15 @@ public Map getConfigValueCache() {
return configValueCache;
}
+ /**
+ * Gets dynamic config value cache.
+ *
+ * @return the dynamic config value cache
+ */
+ public Map getDynamicConfigValueCache() {
+ return dynamicConfigValueCache;
+ }
+
/**
* Sets config listener.
*
@@ -915,7 +944,7 @@ public boolean updateAttribute(String property, String newValueStr, boolean over
int index = methodAndP.indexOf(RpcConstants.HIDE_KEY_PREFIX);
if (index <= 0) {
throw ExceptionUtils.buildRuntime(property, newValueStr,
- "Unknown update attribute key!");
+ "Unknown update attribute key!");
}
String methodName = methodAndP.substring(0, index);
String methodProperty = methodAndP.substring(index + 1);
@@ -925,6 +954,9 @@ public boolean updateAttribute(String property, String newValueStr, boolean over
// 拿到旧的值
Object oldValue = null;
Object newValue = CompatibleTypeUtils.convert(newValueStr, propertyClazz);
+ if (dynamicConfigValueCache.containsKey(property)) {
+ dynamicConfigValueCache.put(property, newValue);
+ }
if (methodConfig == null) {
methodConfig = new MethodConfig();
methodConfig.setName(methodName);
@@ -947,7 +979,7 @@ public boolean updateAttribute(String property, String newValueStr, boolean over
BeanUtils.setProperty(methodConfig, methodProperty, propertyClazz, newValue);// 覆盖属性
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Property \"" + methodName + "." + methodProperty + "\" changed from {} to {}",
- oldValue, newValueStr);
+ oldValue, newValueStr);
}
}
} else { // 接口级配置 例如timeout
@@ -957,6 +989,9 @@ public boolean updateAttribute(String property, String newValueStr, boolean over
// 拿到旧的值
Object oldValue = BeanUtils.getProperty(this, property, propertyClazz);
Object newValue = CompatibleTypeUtils.convert(newValueStr, propertyClazz);
+ if (dynamicConfigValueCache.containsKey(property)) {
+ dynamicConfigValueCache.put(property, newValue);
+ }
if (oldValue == null) {
if (newValueStr != null) {
changed = true;
@@ -976,7 +1011,7 @@ public boolean updateAttribute(String property, String newValueStr, boolean over
throw e;
} catch (Exception e) {
throw new SofaRpcRuntimeException(LogCodes.getLog(LogCodes.ERROR_UPDATE_ATTRIBUTE, property, newValueStr),
- e);
+ e);
}
}
@@ -1001,11 +1036,17 @@ public Object getMethodConfigValue(String methodName, String configKey, Object d
* @return 配置值 method config value
*/
public Object getMethodConfigValue(String methodName, String configKey) {
- if (configValueCache == null) {
- return null;
- }
String key = buildmkey(methodName, configKey);
- return configValueCache.get(key);
+ Object value = null;
+ if (dynamicConfigValueCache != null) {
+ value = dynamicConfigValueCache.get(key);
+ }
+ if (value == null) {
+ if (configValueCache != null) {
+ value = configValueCache.get(key);
+ }
+ }
+ return value;
}
/**
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/config/ConsumerConfig.java b/core/api/src/main/java/com/alipay/sofa/rpc/config/ConsumerConfig.java
index 1b72bc901..2dfa5ef4c 100644
--- a/core/api/src/main/java/com/alipay/sofa/rpc/config/ConsumerConfig.java
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/config/ConsumerConfig.java
@@ -33,10 +33,12 @@
import java.io.Serializable;
import java.lang.reflect.Method;
+import java.util.ArrayList;
import java.util.List;
import static com.alipay.sofa.rpc.common.RpcConfigs.getBooleanValue;
import static com.alipay.sofa.rpc.common.RpcConfigs.getIntValue;
+import static com.alipay.sofa.rpc.common.RpcConfigs.getListValue;
import static com.alipay.sofa.rpc.common.RpcConfigs.getStringValue;
import static com.alipay.sofa.rpc.common.RpcOptions.CONSUMER_REJECTED_EXECUTION_POLICY;
import static com.alipay.sofa.rpc.common.RpcOptions.CONSUMER_ADDRESS_HOLDER;
@@ -56,6 +58,7 @@
import static com.alipay.sofa.rpc.common.RpcOptions.CONSUMER_RECONNECT_PERIOD;
import static com.alipay.sofa.rpc.common.RpcOptions.CONSUMER_REPEATED_REFERENCE_LIMIT;
import static com.alipay.sofa.rpc.common.RpcOptions.CONSUMER_RETRIES;
+import static com.alipay.sofa.rpc.common.RpcOptions.CONSUMER_ROUTERS;
import static com.alipay.sofa.rpc.common.RpcOptions.CONSUMER_STICKY;
import static com.alipay.sofa.rpc.common.RpcOptions.DEFAULT_PROTOCOL;
@@ -173,7 +176,8 @@ public class ConsumerConfig extends AbstractInterfaceConfig router;
+ protected List router = new ArrayList(
+ getListValue(CONSUMER_ROUTERS));
/**
* 路由规则引用,多个用英文逗号隔开。List
@@ -682,6 +686,18 @@ public ConsumerConfig setRouter(List router) {
return this;
}
+ /**
+ * Add router.
+ *
+ * @param router the add router
+ */
+ public void addRouter(List router) {
+ if (this.router == null) {
+ this.router = new ArrayList<>();
+ }
+ this.router.addAll(router);
+ }
+
/**
* Gets routerRef.
*
@@ -917,8 +933,11 @@ public int getMethodRetries(String methodName) {
* @return the time out
*/
public int getMethodTimeout(String methodName) {
- return (Integer) getMethodConfigValue(methodName, RpcConstants.CONFIG_KEY_TIMEOUT,
- getTimeout());
+ Object methodTimeout = getMethodConfigValue(methodName, RpcConstants.CONFIG_KEY_TIMEOUT);
+ if (methodTimeout == null || ((Integer) methodTimeout) == 0) {
+ return getTimeout();
+ }
+ return (Integer) methodTimeout;
}
/**
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/config/MethodConfig.java b/core/api/src/main/java/com/alipay/sofa/rpc/config/MethodConfig.java
index 611795c49..d8ab50aee 100644
--- a/core/api/src/main/java/com/alipay/sofa/rpc/config/MethodConfig.java
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/config/MethodConfig.java
@@ -137,7 +137,7 @@ public MethodConfig setParameters(Map parameters) {
* @return the timeout
*/
public Integer getTimeout() {
- return timeout;
+ return timeout == null ? 0 : timeout;
}
/**
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/config/RegistryConfig.java b/core/api/src/main/java/com/alipay/sofa/rpc/config/RegistryConfig.java
index b11533728..674a89fa6 100644
--- a/core/api/src/main/java/com/alipay/sofa/rpc/config/RegistryConfig.java
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/config/RegistryConfig.java
@@ -398,6 +398,36 @@ public String getParameter(String key) {
return parameters == null ? null : parameters.get(key);
}
+ /**
+ * Gets parameter or default.
+ *
+ * @param key the key
+ * @return the value
+ */
+ public String getParameter(String key, String defaultValue) {
+ return getParameter(key) == null ? defaultValue : getParameter(key);
+ }
+
+ /**
+ * Gets parameter or default.
+ *
+ * @param key the key
+ * @return the value
+ */
+ public int getParameter(String key, int defaultValue) {
+ return getParameter(key) == null ? defaultValue : Integer.parseInt(parameters.get(key));
+ }
+
+ /**
+ * Gets parameter or default.
+ *
+ * @param key the key
+ * @return the value
+ */
+ public boolean getParameter(String key, boolean defaultValue) {
+ return getParameter(key) == null ? defaultValue : Boolean.parseBoolean(parameters.get(key));
+ }
+
@Override
public String toString() {
return "RegistryConfig{" +
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/config/ServerConfig.java b/core/api/src/main/java/com/alipay/sofa/rpc/config/ServerConfig.java
index e48467249..f77f7c818 100644
--- a/core/api/src/main/java/com/alipay/sofa/rpc/config/ServerConfig.java
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/config/ServerConfig.java
@@ -16,7 +16,9 @@
*/
package com.alipay.sofa.rpc.config;
+import com.alipay.sofa.common.config.SofaConfigs;
import com.alipay.sofa.rpc.common.RpcConstants;
+import com.alipay.sofa.rpc.common.config.RpcConfigKeys;
import com.alipay.sofa.rpc.common.utils.ExceptionUtils;
import com.alipay.sofa.rpc.common.utils.NetUtils;
import com.alipay.sofa.rpc.common.utils.StringUtils;
@@ -96,7 +98,10 @@ public class ServerConfig extends AbstractIdConfig implements Serializable {
/**
* 线程池类型
*/
- protected String threadPoolType = getStringValue(SERVER_POOL_TYPE);
+ protected String threadPoolType = SofaConfigs
+ .getOrCustomDefault(
+ RpcConfigKeys.SERVER_THREAD_POOL_TYPE /* 优先读取环境变量 */
+ , getStringValue(SERVER_POOL_TYPE) /* 兜底读json配置文件 */);
/**
* 业务线程池大小
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/config/UserThreadPoolManager.java b/core/api/src/main/java/com/alipay/sofa/rpc/config/UserThreadPoolManager.java
index 6f044f986..0f667ca0f 100644
--- a/core/api/src/main/java/com/alipay/sofa/rpc/config/UserThreadPoolManager.java
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/config/UserThreadPoolManager.java
@@ -81,9 +81,7 @@ public static UserThreadPool getUserThread(String service) {
public static Set getUserThreadPoolSet() {
Set userThreadPoolSet = new HashSet<>();
if (hasUserThread()) {
- for (UserThreadPool userThreadPool : userThreadMap.values()) {
- userThreadPoolSet.add(userThreadPool);
- }
+ userThreadPoolSet.addAll(userThreadMap.values());
}
return userThreadPoolSet;
}
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/context/RecordContextResolver.java b/core/api/src/main/java/com/alipay/sofa/rpc/context/RecordContextResolver.java
new file mode 100644
index 000000000..479f29d68
--- /dev/null
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/context/RecordContextResolver.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.context;
+
+import com.alipay.sofa.common.insight.RecordContext;
+import com.alipay.sofa.rpc.common.RemotingConstants;
+import com.alipay.sofa.rpc.core.request.SofaRequest;
+
+import java.util.Map;
+
+/**
+ * @author Even
+ * @date 2024/4/29 21:03
+ */
+public class RecordContextResolver {
+
+ public static void carryWithRequest(RecordContext recordContext, SofaRequest sofaRequest) {
+ recordContext.setTargetServiceUniqueName(sofaRequest.getTargetServiceUniqueName());
+ recordContext.setMethodName(sofaRequest.getMethodName());
+ Object traceContext = sofaRequest.getRequestProp(RemotingConstants.RPC_TRACE_NAME);
+ if (traceContext instanceof Map) {
+ Map ctxMap = (Map) traceContext;
+ String traceId = ctxMap.get(RemotingConstants.TRACE_ID_KEY);
+ String rpcId = ctxMap.get(RemotingConstants.RPC_ID_KEY);
+ recordContext.setTraceId(traceId);
+ recordContext.setRpcId(rpcId);
+ }
+ }
+}
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/core/request/SofaRequest.java b/core/api/src/main/java/com/alipay/sofa/rpc/core/request/SofaRequest.java
index 1829ec7a4..aec722318 100644
--- a/core/api/src/main/java/com/alipay/sofa/rpc/core/request/SofaRequest.java
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/core/request/SofaRequest.java
@@ -306,7 +306,10 @@ public SofaRequest setData(AbstractByteBuf data) {
* @return 如果是Future和Callback,是异步请求
*/
public boolean isAsync() {
- return invokeType != null && (RpcConstants.INVOKER_TYPE_CALLBACK.equals(invokeType)
- || RpcConstants.INVOKER_TYPE_FUTURE.equals(invokeType));
+ return RpcConstants.INVOKER_TYPE_CALLBACK.equals(invokeType)
+ || RpcConstants.INVOKER_TYPE_FUTURE.equals(invokeType)
+ || RpcConstants.INVOKER_TYPE_BI_STREAMING.equals(invokeType)
+ || RpcConstants.INVOKER_TYPE_SERVER_STREAMING.equals(invokeType)
+ || RpcConstants.INVOKER_TYPE_CLIENT_STREAMING.equals(invokeType);
}
}
\ No newline at end of file
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/dynamic/ConfigChangeType.java b/core/api/src/main/java/com/alipay/sofa/rpc/dynamic/ConfigChangeType.java
new file mode 100644
index 000000000..99f87d457
--- /dev/null
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/dynamic/ConfigChangeType.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.dynamic;
+
+/**
+ * @author Narziss
+ * @version ConfigChangeType.java, v 0.1 2024年09月15日 20:20 Narziss
+ */
+
+public enum ConfigChangeType {
+ /**
+ * A config is created.
+ */
+ ADDED,
+
+ /**
+ * A config is updated.
+ */
+ MODIFIED,
+
+ /**
+ * A config is deleted.
+ */
+ DELETED
+}
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/dynamic/ConfigChangedEvent.java b/core/api/src/main/java/com/alipay/sofa/rpc/dynamic/ConfigChangedEvent.java
new file mode 100644
index 000000000..220f2889d
--- /dev/null
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/dynamic/ConfigChangedEvent.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.dynamic;
+
+import com.alipay.sofa.rpc.common.utils.StringUtils;
+import com.alipay.sofa.rpc.log.Logger;
+import com.alipay.sofa.rpc.log.LoggerFactory;
+
+import java.util.EventObject;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Narziss
+ * @version ConfigChangedEvent.java, v 0.1 2024年09月15日 20:12 Narziss
+ */
+
+public class ConfigChangedEvent extends EventObject {
+
+ private final static Logger LOGGER = LoggerFactory.getLogger(ConfigChangedEvent.class);
+
+ private final String key;
+
+ private final String content;
+
+ private final ConfigChangeType changeType;
+
+ private final Map dynamicValueMap = new HashMap<>();
+
+ public ConfigChangedEvent(String key, String content) {
+ this(key, content, ConfigChangeType.MODIFIED);
+ }
+
+ public ConfigChangedEvent(String key, String content, ConfigChangeType changeType) {
+ super(key);
+ this.key = key;
+ this.content = content;
+ this.changeType = changeType;
+ if (StringUtils.isNotBlank(content)) {
+ parseConfigurationLines(content);
+ }
+ }
+
+ private void parseConfigurationLines(String content) {
+ String[] lines = content.split(System.lineSeparator());
+ for (String line : lines) {
+ String[] keyValue = line.split("=", 2);
+ if (keyValue.length == 2) {
+ String mapKey = keyValue[0].trim();
+ String mapValue = keyValue[1].trim();
+ dynamicValueMap.put(mapKey, mapValue);
+ } else {
+ LOGGER.warn("Malformed configuration line: {}", line);
+ }
+ }
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public ConfigChangeType getChangeType() {
+ return changeType;
+ }
+
+ public Map getDynamicValueMap() {
+ return dynamicValueMap;
+ }
+
+}
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/dynamic/DynamicConfigKeys.java b/core/api/src/main/java/com/alipay/sofa/rpc/dynamic/DynamicConfigKeys.java
index d8c49dcec..44bb51901 100644
--- a/core/api/src/main/java/com/alipay/sofa/rpc/dynamic/DynamicConfigKeys.java
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/dynamic/DynamicConfigKeys.java
@@ -16,10 +16,33 @@
*/
package com.alipay.sofa.rpc.dynamic;
+import com.alipay.sofa.common.config.ConfigKey;
+
/**
* @author bystander
* @version : DynamicConfigKeys.java, v 0.1 2019年04月17日 21:51 bystander Exp $
*/
public class DynamicConfigKeys {
- public static final String DYNAMIC_ALIAS = "dynamicAlias";
+ public static final String DYNAMIC_ALIAS = "dynamicAlias";
+
+ public static final String CONFIG_NODE = "config";
+
+ public static final String DEFAULT_NAMESPACE = "sofa-rpc";
+
+ public static ConfigKey CONFIG_CENTER_ADDRESS = ConfigKey
+ .build(
+ "sofa.rpc.config.center.address",
+ " ",
+ false,
+ "The url of the dynamic configuration.",
+ new String[] { "sofa_rpc_config_center_address" });
+
+ public static ConfigKey DYNAMIC_REFRESH_ENABLE = ConfigKey
+ .build(
+ "sofa.rpc.config.dynamic.refresh.enable",
+ false,
+ false,
+ "Switch for dynamic configuration refresh.",
+ new String[] { "sofa_rpc_config_dynamic_refresh_enable" });
+
}
\ No newline at end of file
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/dynamic/DynamicConfigManager.java b/core/api/src/main/java/com/alipay/sofa/rpc/dynamic/DynamicConfigManager.java
index f44818df8..8c7ac5fa6 100644
--- a/core/api/src/main/java/com/alipay/sofa/rpc/dynamic/DynamicConfigManager.java
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/dynamic/DynamicConfigManager.java
@@ -17,22 +17,40 @@
package com.alipay.sofa.rpc.dynamic;
import com.alipay.sofa.rpc.auth.AuthRuleGroup;
+import com.alipay.sofa.rpc.common.utils.StringUtils;
import com.alipay.sofa.rpc.ext.Extensible;
+import com.alipay.sofa.rpc.listener.ConfigListener;
/**
- *
* @author bystander
* @version : DynamicManager.java, v 0.1 2019年04月12日 11:35 bystander Exp $
*/
@Extensible(singleton = true)
public abstract class DynamicConfigManager {
- private String appName;
+ private String appName;
+
+ private DynamicUrl dynamicUrl;
protected DynamicConfigManager(String appName) {
this.appName = appName;
}
+ protected DynamicConfigManager(String appName, String configCenterAddress) {
+ this.appName = appName;
+ if (StringUtils.isNotBlank(configCenterAddress)) {
+ this.dynamicUrl = new DynamicUrl(configCenterAddress);
+ }
+ }
+
+ protected String getAppName() {
+ return appName;
+ }
+
+ protected DynamicUrl getDynamicUrl() {
+ return dynamicUrl;
+ }
+
/**
* Init service's governance related configuration.
* Such as auth rules、lb rules
@@ -41,11 +59,21 @@ protected DynamicConfigManager(String appName) {
*/
public abstract void initServiceConfiguration(String service);
+ /**
+ * Init service's governance related configuration.
+ * Such as auth rules、lb rules
+ *
+ * @param service target service
+ * @param listener config listener
+ */
+ public void initServiceConfiguration(String service, ConfigListener listener) {
+ }
+
/**
* Get provider service related property.
*
* @param service target service
- * @param key property key
+ * @param key property key
* @return property value
*/
public abstract String getProviderServiceProperty(String service, String key);
@@ -54,7 +82,7 @@ protected DynamicConfigManager(String appName) {
* Get consumer service related property.
*
* @param service target service
- * @param key property key
+ * @param key property key
* @return property value
*/
public abstract String getConsumerServiceProperty(String service, String key);
@@ -63,8 +91,8 @@ protected DynamicConfigManager(String appName) {
* Get provider method related property.
*
* @param service target service
- * @param method target method
- * @param key property key
+ * @param method target method
+ * @param key property key
* @return property value
*/
public abstract String getProviderMethodProperty(String service, String method, String key);
@@ -73,8 +101,8 @@ protected DynamicConfigManager(String appName) {
* Get consumer method related property.
*
* @param service target service
- * @param method target method
- * @param key property key
+ * @param method target method
+ * @param key property key
* @return property value
*/
public abstract String getConsumerMethodProperty(String service, String method, String key);
@@ -86,4 +114,14 @@ protected DynamicConfigManager(String appName) {
* @return auth rules
*/
public abstract AuthRuleGroup getServiceAuthRule(String service);
+
+ /**
+ * Add config listener.
+ *
+ * @param key config key
+ * @param listener config listener
+ */
+ public void addListener(String key, ConfigListener listener) {
+ }
+
}
\ No newline at end of file
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/dynamic/DynamicConfigManagerFactory.java b/core/api/src/main/java/com/alipay/sofa/rpc/dynamic/DynamicConfigManagerFactory.java
index d4c2760c8..5b29646f2 100644
--- a/core/api/src/main/java/com/alipay/sofa/rpc/dynamic/DynamicConfigManagerFactory.java
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/dynamic/DynamicConfigManagerFactory.java
@@ -25,6 +25,8 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
/**
* @author bystander
@@ -37,6 +39,11 @@ public class DynamicConfigManagerFactory {
*/
private final static ConcurrentMap ALL_DYNAMICS = new ConcurrentHashMap();
+ /**
+ * 类锁
+ */
+ private final static Lock classLock = new ReentrantLock();
+
/**
* slf4j Logger for this class
*/
@@ -44,18 +51,19 @@ public class DynamicConfigManagerFactory {
.getLogger(DynamicConfigManagerFactory.class);
/**
- * 得到动态配置管理
+ * 得到动态配置管理器
*
* @param alias 别名
* @return DynamicManager 实现
*/
- public static synchronized DynamicConfigManager getDynamicManager(String appName, String alias) {
- if (ALL_DYNAMICS.size() > 3) { // 超过3次 是不是配错了?
- if (LOGGER.isWarnEnabled()) {
- LOGGER.warn("Size of dynamic manager is greater than 3, Please check it!");
- }
- }
+ public static DynamicConfigManager getDynamicManager(String appName, String alias) {
+ classLock.lock();
try {
+ if (ALL_DYNAMICS.size() > 3) { // 超过3次 是不是配错了?
+ if (LOGGER.isWarnEnabled()) {
+ LOGGER.warn("Size of dynamic manager is greater than 3, Please check it!");
+ }
+ }
// 注意:RegistryConfig重写了equals方法,如果多个RegistryConfig属性一样,则认为是一个对象
DynamicConfigManager registry = ALL_DYNAMICS.get(alias);
if (registry == null) {
@@ -73,6 +81,8 @@ public static synchronized DynamicConfigManager getDynamicManager(String appName
} catch (Throwable e) {
throw new SofaRpcRuntimeException(LogCodes.getLog(LogCodes.ERROR_LOAD_EXT, "DynamicConfigManager", alias),
e);
+ } finally {
+ classLock.unlock();
}
}
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/dynamic/DynamicUrl.java b/core/api/src/main/java/com/alipay/sofa/rpc/dynamic/DynamicUrl.java
new file mode 100644
index 000000000..b83ffc3d7
--- /dev/null
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/dynamic/DynamicUrl.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.dynamic;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author Narziss
+ * @version DynamicUrl.java, v 0.1 2024年10月28日 21:23 Narziss
+ */
+public class DynamicUrl {
+
+ private final String originalUrl;
+ private final String protocol;
+ private final String address;
+ private final String host;
+ private final int port;
+ private final String path;
+ private final Map params = new HashMap<>();
+
+ /**
+ * @param configCenterAddress example: apollo://127.0.0.1:8080/config?appId=xxx&cluster=yyy
+ */
+ public DynamicUrl(String configCenterAddress) {
+ this.originalUrl = configCenterAddress;
+ // 正则表达式解析协议、主机、端口、路径和参数,其中路径和参数是可选的
+ String regex = "^(\\w+)://([^:/]+):(\\d+)(/[^?]*)?(\\?.*)?$";
+ Matcher matcher = Pattern.compile(regex).matcher(configCenterAddress);
+ if (matcher.find()) {
+ this.protocol = matcher.group(1);
+ this.host = matcher.group(2);
+ this.port = Integer.parseInt(matcher.group(3));
+ // 判断路径是否为空或者为 "/"
+ this.path = (matcher.group(4) != null && !matcher.group(4).equals("/")) ? matcher.group(4) : "";
+ this.address = this.host + ":" + this.port + this.path;
+ if (matcher.group(5) != null) {
+ parseQueryParams(matcher.group(5).substring(1));
+ }
+ } else {
+ throw new IllegalArgumentException("Invalid URL format");
+ }
+ }
+
+ private void parseQueryParams(String query) {
+ String[] paramPairs = query.split("&");
+ for (String paramPair : paramPairs) {
+ String[] keyValue = paramPair.split("=", 2);
+ if (keyValue.length == 2) {
+ params.put(keyValue[0], keyValue[1]);
+ }
+ }
+ }
+
+ public String getOriginalUrl() {
+ return originalUrl;
+ }
+
+ public String getProtocol() {
+ return protocol;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public Map getParams() {
+ return params;
+ }
+
+ public String getParam(String key) {
+ return params.get(key);
+ }
+}
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/event/ProviderProcessorRegisterEvent.java b/core/api/src/main/java/com/alipay/sofa/rpc/event/ProviderProcessorRegisterEvent.java
new file mode 100644
index 000000000..fd9321260
--- /dev/null
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/event/ProviderProcessorRegisterEvent.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.event;
+
+import com.alipay.sofa.rpc.config.ProviderConfig;
+import com.alipay.sofa.rpc.config.ServerConfig;
+
+/**
+ * @author Even
+ * @date 2024/4/28 17:18
+ */
+public class ProviderProcessorRegisterEvent implements Event {
+
+ private final ProviderConfig providerConfig;
+
+ private final ServerConfig serverConfig;
+
+ public ProviderProcessorRegisterEvent(ProviderConfig providerConfig, ServerConfig serverConfig) {
+ this.providerConfig = providerConfig;
+ this.serverConfig = serverConfig;
+ }
+
+ public ProviderConfig getProviderConfig() {
+ return providerConfig;
+ }
+
+ public ServerConfig getServerConfig() {
+ return serverConfig;
+ }
+}
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/event/ProviderProcessorUnRegistryEvent.java b/core/api/src/main/java/com/alipay/sofa/rpc/event/ProviderProcessorUnRegistryEvent.java
new file mode 100644
index 000000000..26fb38fcd
--- /dev/null
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/event/ProviderProcessorUnRegistryEvent.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.event;
+
+import com.alipay.sofa.rpc.config.ProviderConfig;
+import com.alipay.sofa.rpc.config.ServerConfig;
+
+/**
+ * @author Even
+ * @date 2024/4/28 17:19
+ */
+public class ProviderProcessorUnRegistryEvent implements Event {
+
+ private final ProviderConfig providerConfig;
+
+ private final ServerConfig serverConfig;
+
+ public ProviderProcessorUnRegistryEvent(ProviderConfig providerConfig, ServerConfig serverConfig) {
+ this.providerConfig = providerConfig;
+ this.serverConfig = serverConfig;
+ }
+
+ public ProviderConfig getProviderConfig() {
+ return providerConfig;
+ }
+
+ public ServerConfig getServerConfig() {
+ return serverConfig;
+ }
+}
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/ext/ExtensionLoader.java b/core/api/src/main/java/com/alipay/sofa/rpc/ext/ExtensionLoader.java
index 8deb9200f..0e36f2365 100644
--- a/core/api/src/main/java/com/alipay/sofa/rpc/ext/ExtensionLoader.java
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/ext/ExtensionLoader.java
@@ -34,6 +34,7 @@
import java.io.InputStreamReader;
import java.lang.reflect.Modifier;
import java.net.URL;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
@@ -184,7 +185,7 @@ protected void loadFromClassLoader(ClassLoader classLoader, String fullFileName)
}
BufferedReader reader = null;
try {
- reader = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"));
+ reader = new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8));
String line;
while ((line = reader.readLine()) != null) {
readLine(url, line);
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/filter/FilterChain.java b/core/api/src/main/java/com/alipay/sofa/rpc/filter/FilterChain.java
index 9b0e97c20..e8488751c 100644
--- a/core/api/src/main/java/com/alipay/sofa/rpc/filter/FilterChain.java
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/filter/FilterChain.java
@@ -193,7 +193,7 @@ private static List selectActualFilters(AbstractInterfaceConfig config,
List> extensionFilters = new LinkedList>();
List filterAliases = config.getFilter(); //
if (CommonUtils.isNotEmpty(filterAliases)) {
- for (String filterAlias : filterAliases) {
+ filterAliases.stream().distinct().forEach(filterAlias -> {
if (startsWithExcludePrefix(filterAlias)) { // 排除用的特殊字符
excludes.add(filterAlias.substring(1));
} else {
@@ -202,7 +202,7 @@ private static List selectActualFilters(AbstractInterfaceConfig config,
extensionFilters.add(filter);
}
}
- }
+ });
}
// 解析自动加载的过滤器
if (!excludes.contains(StringUtils.ALL) && !excludes.contains(StringUtils.DEFAULT)) { // 配了-*和-default表示不加载内置
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/listener/ConfigListener.java b/core/api/src/main/java/com/alipay/sofa/rpc/listener/ConfigListener.java
index 7fb03ac2b..0c7685350 100644
--- a/core/api/src/main/java/com/alipay/sofa/rpc/listener/ConfigListener.java
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/listener/ConfigListener.java
@@ -16,6 +16,8 @@
*/
package com.alipay.sofa.rpc.listener;
+import com.alipay.sofa.rpc.dynamic.ConfigChangedEvent;
+
import java.util.Map;
/**
@@ -25,6 +27,15 @@
*/
public interface ConfigListener {
+ /**
+ * 处理配置变更事件
+ *
+ * @param event 配置变更事件
+ */
+ default void process(ConfigChangedEvent event){
+ // do nothing
+ }
+
/**
* 配置发生变化,例如
*
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/log/LogCodes.java b/core/api/src/main/java/com/alipay/sofa/rpc/log/LogCodes.java
index d42c83b5d..4188dc49d 100644
--- a/core/api/src/main/java/com/alipay/sofa/rpc/log/LogCodes.java
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/log/LogCodes.java
@@ -393,7 +393,7 @@ public static String getLog(String code) {
return CODE_DOES_NOT_EXIST + code;
}
try {
- return String.format(LOG, code, LOG_CODES.get(code), LogCodes.NOTE);
+ return format(code, LOG_CODES.get(code), LogCodes.NOTE);
} catch (Throwable e) {
LOGGER.error(LOG_FORMAT_ERROR + code, e);
}
@@ -428,7 +428,7 @@ public static String getLog(String code, Object... messages) {
}
try {
- return String.format(LOG, code, MessageFormat.format(message, messages), LogCodes.NOTE);
+ return format(code, MessageFormat.format(message, messages), LogCodes.NOTE);
} catch (Throwable e) {
LOGGER.error(LOG_FORMAT_ERROR + code, e);
}
@@ -456,4 +456,9 @@ public static String getLiteLog(String codeOrMsg, Object... messages) {
}
return LITE_LOG_FORMAT_ERROR + codeOrMsg;
}
+
+ private static String format(String code, String message, String note) {
+ return "RPC-" + code + ": " + message + " " + note;
+ }
+
}
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/server/BusinessPool.java b/core/api/src/main/java/com/alipay/sofa/rpc/server/BusinessPool.java
index f391b6b72..ab3581ac4 100644
--- a/core/api/src/main/java/com/alipay/sofa/rpc/server/BusinessPool.java
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/server/BusinessPool.java
@@ -16,9 +16,12 @@
*/
package com.alipay.sofa.rpc.server;
+import com.alipay.sofa.rpc.common.threadpool.SofaExecutorFactory;
import com.alipay.sofa.rpc.config.ServerConfig;
+import com.alipay.sofa.rpc.ext.ExtensionLoaderFactory;
import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
@@ -43,4 +46,11 @@ public static ThreadPoolExecutor initPool(ServerConfig serverConfig) {
return new ThreadPoolExecutor(minPoolSize, maxPoolSize, aliveTime, TimeUnit.MILLISECONDS, poolQueue);
}
+ public static Executor initExecutor(String executorName, ServerConfig serverConfig) {
+ Executor executor = ExtensionLoaderFactory.getExtensionLoader(SofaExecutorFactory.class)
+ .getExtension(serverConfig.getThreadPoolType())
+ .createExecutor(executorName, serverConfig);
+ return executor;
+ }
+
}
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/server/UserThreadPool.java b/core/api/src/main/java/com/alipay/sofa/rpc/server/UserThreadPool.java
index 425651b6a..2d74195b4 100644
--- a/core/api/src/main/java/com/alipay/sofa/rpc/server/UserThreadPool.java
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/server/UserThreadPool.java
@@ -19,9 +19,12 @@
import com.alipay.sofa.rpc.common.struct.NamedThreadFactory;
import com.alipay.sofa.rpc.common.utils.ThreadPoolUtils;
+import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
/**
* 给用户配置的自定义业务线程池
@@ -87,20 +90,31 @@ public UserThreadPool(String uniqueThreadPoolName) {
/**
* 线程池
*/
+ @Deprecated
transient volatile ThreadPoolExecutor executor;
+ transient volatile Executor userExecutor;
+
+ private Lock lock = new ReentrantLock();
/**
* 初始化线程池
*/
public void init() {
- executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.MILLISECONDS,
+ userExecutor = buildExecutor();
+ }
+
+ protected Executor buildExecutor() {
+ ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime,
+ TimeUnit.MILLISECONDS,
ThreadPoolUtils.buildQueue(queueSize), new NamedThreadFactory(threadPoolName));
if (allowCoreThreadTimeOut) {
- executor.allowCoreThreadTimeOut(true);
+ threadPoolExecutor.allowCoreThreadTimeOut(true);
}
if (prestartAllCoreThreads) {
- executor.prestartAllCoreThreads();
+ threadPoolExecutor.prestartAllCoreThreads();
}
+ threadPoolExecutor.setRejectedExecutionHandler(new SofaRejectedExecutionHandler());
+ return threadPoolExecutor;
}
/**
@@ -257,14 +271,28 @@ public UserThreadPool setKeepAliveTime(int keepAliveTime) {
*
* @return the executor
*/
+ @Deprecated
public ThreadPoolExecutor getExecutor() {
if (executor == null) {
- synchronized (this) {
- if (executor == null) {
+ Executor tmp = getUserExecutor();
+ if (tmp instanceof ThreadPoolExecutor) {
+ executor = (ThreadPoolExecutor) tmp;
+ }
+ }
+ return executor;
+ }
+
+ public Executor getUserExecutor() {
+ if (userExecutor == null) {
+ lock.lock();
+ try {
+ if (userExecutor == null) {
init();
}
+ } finally {
+ lock.unlock();
}
}
- return executor;
+ return userExecutor;
}
}
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/server/UserVirtualThreadPool.java b/core/api/src/main/java/com/alipay/sofa/rpc/server/UserVirtualThreadPool.java
new file mode 100644
index 000000000..e8aaeb974
--- /dev/null
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/server/UserVirtualThreadPool.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.server;
+
+import com.alipay.sofa.rpc.common.threadpool.SofaExecutorFactory;
+import com.alipay.sofa.rpc.ext.ExtensionLoaderFactory;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ *
+ * @author junyuan
+ * @version UserVirtualThreadPool.java, v 0.1 2023年12月14日 14:17 junyuan Exp $
+ */
+public class UserVirtualThreadPool extends UserThreadPool {
+ private static final AtomicInteger POOL_NAME_COUNTER = new AtomicInteger(0);
+
+ /**
+ * 线程名字
+ *
+ */
+ private String threadPoolName;
+
+ public UserVirtualThreadPool() {
+ this.threadPoolName = DEFAUT_POOL_NAME + "-" + POOL_NAME_COUNTER.getAndIncrement();
+ }
+
+ @Override
+ protected Executor buildExecutor() {
+ return ExtensionLoaderFactory.getExtensionLoader(SofaExecutorFactory.class)
+ .getExtension("virtual").createExecutor(threadPoolName, null);
+ }
+}
\ No newline at end of file
diff --git a/core/api/src/main/java/com/alipay/sofa/rpc/transport/SofaStreamObserver.java b/core/api/src/main/java/com/alipay/sofa/rpc/transport/SofaStreamObserver.java
new file mode 100644
index 000000000..c841e5edc
--- /dev/null
+++ b/core/api/src/main/java/com/alipay/sofa/rpc/transport/SofaStreamObserver.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.transport;
+
+/**
+ * StreamHandler, works just like gRPC StreamObserver.
+ */
+public interface SofaStreamObserver {
+
+ /**
+ * Sends a message, or defines the behavior when a message is received.
+ *
This method should never be called after {@link SofaStreamObserver#onCompleted()} has been invoked.
+ */
+ void onNext(T message);
+
+ /**
+ * Note: This method MUST be invoked after the transport is complete.
+ * Failure to do so may result in unexpected errors.
+ *
+ * Signals that all messages have been sent/received normally, and closes this stream.
+ */
+ void onCompleted();
+
+ /**
+ * Signals an exception to terminate this stream, or defines the behavior when an error occurs.
+ *
+ * Once this method is invoked by one side, it can't send more messages, and the corresponding method on the other side will be triggered.
+ * Depending on the protocol implementation, it's possible that the other side can still call {@link SofaStreamObserver#onNext(Object)} after this method has been invoked, although this is not recommended.
+ *
+ * As a best practice, it is advised not to send any more information once this method is called.
+ *
+ */
+ void onError(Throwable throwable);
+}
diff --git a/core/api/src/main/resources/META-INF/services/sofa-rpc/com.alipay.sofa.rpc.common.threadpool.SofaExecutorFactory b/core/api/src/main/resources/META-INF/services/sofa-rpc/com.alipay.sofa.rpc.common.threadpool.SofaExecutorFactory
new file mode 100644
index 000000000..aac91cad5
--- /dev/null
+++ b/core/api/src/main/resources/META-INF/services/sofa-rpc/com.alipay.sofa.rpc.common.threadpool.SofaExecutorFactory
@@ -0,0 +1,2 @@
+cached=com.alipay.sofa.rpc.common.threadpool.extension.CachedThreadPoolFactory
+virtual=com.alipay.sofa.rpc.common.threadpool.extension.VirtualThreadPoolFactory
diff --git a/core/api/src/test/java/com/alipay/sofa/rpc/client/RouterChainTest.java b/core/api/src/test/java/com/alipay/sofa/rpc/client/RouterChainTest.java
index 17e436777..b762e4d23 100644
--- a/core/api/src/test/java/com/alipay/sofa/rpc/client/RouterChainTest.java
+++ b/core/api/src/test/java/com/alipay/sofa/rpc/client/RouterChainTest.java
@@ -48,7 +48,6 @@ public void buildProviderChain() {
ConsumerConfig config = new ConsumerConfig();
config.setBootstrap("test");
ArrayList list = new ArrayList();
- config.setRouter(Arrays.asList("testChainRouter0", "-testChainRouter8", "notExistChainRouter"));
list.add(new TestChainRouter1());
list.add(new TestChainRouter2());
list.add(new TestChainRouter3());
diff --git a/core/api/src/test/java/com/alipay/sofa/rpc/common/threadpool/ExecutorFactoryTest.java b/core/api/src/test/java/com/alipay/sofa/rpc/common/threadpool/ExecutorFactoryTest.java
new file mode 100644
index 000000000..aabb5636e
--- /dev/null
+++ b/core/api/src/test/java/com/alipay/sofa/rpc/common/threadpool/ExecutorFactoryTest.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.common.threadpool;
+
+import com.alipay.sofa.rpc.config.ServerConfig;
+import com.alipay.sofa.rpc.ext.ExtensionLoaderFactory;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ *
+ * @author junyuan
+ * @version ExecutorFactoryTest.java, v 0.1 2023年12月15日 10:59 junyuan Exp $
+ */
+public class ExecutorFactoryTest {
+
+ @Test
+ public void testBuildCachedPool() {
+ ServerConfig serverConfig = new ServerConfig();
+ Executor executor = ExtensionLoaderFactory.getExtensionLoader(SofaExecutorFactory.class).getExtension("cached")
+ .createExecutor("test", serverConfig);
+ Assert.assertTrue(executor instanceof ThreadPoolExecutor);
+
+ ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
+ Assert.assertEquals(threadPoolExecutor.getCorePoolSize(), serverConfig.getCoreThreads());
+ }
+}
diff --git a/core/api/src/test/java/com/alipay/sofa/rpc/config/ConsumerConfigTest.java b/core/api/src/test/java/com/alipay/sofa/rpc/config/ConsumerConfigTest.java
new file mode 100644
index 000000000..78eef2adb
--- /dev/null
+++ b/core/api/src/test/java/com/alipay/sofa/rpc/config/ConsumerConfigTest.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.config;
+
+import com.alipay.sofa.rpc.invoke.Invoker;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Even
+ * @date 2025/3/4 21:36
+ */
+public class ConsumerConfigTest {
+
+ @Test
+ public void testMethodTimeout() {
+ ConsumerConfig consumerConfig = new ConsumerConfig<>();
+ consumerConfig.setTimeout(4000);
+ consumerConfig.setInterfaceId(Invoker.class.getName());
+ consumerConfig.getConfigValueCache(true);
+ Assert.assertEquals(4000, consumerConfig.getMethodTimeout("invoke"));
+
+ List methodConfigs = new ArrayList<>();
+ MethodConfig methodConfig = new MethodConfig();
+ methodConfig.setName("invoke");
+ methodConfigs.add(methodConfig);
+ consumerConfig.setMethods(methodConfigs);
+ consumerConfig.getConfigValueCache(true);
+ Assert.assertEquals(4000, consumerConfig.getMethodTimeout("invoke"));
+
+ methodConfig.setTimeout(5000);
+ consumerConfig.getConfigValueCache(true);
+ Assert.assertEquals(5000, consumerConfig.getMethodTimeout("invoke"));
+
+ methodConfig.setTimeout(-1);
+ consumerConfig.getConfigValueCache(true);
+ Assert.assertEquals(-1, consumerConfig.getMethodTimeout("invoke"));
+ }
+}
\ No newline at end of file
diff --git a/core/api/src/test/java/com/alipay/sofa/rpc/config/UserThreadPoolManagerTest.java b/core/api/src/test/java/com/alipay/sofa/rpc/config/UserThreadPoolManagerTest.java
index ddab64d26..25fa22bd9 100644
--- a/core/api/src/test/java/com/alipay/sofa/rpc/config/UserThreadPoolManagerTest.java
+++ b/core/api/src/test/java/com/alipay/sofa/rpc/config/UserThreadPoolManagerTest.java
@@ -17,9 +17,15 @@
package com.alipay.sofa.rpc.config;
import com.alipay.sofa.rpc.server.UserThreadPool;
+import com.alipay.sofa.rpc.server.UserVirtualThreadPool;
+import com.alipay.sofa.rpc.server.SofaRejectedExecutionHandler;
import org.junit.Assert;
import org.junit.Test;
+
import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ThreadPoolExecutor;
public class UserThreadPoolManagerTest {
@Test
@@ -50,4 +56,39 @@ public void getUserThreadPoolMap() {
userThreadPoolSet = UserThreadPoolManager.getUserThreadPoolSet();
Assert.assertEquals(4, userThreadPoolSet.size());
}
+
+ @Test
+ public void userThreadPoolBuildTest() {
+ UserThreadPool userThreadPool = new UserVirtualThreadPool();
+ Object result;
+ try {
+ result = userThreadPool.getExecutor();
+ } catch (UnsupportedOperationException e) {
+ // jdk 21 以下, 这里应该抛出 UnsupportedOperationException
+ return;
+ }
+ Assert.assertNull(result);
+ }
+
+ @Test
+ public void testRejectedExecutionHandler() {
+ UserThreadPool userThreadPool = new UserThreadPool();
+ Executor executorService = userThreadPool.getUserExecutor();
+ Assert.assertTrue(executorService instanceof ThreadPoolExecutor);
+ RejectedExecutionHandler rejectedExecutionHandler = ((ThreadPoolExecutor) executorService)
+ .getRejectedExecutionHandler();
+ Assert.assertTrue(rejectedExecutionHandler instanceof SofaRejectedExecutionHandler);
+ }
+
+ @Test
+ public void userThreadPoolCompatibleTest() {
+ UserThreadPool userThreadPool = new UserThreadPool();
+ Object result;
+ result = userThreadPool.getExecutor();
+ Assert.assertNotNull(result);
+ Executor executorService = userThreadPool.getUserExecutor();
+ Assert.assertTrue(executorService instanceof ThreadPoolExecutor);
+
+ Assert.assertEquals(executorService, result);
+ }
}
\ No newline at end of file
diff --git a/core/api/src/test/java/com/alipay/sofa/rpc/dynamic/DynamicUrlTest.java b/core/api/src/test/java/com/alipay/sofa/rpc/dynamic/DynamicUrlTest.java
new file mode 100644
index 000000000..86f2cd187
--- /dev/null
+++ b/core/api/src/test/java/com/alipay/sofa/rpc/dynamic/DynamicUrlTest.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.dynamic;
+
+import org.junit.Test;
+import org.junit.Assert;
+
+/**
+ * @author Narziss
+ * @version DynamicUrl.java, v 0.1 2024年10月28日 21:40 Narziss
+ */
+public class DynamicUrlTest {
+
+ @Test
+ public void testWithPathAndParams() {
+ DynamicUrl dynamicUrl = new DynamicUrl("apollo://127.0.0.1:8080/config?appId=xxx&cluster=yyy");
+ Assert.assertEquals("apollo", dynamicUrl.getProtocol());
+ Assert.assertEquals("127.0.0.1", dynamicUrl.getHost());
+ Assert.assertEquals(8080, dynamicUrl.getPort());
+ Assert.assertEquals("/config", dynamicUrl.getPath());
+ Assert.assertEquals("127.0.0.1:8080/config", dynamicUrl.getAddress());
+ Assert.assertNotNull(dynamicUrl.getParams());
+ Assert.assertEquals("xxx", dynamicUrl.getParams().get("appId"));
+ Assert.assertEquals("yyy", dynamicUrl.getParams().get("cluster"));
+ }
+
+ @Test
+ public void testWithSlashAndParams() {
+ DynamicUrl dynamicUrl = new DynamicUrl("apollo://127.0.0.1:8080/?appId=xxx&cluster=yyy");
+ Assert.assertEquals("apollo", dynamicUrl.getProtocol());
+ Assert.assertEquals("127.0.0.1", dynamicUrl.getHost());
+ Assert.assertEquals(8080, dynamicUrl.getPort());
+ Assert.assertEquals("", dynamicUrl.getPath());// 如果路径为空,返回空字符串
+ Assert.assertEquals("127.0.0.1:8080", dynamicUrl.getAddress());
+ Assert.assertNotNull(dynamicUrl.getParams());
+ Assert.assertEquals("xxx", dynamicUrl.getParams().get("appId"));
+ Assert.assertEquals("yyy", dynamicUrl.getParams().get("cluster"));
+ }
+
+ @Test
+ public void testWithParams() {
+ DynamicUrl dynamicUrl = new DynamicUrl("apollo://127.0.0.1:8080?appId=xxx&cluster=yyy");
+ Assert.assertEquals("apollo", dynamicUrl.getProtocol());
+ Assert.assertEquals("127.0.0.1", dynamicUrl.getHost());
+ Assert.assertEquals(8080, dynamicUrl.getPort());
+ Assert.assertEquals("", dynamicUrl.getPath());
+ Assert.assertEquals("127.0.0.1:8080", dynamicUrl.getAddress());
+ Assert.assertNotNull(dynamicUrl.getParams());
+ Assert.assertEquals("xxx", dynamicUrl.getParams().get("appId"));
+ Assert.assertEquals("yyy", dynamicUrl.getParams().get("cluster"));
+ }
+
+ @Test
+ public void testOnlyHostAndPort() {
+ DynamicUrl dynamicUrl = new DynamicUrl("apollo://127.0.0.1:8080");
+ Assert.assertEquals("apollo", dynamicUrl.getProtocol());
+ Assert.assertEquals("127.0.0.1", dynamicUrl.getHost());
+ Assert.assertEquals(8080, dynamicUrl.getPort());
+ Assert.assertEquals("", dynamicUrl.getPath());
+ Assert.assertEquals("127.0.0.1:8080", dynamicUrl.getAddress());
+ System.out.println(dynamicUrl.getParams());
+ Assert.assertNotNull(dynamicUrl.getParams());
+ Assert.assertTrue(dynamicUrl.getParams().isEmpty());
+ }
+}
diff --git a/core/api/src/test/java/com/alipay/sofa/rpc/filter/FilterChainTest.java b/core/api/src/test/java/com/alipay/sofa/rpc/filter/FilterChainTest.java
index 2b6984b85..647ce4c34 100644
--- a/core/api/src/test/java/com/alipay/sofa/rpc/filter/FilterChainTest.java
+++ b/core/api/src/test/java/com/alipay/sofa/rpc/filter/FilterChainTest.java
@@ -25,7 +25,6 @@
import org.junit.Test;
import java.util.ArrayList;
-import java.util.Arrays;
/**
*
@@ -38,12 +37,10 @@ public class FilterChainTest {
public void buildProviderChain() {
ProviderConfig providerConfig = new ProviderConfig();
- providerConfig.setFilter(Arrays.asList("testChainFilter0", "-testChainFilter8"));
providerConfig.setInterfaceId(Serializer.class.getName());
ConsumerConfig consumerConfig = new ConsumerConfig();
ArrayList list = new ArrayList();
- consumerConfig.setFilter(Arrays.asList("testChainFilter0", "-testChainFilter8"));
list.add(new TestChainFilter1());
list.add(new TestChainFilter2());
list.add(new TestChainFilter3());
diff --git a/core/api/src/test/java/com/alipay/sofa/rpc/std/config/AbstractInterfaceConfigTest.java b/core/api/src/test/java/com/alipay/sofa/rpc/std/config/AbstractInterfaceConfigTest.java
index fa25d4bc3..881d310f9 100644
--- a/core/api/src/test/java/com/alipay/sofa/rpc/std/config/AbstractInterfaceConfigTest.java
+++ b/core/api/src/test/java/com/alipay/sofa/rpc/std/config/AbstractInterfaceConfigTest.java
@@ -23,6 +23,7 @@
import com.alipay.sofa.rpc.config.AbstractInterfaceConfig;
import com.alipay.sofa.rpc.config.ApplicationConfig;
import com.alipay.sofa.rpc.config.MethodConfig;
+import com.alipay.sofa.rpc.config.ProviderConfig;
import com.alipay.sofa.rpc.config.RegistryConfig;
import com.alipay.sofa.rpc.core.exception.SofaRpcRuntimeException;
import com.alipay.sofa.rpc.listener.ConfigListener;
@@ -50,6 +51,21 @@
* @version : AbstractInterfaceConfigTest.java, v 0.1 2022年01月25日 4:46 下午 zhaowang
*/
public class AbstractInterfaceConfigTest {
+ @Test
+ public void testMethodTimeout() {
+ MethodConfig config = new MethodConfig();
+ config.setTimeout(null);
+
+ ProviderConfig p = new ProviderConfig();
+ p.setMethods(new HashMap<>());
+ p.getMethods().put("test", config);
+
+ try {
+ Assert.assertFalse(p.hasTimeout());
+ } catch (Exception e) {
+ Assert.fail("exception should not appears: " + e.getMessage());
+ }
+ }
@Test
public void testDefaultValue() {
@@ -58,7 +74,7 @@ public void testDefaultValue() {
assertEquals(null, defaultConfig.getInterfaceId());
assertEquals("", defaultConfig.getUniqueId());
assertEquals(null, defaultConfig.getFilterRef());
- assertEquals(null, defaultConfig.getFilter());
+ assertNotNull(defaultConfig.getFilter());
assertEquals(null, defaultConfig.getRegistry());
assertEquals(null, defaultConfig.getMethods());
assertEquals("hessian2", defaultConfig.getSerialization());
@@ -122,9 +138,18 @@ public void testSetGet() {
config.setFilterRef(filterRefList);
assertSame(filterRefList, config.getFilterRef());
+ List defaultFilter = config.getFilter();
List filterList = new ArrayList<>();
+ filterList.add("testFilter");
+ config.addFilter(filterList);
+ Assert.assertTrue(defaultFilter.contains("testChainFilter0"));
+ Assert.assertTrue(defaultFilter.contains("-testChainFilter8"));
+ Assert.assertTrue(defaultFilter.contains("testFilter"));
+ assertSame(defaultFilter, config.getFilter());
+
config.setFilter(filterList);
assertSame(filterList, config.getFilter());
+ assertNotSame(defaultFilter, config.getFilter());
List registryConfigs = new ArrayList<>();
config.setRegistry(registryConfigs);
diff --git a/core/api/src/test/java/com/alipay/sofa/rpc/std/config/ConsumerConfigTest.java b/core/api/src/test/java/com/alipay/sofa/rpc/std/config/ConsumerConfigTest.java
index fabb40a41..43d5daa0d 100644
--- a/core/api/src/test/java/com/alipay/sofa/rpc/std/config/ConsumerConfigTest.java
+++ b/core/api/src/test/java/com/alipay/sofa/rpc/std/config/ConsumerConfigTest.java
@@ -24,9 +24,13 @@
import com.alipay.sofa.rpc.core.exception.SofaRpcRuntimeException;
import com.alipay.sofa.rpc.std.sample.SampleService;
import com.alipay.sofa.rpc.std.sample.SampleServiceImpl;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* @author zhaowang
* @version : ConsumerConfigTest.java, v 0.1 2022年01月28日 2:33 下午 zhaowang
@@ -59,7 +63,7 @@ public void testDefaultValue() {
assertEquals(30000, config.getHeartbeatPeriod());
assertEquals(10000, config.getReconnectPeriod());
assertEquals("DISCARD", config.getRejectedExecutionPolicy());
- assertEquals(null, config.getRouter());
+ assertNotNull(config.getRouter());
assertEquals(null, config.getRouterRef());
assertEquals(null, config.getOnReturn());
assertEquals(null, config.getOnConnect());
@@ -119,6 +123,20 @@ public void testGetInterfaceId() {
assertEquals("serviceName", config.getInterfaceId());
}
+ @Test
+ public void testRouter() {
+ List router = config.getRouter();
+ List addRouter = new ArrayList<>();
+ addRouter.add("testRouter");
+ config.addRouter(addRouter);
+ assertSame(router, config.getRouter());
+ Assert.assertTrue(router.contains("testRouter"));
+
+ config.setRouter(addRouter);
+ assertNotSame(router, config.getRouter());
+ assertSame(addRouter, config.getRouter());
+ }
+
public interface InnerInterface {
}
diff --git a/core/api/src/test/resources/sofa-rpc/rpc-config.json b/core/api/src/test/resources/sofa-rpc/rpc-config.json
index 56d8da171..fa1fc161b 100644
--- a/core/api/src/test/resources/sofa-rpc/rpc-config.json
+++ b/core/api/src/test/resources/sofa-rpc/rpc-config.json
@@ -1,4 +1,6 @@
{
"rpc.config.order": 999,
- "logger.impl": "com.alipay.sofa.rpc.log.SystemLogger"
+ "logger.impl": "com.alipay.sofa.rpc.log.SystemLogger",
+ "default.filters" : ["testChainFilter0", "-testChainFilter8", "testChainFilter0"],
+ "consumer.routers" : ["testChainRouter0", "-testChainRouter8", "notExistChainRouter", "testChainRouter0"]
}
\ No newline at end of file
diff --git a/core/common/src/main/java/com/alipay/sofa/rpc/common/BatchExecutorQueue.java b/core/common/src/main/java/com/alipay/sofa/rpc/common/BatchExecutorQueue.java
new file mode 100644
index 000000000..d8bdcd458
--- /dev/null
+++ b/core/common/src/main/java/com/alipay/sofa/rpc/common/BatchExecutorQueue.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.common;
+
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * 批量执行队列
+ *
+ * @author chengming
+ * @version BatchExecutorQueue.java, v 0.1 2024年02月28日 5:36 PM chengming
+ */
+public class BatchExecutorQueue {
+
+ static final int DEFAULT_QUEUE_SIZE = 128;
+
+ private final Queue queue;
+
+ private final AtomicBoolean scheduled;
+
+ private final int chunkSize;
+
+ public BatchExecutorQueue() {
+ this(DEFAULT_QUEUE_SIZE);
+ }
+
+ public BatchExecutorQueue(int chunkSize) {
+ this.queue = new ConcurrentLinkedQueue<>();
+ this.scheduled = new AtomicBoolean(false);
+ this.chunkSize = chunkSize;
+ }
+
+ public void enqueue(T message, Executor executor) {
+ queue.add(message);
+ scheduleFlush(executor);
+ }
+
+ protected void scheduleFlush(Executor executor) {
+ if (scheduled.compareAndSet(false, true)) {
+ executor.execute(() -> this.run(executor));
+ }
+ }
+
+ void run(Executor executor) {
+ try {
+ Queue snapshot = new LinkedList<>();
+ T item;
+ while ((item = queue.poll()) != null) {
+ snapshot.add(item);
+ }
+ int i = 0;
+ boolean flushedOnce = false;
+ while ((item = snapshot.poll()) != null) {
+ if (snapshot.size() == 0) {
+ flushedOnce = false;
+ break;
+ }
+ if (i == chunkSize) {
+ i = 0;
+ flush(item);
+ flushedOnce = true;
+ } else {
+ prepare(item);
+ i++;
+ }
+ }
+ if ((i != 0 || !flushedOnce) && item != null) {
+ flush(item);
+ }
+ } finally {
+ scheduled.set(false);
+ if (!queue.isEmpty()) {
+ scheduleFlush(executor);
+ }
+ }
+ }
+
+ protected void prepare(T item) {
+ }
+
+ protected void flush(T item) {
+ }
+
+ /**
+ * UT only
+ * @return
+ */
+ @Deprecated
+ public AtomicBoolean getScheduled() {
+ return scheduled;
+ }
+
+ /**
+ * UT only
+ * @return
+ */
+ @Deprecated
+ public Queue getQueue() {
+ return queue;
+ }
+}
\ No newline at end of file
diff --git a/core/common/src/main/java/com/alipay/sofa/rpc/common/SofaOptions.java b/core/common/src/main/java/com/alipay/sofa/rpc/common/SofaOptions.java
index ae46681d5..4a9e1753c 100644
--- a/core/common/src/main/java/com/alipay/sofa/rpc/common/SofaOptions.java
+++ b/core/common/src/main/java/com/alipay/sofa/rpc/common/SofaOptions.java
@@ -160,6 +160,10 @@ public class SofaOptions {
* 序列化覆盖
*/
public static final String CONFIG_SERIALIZE_BLACKLIST_OVERRIDE = "rpc_serialize_blacklist_override";
+ /**
+ * 序列化覆盖
+ */
+ public static final String CONFIG_SERIALIZE_WHITELIST_OVERRIDE = "rpc_serialize_whitelist_override";
//========= GRPC 相关配置 ==========
/**
diff --git a/core/common/src/main/java/com/alipay/sofa/rpc/common/config/RpcConfigKeys.java b/core/common/src/main/java/com/alipay/sofa/rpc/common/config/RpcConfigKeys.java
index 11fc481c5..d15aae3ac 100644
--- a/core/common/src/main/java/com/alipay/sofa/rpc/common/config/RpcConfigKeys.java
+++ b/core/common/src/main/java/com/alipay/sofa/rpc/common/config/RpcConfigKeys.java
@@ -18,6 +18,9 @@
import com.alipay.sofa.common.config.ConfigKey;
+import static com.alipay.sofa.rpc.common.SofaOptions.CONFIG_SERIALIZE_BLACKLIST_OVERRIDE;
+import static com.alipay.sofa.rpc.common.SofaOptions.CONFIG_SERIALIZE_WHITELIST_OVERRIDE;
+
/**
* @author zhaowang
* @version : RpcConfigKeys.java, v 0.1 2020年12月14日 9:56 下午 zhaowang Exp $
@@ -102,4 +105,51 @@ public class RpcConfigKeys {
false,
"judge the generic object exception fields.",
new String[] { "sofa_rpc_generic_exception_fields" });
+
+ public static final ConfigKey SERIALIZE_BLACKLIST_OVERRIDE = ConfigKey
+ .build(
+ "sofa.rpc.serialize.blacklist.override",
+ "",
+ false,
+ "Additional serialization blacklist.",
+ new String[] { CONFIG_SERIALIZE_BLACKLIST_OVERRIDE });
+
+ public static final ConfigKey SERIALIZE_WHITELIST_OVERRIDE = ConfigKey
+ .build(
+ "sofa.rpc.serialize.whitelist.override",
+ "",
+ false,
+ "Additional serialization whitelist.",
+ new String[] { CONFIG_SERIALIZE_WHITELIST_OVERRIDE });
+
+ public static final ConfigKey SERIALIZE_CHECKER_MODE = ConfigKey
+ .build(
+ "sofa.rpc.codec.serialize.checkMode",
+ "STRICT",
+ true,
+ " The default filtering mode is STRICT.You can also set WARN or DISABLE",
+ new String[] { "sofa_rpc_codec_serialize_checkMode" });
+
+ /**
+ * biz thread pool type
+ */
+ public static ConfigKey SERVER_THREAD_POOL_TYPE = ConfigKey
+ .build(
+ "sofa.rpc.server.thread.pool.type",
+ "cached",
+ false,
+ "specify biz thread pool implementation type",
+ new String[] { "sofa_rpc_server_thread_pool_type" });
+
+ /**
+ * grpc client keep alive interval
+ */
+ public static ConfigKey TRIPLE_CLIENT_KEEP_ALIVE_INTERVAL = ConfigKey
+ .build(
+ "sofa.rpc.triple.client.keepAlive.interval",
+ 0,
+ false,
+ "keep alive interval in second for triple client",
+ new String[] { "sofa_rpc_triple_client_keepAlive_interval" });
+
}
diff --git a/core/common/src/main/resources/com/alipay/sofa/rpc/common/rpc-config-default.json b/core/common/src/main/resources/com/alipay/sofa/rpc/common/rpc-config-default.json
index 9becfcf3f..48aaddf2b 100644
--- a/core/common/src/main/resources/com/alipay/sofa/rpc/common/rpc-config-default.json
+++ b/core/common/src/main/resources/com/alipay/sofa/rpc/common/rpc-config-default.json
@@ -17,7 +17,7 @@ PS:大家也看到了,本JSON文档是支持注释的,而标准JSON是不支
"META-INF/services/"
],
// 需要被加载的模块列表,多个用逗号隔开
- "module.load.list" : "*",
+ "module.load.list": "*",
/*-------------RPC框架内部使用配置项-------------*/
@@ -66,6 +66,8 @@ PS:大家也看到了,本JSON文档是支持注释的,而标准JSON是不支
"default.transport": "netty4",
// 默认tracer实现
"default.tracer": "",
+ // 默认filter实现
+ "default.filters": [],
/*-------------默认配置值结束-------------*/
@@ -211,6 +213,7 @@ PS:大家也看到了,本JSON文档是支持注释的,而标准JSON是不支
"consumer.connect.create.when.absent": true,
// 默认回调线程池满时的拒绝策略,可用值:DISCARD, CALLER_RUNS, CALLER_HANDLE_EXCEPTION
"consumer.rejected.execution.policy": "DISCARD",
+ "consumer.routers": [],
/*-------------Consumer相关配置结束-------------*/
@@ -270,6 +273,8 @@ PS:大家也看到了,本JSON文档是支持注释的,而标准JSON是不支
"compress.size.baseline": 2048,
//Whether the Http2 Cleartext protocol client uses Prior Knowledge to start Http2
"transport.client.h2c.usePriorKnowledge": true,
+ // grpc client keep alive interval, default to 0, no keep alive
+ "sofa.rpc.triple.client.keepAlive.interval": 0,
/*-------------Transport层相关配置结束-------------*/
/*
diff --git a/core/common/src/test/java/com/alipay/sofa/rpc/common/BatchExecutorQueueTest.java b/core/common/src/test/java/com/alipay/sofa/rpc/common/BatchExecutorQueueTest.java
new file mode 100644
index 000000000..d77b08b3b
--- /dev/null
+++ b/core/common/src/test/java/com/alipay/sofa/rpc/common/BatchExecutorQueueTest.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.common;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+/**
+ * @author chengming
+ * @version BatchExecutorQueueTest.java, v 0.1 2024年03月01日 10:55 AM chengming
+ */
+public class BatchExecutorQueueTest {
+
+ private BatchExecutorQueue
diff --git a/remoting/remoting-bolt/src/main/java/com/alipay/sofa/rpc/codec/bolt/SofaRpcSerialization.java b/remoting/remoting-bolt/src/main/java/com/alipay/sofa/rpc/codec/bolt/SofaRpcSerialization.java
index 655c8fd87..297243824 100644
--- a/remoting/remoting-bolt/src/main/java/com/alipay/sofa/rpc/codec/bolt/SofaRpcSerialization.java
+++ b/remoting/remoting-bolt/src/main/java/com/alipay/sofa/rpc/codec/bolt/SofaRpcSerialization.java
@@ -312,6 +312,9 @@ protected void parseRequestHeader(Map headerMap, Object sofaRequ
if (sofaRequest instanceof SofaRequest) {
// 处理 tracer
parseRequestHeader(RemotingConstants.RPC_TRACE_NAME, headerMap, (SofaRequest) sofaRequest);
+ if (RpcInvokeContext.isBaggageEnable()) {
+ parseRequestHeader(RemotingConstants.RPC_REQUEST_BAGGAGE, headerMap, (SofaRequest) sofaRequest);
+ }
Map requestProps = ((SofaRequest) sofaRequest).getRequestProps();
if (requestProps == null) {
for (Map.Entry entry : headerMap.entrySet()) {
diff --git a/remoting/remoting-bolt/src/main/java/com/alipay/sofa/rpc/server/bolt/BoltServer.java b/remoting/remoting-bolt/src/main/java/com/alipay/sofa/rpc/server/bolt/BoltServer.java
index d1b9cec9d..3a054d98c 100644
--- a/remoting/remoting-bolt/src/main/java/com/alipay/sofa/rpc/server/bolt/BoltServer.java
+++ b/remoting/remoting-bolt/src/main/java/com/alipay/sofa/rpc/server/bolt/BoltServer.java
@@ -20,6 +20,7 @@
import com.alipay.remoting.rpc.RpcServer;
import com.alipay.sofa.rpc.common.cache.ReflectCache;
import com.alipay.sofa.rpc.common.struct.NamedThreadFactory;
+import com.alipay.sofa.rpc.common.threadpool.ThreadPoolConstant;
import com.alipay.sofa.rpc.config.ConfigUniqueNameGenerator;
import com.alipay.sofa.rpc.config.ProviderConfig;
import com.alipay.sofa.rpc.config.ServerConfig;
@@ -40,6 +41,8 @@
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicInteger;
@@ -75,8 +78,14 @@ public class BoltServer implements Server {
/**
* 业务线程池
*/
+ @Deprecated
protected ThreadPoolExecutor bizThreadPool;
+ /**
+ * 业务线程池, 也支持非池化的执行器
+ */
+ protected Executor bizExecutor;
+
/**
* Invoker列表,接口--> Invoker
*/
@@ -85,20 +94,37 @@ public class BoltServer implements Server {
@Override
public void init(ServerConfig serverConfig) {
this.serverConfig = serverConfig;
- // 启动线程池
- bizThreadPool = initThreadPool(serverConfig);
+ bizExecutor = initExecutor(serverConfig);
+ if (bizExecutor instanceof ThreadPoolExecutor) {
+ bizThreadPool = (ThreadPoolExecutor) bizExecutor;
+ }
boltServerProcessor = new BoltServerProcessor(this);
}
- protected ThreadPoolExecutor initThreadPool(ServerConfig serverConfig) {
- ThreadPoolExecutor threadPool = BusinessPool.initPool(serverConfig);
- threadPool.setThreadFactory(new NamedThreadFactory(
- "SEV-BOLT-BIZ-" + serverConfig.getPort(), serverConfig.isDaemon()));
- threadPool.setRejectedExecutionHandler(new SofaRejectedExecutionHandler());
+ /**
+ * 指定类型初始化线程池
+ * @param serverConfig
+ * @return
+ */
+ protected Executor initExecutor(ServerConfig serverConfig) {
+ Executor executor = BusinessPool.initExecutor(
+ ThreadPoolConstant.BIZ_THREAD_NAME_PREFIX + serverConfig.getPort(), serverConfig);
+ if (executor instanceof ThreadPoolExecutor) {
+ configureThreadPoolExecutor((ThreadPoolExecutor) executor, serverConfig);
+ }
+ return executor;
+ }
+
+ /**
+ * 针对 ThreadPoolExecutor 进行额外配置
+ * @param executor
+ * @param serverConfig
+ */
+ protected void configureThreadPoolExecutor(ThreadPoolExecutor executor, ServerConfig serverConfig) {
+ executor.setRejectedExecutionHandler(new SofaRejectedExecutionHandler());
if (serverConfig.isPreStartCore()) { // 初始化核心线程池
- threadPool.prestartAllCoreThreads();
+ executor.prestartAllCoreThreads();
}
- return threadPool;
}
@Override
@@ -204,28 +230,73 @@ public void destroy() {
return;
}
int stopTimeout = serverConfig.getStopTimeout();
- if (stopTimeout > 0) { // 需要等待结束时间
- AtomicInteger count = boltServerProcessor.processingCount;
- // 有正在执行的请求 或者 队列里有请求
- if (count.get() > 0 || bizThreadPool.getQueue().size() > 0) {
- long start = RpcRuntimeContext.now();
- if (LOGGER.isInfoEnabled()) {
- LOGGER.info("There are {} call in processing and {} call in queue, wait {} ms to end",
- count, bizThreadPool.getQueue().size(), stopTimeout);
- }
- while ((count.get() > 0 || bizThreadPool.getQueue().size() > 0)
- && RpcRuntimeContext.now() - start < stopTimeout) { // 等待返回结果
- try {
- Thread.sleep(10);
- } catch (InterruptedException ignore) {
- }
+ destroyThreadPool(bizExecutor, stopTimeout);
+ stop();
+ }
+
+ /**
+ * 如果未设置有效的 stopWaitTime, 将直接触发 shutdown
+ * @param executor
+ * @param stopWaitTime
+ */
+ private void destroyThreadPool(Executor executor, int stopWaitTime) {
+ if (stopWaitTime > 0) {
+ if (executor instanceof ThreadPoolExecutor) {
+ threadPoolExecutorDestroy((ThreadPoolExecutor) executor, stopWaitTime);
+ } else if (executor instanceof ExecutorService) {
+ executorServiceDestroy((ExecutorService) executor, stopWaitTime);
+ }
+ }
+ }
+
+ /**
+ * 将在 stopWaitTime 时限到期时强制 shutdown
+ * @param executor
+ * @param stopWaitTime
+ */
+ private void threadPoolExecutorDestroy(ThreadPoolExecutor executor, int stopWaitTime) {
+ AtomicInteger count = boltServerProcessor.processingCount;
+ // 有正在执行的请求 或者 队列里有请求
+ if (count.get() > 0 || executor.getQueue().size() > 0) {
+ long start = RpcRuntimeContext.now();
+ if (LOGGER.isInfoEnabled()) {
+ LOGGER.info("There are {} call in processing and {} call in queue, wait {} ms to end",
+ count, executor.getQueue().size(), stopWaitTime);
+ }
+ while ((count.get() > 0 || executor.getQueue().size() > 0)
+ && RpcRuntimeContext.now() - start < stopWaitTime) { // 等待返回结果
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException ignore) {
}
- } // 关闭前检查已有请求?
+ }
}
+ executor.shutdown();
+ }
- // 关闭线程池
- bizThreadPool.shutdown();
- stop();
+ /**
+ * 针对 ExecutorService, shutdown 后仍然会处理 queue 内任务, 不用判断 queue
+ * @param executorService
+ * @param stopWaitTime
+ */
+ private void executorServiceDestroy(ExecutorService executorService, int stopWaitTime) {
+ AtomicInteger count = boltServerProcessor.processingCount;
+ // 有正在执行的请求 或者 队列里有请求
+ if (count.get() > 0) {
+ long start = RpcRuntimeContext.now();
+ if (LOGGER.isInfoEnabled()) {
+ LOGGER.info("There are {} call in processing, wait {} ms to end",
+ count, stopWaitTime);
+ }
+ while ((count.get() > 0)
+ && RpcRuntimeContext.now() - start < stopWaitTime) { // 等待返回结果
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException ignore) {
+ }
+ }
+ }
+ executorService.shutdown();
}
@Override
@@ -244,10 +315,15 @@ public void destroy(DestroyHook hook) {
*
* @return 业务线程池
*/
+ @Deprecated
public ThreadPoolExecutor getBizThreadPool() {
return bizThreadPool;
}
+ public Executor getBizExecutor() {
+ return bizExecutor;
+ }
+
/**
* 找到服务端Invoker
*
@@ -269,4 +345,13 @@ public void cleanReflectCache(ProviderConfig providerConfig) {
ReflectCache.invalidateMethodSigsCache(key);
ReflectCache.invalidateOverloadMethodCache(key);
}
+
+ @Deprecated
+ protected ThreadPoolExecutor initThreadPool(ServerConfig serverConfig) {
+ ThreadPoolExecutor threadPool = BusinessPool.initPool(serverConfig);
+ threadPool.setThreadFactory(new NamedThreadFactory(
+ ThreadPoolConstant.BIZ_THREAD_NAME_PREFIX + serverConfig.getPort(), serverConfig.isDaemon()));
+ configureThreadPoolExecutor(threadPool, serverConfig);
+ return threadPool;
+ }
}
diff --git a/remoting/remoting-bolt/src/main/java/com/alipay/sofa/rpc/server/bolt/BoltServerProcessor.java b/remoting/remoting-bolt/src/main/java/com/alipay/sofa/rpc/server/bolt/BoltServerProcessor.java
index e03563614..ab209534e 100644
--- a/remoting/remoting-bolt/src/main/java/com/alipay/sofa/rpc/server/bolt/BoltServerProcessor.java
+++ b/remoting/remoting-bolt/src/main/java/com/alipay/sofa/rpc/server/bolt/BoltServerProcessor.java
@@ -31,6 +31,7 @@
import com.alipay.sofa.rpc.common.utils.CommonUtils;
import com.alipay.sofa.rpc.config.ProviderConfig;
import com.alipay.sofa.rpc.config.UserThreadPoolManager;
+import com.alipay.sofa.rpc.context.RecordContextResolver;
import com.alipay.sofa.rpc.context.RpcInternalContext;
import com.alipay.sofa.rpc.context.RpcInvokeContext;
import com.alipay.sofa.rpc.context.RpcRuntimeContext;
@@ -211,6 +212,7 @@ public void handleRequest(BizContext bizCtx, AsyncContext asyncCtx, SofaRequest
LOGGER.errorWithApp(appName, e.getMessage(), e);
}
} finally {
+ RecordContextResolver.carryWithRequest(bizCtx.getInvokeContext().getRecordContext(), request);
processingCount.decrementAndGet();
if (!isAsyncChain) {
if (EventBus.isEnable(ServerEndHandleEvent.class)) {
@@ -332,7 +334,7 @@ public String interest() {
@Override
public Executor getExecutor() {
- return boltServer.getBizThreadPool();
+ return boltServer.getBizExecutor();
}
@Override
@@ -361,7 +363,7 @@ public Executor select(String requestClass, Object requestHeader) {
if (service != null) {
UserThreadPool threadPool = UserThreadPoolManager.getUserThread(service);
if (threadPool != null) {
- Executor executor = threadPool.getExecutor();
+ Executor executor = threadPool.getUserExecutor();
if (executor != null) {
// 存在自定义线程池,且不为空
return executor;
diff --git a/remoting/remoting-bolt/src/test/java/com/alipay/sofa/rpc/codec/bolt/SofaRpcSerializationTest.java b/remoting/remoting-bolt/src/test/java/com/alipay/sofa/rpc/codec/bolt/SofaRpcSerializationTest.java
index 646f12200..2283ec307 100644
--- a/remoting/remoting-bolt/src/test/java/com/alipay/sofa/rpc/codec/bolt/SofaRpcSerializationTest.java
+++ b/remoting/remoting-bolt/src/test/java/com/alipay/sofa/rpc/codec/bolt/SofaRpcSerializationTest.java
@@ -25,6 +25,7 @@
import com.alipay.sofa.rpc.core.request.SofaRequest;
import org.junit.Assert;
import org.junit.Test;
+
import java.util.HashMap;
import java.util.Map;
@@ -92,16 +93,24 @@ public void testParseRequestHeader(){
headerMap.put("testKey1","testValue1");
headerMap.put("rpc_trace_context.sofaTraceId", "traceId");
headerMap.put("rpc_trace_context.sofaRpcId", "rpcId");
+ headerMap.put("rpc_req_baggage.testBaggageKey1", "testBaggageValue1");
+ headerMap.put("rpc_req_baggage.testBaggageKey2", "testBaggageValue2");
SofaRpcSerialization sofaRpcSerialization = new SofaRpcSerialization();
SofaRequest sofaRequest = new SofaRequest();
sofaRequest.addRequestProp("testKey1", "testValue11");
sofaRequest.addRequestProp("testKey2", "testValue2");
sofaRpcSerialization.parseRequestHeader(headerMap, sofaRequest);
+ sofaRpcSerialization.parseRequestHeader(headerMap, sofaRequest);
Assert.assertEquals("testValue1", sofaRequest.getRequestProp("testKey1"));
Assert.assertEquals("testValue2", sofaRequest.getRequestProp("testKey2"));
Object traceMap = sofaRequest.getRequestProp(RemotingConstants.RPC_TRACE_NAME);
Assert.assertTrue(traceMap instanceof Map);
Assert.assertEquals("traceId",((Map)traceMap).get("sofaTraceId"));
Assert.assertEquals("rpcId",((Map)traceMap).get("sofaRpcId"));
+ Object baggageMap = sofaRequest.getRequestProp(RemotingConstants.RPC_REQUEST_BAGGAGE);
+ Assert.assertTrue(baggageMap instanceof Map);
+ Assert.assertEquals("testBaggageValue1", ((Map) baggageMap).get("testBaggageKey1"));
+ Assert.assertEquals("testBaggageValue2", ((Map) baggageMap).get("testBaggageKey2"));
+
}
}
\ No newline at end of file
diff --git a/remoting/remoting-bolt/src/test/java/com/alipay/sofa/rpc/server/bolt/BoltServerTest.java b/remoting/remoting-bolt/src/test/java/com/alipay/sofa/rpc/server/bolt/BoltServerTest.java
index 148cd6b00..066aa89ad 100644
--- a/remoting/remoting-bolt/src/test/java/com/alipay/sofa/rpc/server/bolt/BoltServerTest.java
+++ b/remoting/remoting-bolt/src/test/java/com/alipay/sofa/rpc/server/bolt/BoltServerTest.java
@@ -75,4 +75,29 @@ public void start() throws Exception {
server.destroy();
}
+ @Test
+ public void threadPoolDestroyTest() {
+ String host = "127.0.0.1";
+ int port = 17702;
+ ServerConfig serverConfig = new ServerConfig();
+ serverConfig.setBoundHost(host);
+ serverConfig.setPort(port);
+ serverConfig.setProtocol(RpcConstants.PROTOCOL_TYPE_BOLT);
+
+ BoltServer server = new BoltServer();
+ server.init(serverConfig);
+ server.start();
+ Assert.assertTrue(server.started);
+ Assert.assertTrue(NetUtils.canTelnet(host, port, 1000));
+
+ server.destroy();
+ }
+
+ @Test
+ public void testDeprecatedInitThreadExecutor() {
+ BoltServer server = new BoltServer();
+ server.initThreadPool(new ServerConfig());
+ server.destroy();
+ }
+
}
\ No newline at end of file
diff --git a/remoting/remoting-bolt/src/test/resources/sofa-rpc/rpc-config.json b/remoting/remoting-bolt/src/test/resources/sofa-rpc/rpc-config.json
index 5b573549a..edd0e9cb4 100644
--- a/remoting/remoting-bolt/src/test/resources/sofa-rpc/rpc-config.json
+++ b/remoting/remoting-bolt/src/test/resources/sofa-rpc/rpc-config.json
@@ -1,4 +1,5 @@
{
"rpc.config.order": 999,
- "logger.impl": "com.alipay.sofa.rpc.log.SLF4JLoggerImpl"
+ "logger.impl": "com.alipay.sofa.rpc.log.SLF4JLoggerImpl",
+ "invoke.baggage.enable": true
}
\ No newline at end of file
diff --git a/remoting/remoting-http/src/main/java/com/alipay/sofa/rpc/transport/netty/NettyBatchWriteQueue.java b/remoting/remoting-http/src/main/java/com/alipay/sofa/rpc/transport/netty/NettyBatchWriteQueue.java
new file mode 100644
index 000000000..2ad3dbd1a
--- /dev/null
+++ b/remoting/remoting-http/src/main/java/com/alipay/sofa/rpc/transport/netty/NettyBatchWriteQueue.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.transport.netty;
+
+import com.alipay.sofa.rpc.common.BatchExecutorQueue;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelPromise;
+import io.netty.channel.EventLoop;
+
+/**
+ * @author chengming
+ * @version NettyBatchWriteQueue.java, v 0.1 2024年02月28日 5:42 PM chengming
+ */
+public class NettyBatchWriteQueue extends BatchExecutorQueue {
+
+ private final Channel channel;
+
+ private final EventLoop eventLoop;
+
+ private NettyBatchWriteQueue(Channel channel) {
+ this.channel = channel;
+ this.eventLoop = channel.eventLoop();
+ }
+
+ public ChannelFuture enqueue(Object message) {
+ return enqueue(message, channel.newPromise());
+ }
+
+ public ChannelFuture enqueue(Object message, ChannelPromise channelPromise) {
+ MessageTuple messageTuple = new MessageTuple(message, channelPromise);
+ super.enqueue(messageTuple, eventLoop);
+ return messageTuple.channelPromise;
+ }
+
+ @Override
+ protected void prepare(MessageTuple item) {
+ channel.write(item.originMessage, item.channelPromise);
+ }
+
+ @Override
+ protected void flush(MessageTuple item) {
+ prepare(item);
+ channel.flush();
+ }
+
+ public static NettyBatchWriteQueue createWriteQueue(Channel channel) {
+ return new NettyBatchWriteQueue(channel);
+ }
+
+ static class MessageTuple {
+
+ private final Object originMessage;
+
+ private final ChannelPromise channelPromise;
+
+ public MessageTuple(Object originMessage, ChannelPromise channelPromise) {
+ this.originMessage = originMessage;
+ this.channelPromise = channelPromise;
+ }
+ }
+}
diff --git a/remoting/remoting-http/src/main/java/com/alipay/sofa/rpc/transport/netty/NettyChannel.java b/remoting/remoting-http/src/main/java/com/alipay/sofa/rpc/transport/netty/NettyChannel.java
index 4b71a9ccb..590bfde39 100644
--- a/remoting/remoting-http/src/main/java/com/alipay/sofa/rpc/transport/netty/NettyChannel.java
+++ b/remoting/remoting-http/src/main/java/com/alipay/sofa/rpc/transport/netty/NettyChannel.java
@@ -48,8 +48,11 @@ public class NettyChannel extends AbstractChannel {
+ if (!future1.isSuccess()) {
+ Throwable throwable = future1.cause();
+ LOGGER.error("Failed to send to "
+ + NetUtils.channelToString(localAddress(), remoteAddress())
+ + " for msg : " + obj
+ + ", Cause by:", throwable);
}
});
}
@@ -98,4 +98,14 @@ public void operationComplete(Future future1) throws Exception {
public boolean isAvailable() {
return channel.isOpen() && channel.isActive();
}
+
+ /**
+ * UT only
+ * @param writeQueue
+ */
+ @Deprecated
+ public void setWriteQueue(NettyBatchWriteQueue writeQueue) {
+ this.writeQueue = writeQueue;
+ }
+
}
diff --git a/remoting/remoting-http/src/test/java/com/alipay/sofa/rpc/transport/netty/NettyBatchWriteQueueTest.java b/remoting/remoting-http/src/test/java/com/alipay/sofa/rpc/transport/netty/NettyBatchWriteQueueTest.java
new file mode 100644
index 000000000..911ee1065
--- /dev/null
+++ b/remoting/remoting-http/src/test/java/com/alipay/sofa/rpc/transport/netty/NettyBatchWriteQueueTest.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.transport.netty;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelPromise;
+import io.netty.channel.EventLoop;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+/**
+ * @author chengming
+ * @version NettyBatchWriteQueueTest.java, v 0.1 2024年03月01日 11:06 AM chengming
+ */
+public class NettyBatchWriteQueueTest {
+
+ private Channel mockChannel = Mockito.mock(Channel.class);
+
+ private EventLoop mockEventLoop = Mockito.mock(EventLoop.class);
+
+ private ChannelPromise mockChannelPromise = Mockito.mock(ChannelPromise.class);
+
+ private NettyBatchWriteQueue nettyBatchWriteQueue = Mockito.mock(NettyBatchWriteQueue.class);
+
+ @Before
+ public void setUp() {
+ when(mockChannel.eventLoop()).thenReturn(mockEventLoop);
+ when(mockChannel.newPromise()).thenReturn(mockChannelPromise);
+ nettyBatchWriteQueue = NettyBatchWriteQueue.createWriteQueue(mockChannel);
+ }
+
+ @Test
+ public void testEnqueue() {
+ Object message = new Object();
+ ChannelFuture future = nettyBatchWriteQueue.enqueue(message);
+ Assert.assertNotNull(future);
+
+ Mockito.verify(mockEventLoop).execute(any(Runnable.class));
+ }
+
+ @Test
+ public void testPrepare() {
+ Object message = new Object();
+ NettyBatchWriteQueue.MessageTuple messageTuple = new NettyBatchWriteQueue.MessageTuple(message,
+ mockChannelPromise);
+ nettyBatchWriteQueue.prepare(messageTuple);
+
+ Mockito.verify(mockChannel).write(eq(message), eq(mockChannelPromise));
+ }
+
+ @Test
+ public void testFlush() {
+ Object message = new Object();
+ NettyBatchWriteQueue.MessageTuple messageTuple = new NettyBatchWriteQueue.MessageTuple(message,
+ mockChannelPromise);
+ nettyBatchWriteQueue.flush(messageTuple);
+
+ Mockito.verify(mockChannel).write(eq(message), eq(mockChannelPromise));
+ Mockito.verify(mockChannel).flush();
+ }
+}
\ No newline at end of file
diff --git a/remoting/remoting-http/src/test/java/com/alipay/sofa/rpc/transport/netty/NettyChannelTest.java b/remoting/remoting-http/src/test/java/com/alipay/sofa/rpc/transport/netty/NettyChannelTest.java
new file mode 100644
index 000000000..83846eab9
--- /dev/null
+++ b/remoting/remoting-http/src/test/java/com/alipay/sofa/rpc/transport/netty/NettyChannelTest.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.transport.netty;
+
+import io.netty.buffer.PooledByteBufAllocator;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.EventLoop;
+import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.GenericFutureListener;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+/**
+ * @author chengming
+ * @version NettyChannelTest.java, v 0.1 2024年02月29日 3:18 PM chengming
+ */
+public class NettyChannelTest {
+
+ private Channel mockChannel = Mockito.mock(Channel.class);
+
+ private ChannelHandlerContext mockContext = Mockito.mock(ChannelHandlerContext.class);
+
+ private NettyBatchWriteQueue mockWriteQueue = Mockito.mock(NettyBatchWriteQueue.class);
+
+ private ChannelFuture mockFuture = Mockito.mock(ChannelFuture.class);
+
+ private NettyChannel nettyChannel;
+
+ @Before
+ public void setUp() {
+ Mockito.when(mockChannel.eventLoop()).thenReturn(Mockito.mock(EventLoop.class));
+ Mockito.when(mockChannel.alloc()).thenReturn(PooledByteBufAllocator.DEFAULT);
+ when(mockContext.channel()).thenReturn(mockChannel);
+ when(mockWriteQueue.enqueue(any())).thenReturn(mockFuture);
+
+ nettyChannel = new NettyChannel(mockChannel);
+ nettyChannel.setWriteQueue(mockWriteQueue);
+ }
+
+ @Test
+ public void testRunSuccess() throws Exception {
+ nettyChannel.writeAndFlush("111");
+
+ Mockito.verify(mockWriteQueue).enqueue("111");
+
+ ArgumentCaptor captor = ArgumentCaptor.forClass(GenericFutureListener.class);
+ Mockito.verify(mockFuture).addListener(captor.capture());
+
+ // 模拟 FutureListener 的回调
+ GenericFutureListener> listener = captor.getValue();
+ listener.operationComplete((Future) mockFuture);
+
+ // 验证没有错误日志被记录(因为操作是成功的)
+ // Mockito.verify(Mockito.mock(NetUtils.class), times(10));
+ // NetUtils.channelToString(any(InetSocketAddress.class), any(InetSocketAddress.class));
+ }
+
+}
diff --git a/remoting/remoting-triple/build/generated/source/proto/main/java/triple/GenericProto.java b/remoting/remoting-triple/build/generated/source/proto/main/java/triple/GenericProto.java
index 808f3a3a7..37f91d33e 100644
--- a/remoting/remoting-triple/build/generated/source/proto/main/java/triple/GenericProto.java
+++ b/remoting/remoting-triple/build/generated/source/proto/main/java/triple/GenericProto.java
@@ -36,9 +36,12 @@ public static void registerAllExtensions(
"\n\021transformer.proto\"@\n\007Request\022\025\n\rserial" +
"izeType\030\001 \001(\t\022\014\n\004args\030\002 \003(\014\022\020\n\010argTypes\030" +
"\003 \003(\t\"=\n\010Response\022\025\n\rserializeType\030\001 \001(\t" +
- "\022\014\n\004data\030\002 \001(\014\022\014\n\004type\030\003 \001(\t22\n\016GenericS" +
- "ervice\022 \n\007generic\022\010.Request\032\t.Response\"\000" +
- "B\030\n\006tripleB\014GenericProtoP\001b\006proto3"
+ "\022\014\n\004data\030\002 \001(\014\022\014\n\004type\030\003 \001(\t2\220\001\n\016Generic" +
+ "Service\022 \n\007generic\022\010.Request\032\t.Response\"" +
+ "\000\022,\n\017genericBiStream\022\010.Request\032\t.Respons" +
+ "e\"\000(\0010\001\022.\n\023genericServerStream\022\010.Request" +
+ "\032\t.Response\"\0000\001B\030\n\006tripleB\014GenericProtoP" +
+ "\001b\006proto3"
};
descriptor = com.google.protobuf.Descriptors.FileDescriptor
.internalBuildGeneratedFileFrom(descriptorData,
diff --git a/remoting/remoting-triple/build/generated/source/proto/main/java/triple/GenericServiceGrpc.java b/remoting/remoting-triple/build/generated/source/proto/main/java/triple/GenericServiceGrpc.java
index 75a2e5ba5..27d1c02c6 100644
--- a/remoting/remoting-triple/build/generated/source/proto/main/java/triple/GenericServiceGrpc.java
+++ b/remoting/remoting-triple/build/generated/source/proto/main/java/triple/GenericServiceGrpc.java
@@ -46,6 +46,68 @@ triple.Response> getGenericMethod() {
return getGenericMethod;
}
+ private static volatile io.grpc.MethodDescriptor getGenericBiStreamMethod;
+
+ @io.grpc.stub.annotations.RpcMethod(
+ fullMethodName = SERVICE_NAME + '/' + "genericBiStream",
+ requestType = triple.Request.class,
+ responseType = triple.Response.class,
+ methodType = io.grpc.MethodDescriptor.MethodType.BIDI_STREAMING)
+ public static io.grpc.MethodDescriptor getGenericBiStreamMethod() {
+ io.grpc.MethodDescriptor getGenericBiStreamMethod;
+ if ((getGenericBiStreamMethod = GenericServiceGrpc.getGenericBiStreamMethod) == null) {
+ synchronized (GenericServiceGrpc.class) {
+ if ((getGenericBiStreamMethod = GenericServiceGrpc.getGenericBiStreamMethod) == null) {
+ GenericServiceGrpc.getGenericBiStreamMethod = getGenericBiStreamMethod =
+ io.grpc.MethodDescriptor.newBuilder()
+ .setType(io.grpc.MethodDescriptor.MethodType.BIDI_STREAMING)
+ .setFullMethodName(generateFullMethodName(SERVICE_NAME, "genericBiStream"))
+ .setSampledToLocalTracing(true)
+ .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+ triple.Request.getDefaultInstance()))
+ .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+ triple.Response.getDefaultInstance()))
+ .setSchemaDescriptor(new GenericServiceMethodDescriptorSupplier("genericBiStream"))
+ .build();
+ }
+ }
+ }
+ return getGenericBiStreamMethod;
+ }
+
+ private static volatile io.grpc.MethodDescriptor getGenericServerStreamMethod;
+
+ @io.grpc.stub.annotations.RpcMethod(
+ fullMethodName = SERVICE_NAME + '/' + "genericServerStream",
+ requestType = triple.Request.class,
+ responseType = triple.Response.class,
+ methodType = io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING)
+ public static io.grpc.MethodDescriptor getGenericServerStreamMethod() {
+ io.grpc.MethodDescriptor getGenericServerStreamMethod;
+ if ((getGenericServerStreamMethod = GenericServiceGrpc.getGenericServerStreamMethod) == null) {
+ synchronized (GenericServiceGrpc.class) {
+ if ((getGenericServerStreamMethod = GenericServiceGrpc.getGenericServerStreamMethod) == null) {
+ GenericServiceGrpc.getGenericServerStreamMethod = getGenericServerStreamMethod =
+ io.grpc.MethodDescriptor.newBuilder()
+ .setType(io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING)
+ .setFullMethodName(generateFullMethodName(SERVICE_NAME, "genericServerStream"))
+ .setSampledToLocalTracing(true)
+ .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+ triple.Request.getDefaultInstance()))
+ .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+ triple.Response.getDefaultInstance()))
+ .setSchemaDescriptor(new GenericServiceMethodDescriptorSupplier("genericServerStream"))
+ .build();
+ }
+ }
+ }
+ return getGenericServerStreamMethod;
+ }
+
/**
* Creates a new async stub that supports all call types for the service
*/
@@ -101,6 +163,20 @@ public void generic(triple.Request request,
io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGenericMethod(), responseObserver);
}
+ /**
+ */
+ public io.grpc.stub.StreamObserver genericBiStream(
+ io.grpc.stub.StreamObserver responseObserver) {
+ return io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall(getGenericBiStreamMethod(), responseObserver);
+ }
+
+ /**
+ */
+ public void genericServerStream(triple.Request request,
+ io.grpc.stub.StreamObserver responseObserver) {
+ io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGenericServerStreamMethod(), responseObserver);
+ }
+
@java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
.addMethod(
@@ -110,6 +186,20 @@ public void generic(triple.Request request,
triple.Request,
triple.Response>(
this, METHODID_GENERIC)))
+ .addMethod(
+ getGenericBiStreamMethod(),
+ io.grpc.stub.ServerCalls.asyncBidiStreamingCall(
+ new MethodHandlers<
+ triple.Request,
+ triple.Response>(
+ this, METHODID_GENERIC_BI_STREAM)))
+ .addMethod(
+ getGenericServerStreamMethod(),
+ io.grpc.stub.ServerCalls.asyncServerStreamingCall(
+ new MethodHandlers<
+ triple.Request,
+ triple.Response>(
+ this, METHODID_GENERIC_SERVER_STREAM)))
.build();
}
}
@@ -135,6 +225,22 @@ public void generic(triple.Request request,
io.grpc.stub.ClientCalls.asyncUnaryCall(
getChannel().newCall(getGenericMethod(), getCallOptions()), request, responseObserver);
}
+
+ /**
+ */
+ public io.grpc.stub.StreamObserver genericBiStream(
+ io.grpc.stub.StreamObserver responseObserver) {
+ return io.grpc.stub.ClientCalls.asyncBidiStreamingCall(
+ getChannel().newCall(getGenericBiStreamMethod(), getCallOptions()), responseObserver);
+ }
+
+ /**
+ */
+ public void genericServerStream(triple.Request request,
+ io.grpc.stub.StreamObserver responseObserver) {
+ io.grpc.stub.ClientCalls.asyncServerStreamingCall(
+ getChannel().newCall(getGenericServerStreamMethod(), getCallOptions()), request, responseObserver);
+ }
}
/**
@@ -157,6 +263,14 @@ public triple.Response generic(triple.Request request) {
return io.grpc.stub.ClientCalls.blockingUnaryCall(
getChannel(), getGenericMethod(), getCallOptions(), request);
}
+
+ /**
+ */
+ public java.util.Iterator genericServerStream(
+ triple.Request request) {
+ return io.grpc.stub.ClientCalls.blockingServerStreamingCall(
+ getChannel(), getGenericServerStreamMethod(), getCallOptions(), request);
+ }
}
/**
@@ -183,6 +297,8 @@ public com.google.common.util.concurrent.ListenableFuture gener
}
private static final int METHODID_GENERIC = 0;
+ private static final int METHODID_GENERIC_SERVER_STREAM = 1;
+ private static final int METHODID_GENERIC_BI_STREAM = 2;
private static final class MethodHandlers implements
io.grpc.stub.ServerCalls.UnaryMethod,
@@ -205,6 +321,10 @@ public void invoke(Req request, io.grpc.stub.StreamObserver responseObserv
serviceImpl.generic((triple.Request) request,
(io.grpc.stub.StreamObserver) responseObserver);
break;
+ case METHODID_GENERIC_SERVER_STREAM:
+ serviceImpl.genericServerStream((triple.Request) request,
+ (io.grpc.stub.StreamObserver) responseObserver);
+ break;
default:
throw new AssertionError();
}
@@ -215,6 +335,9 @@ public void invoke(Req request, io.grpc.stub.StreamObserver responseObserv
public io.grpc.stub.StreamObserver invoke(
io.grpc.stub.StreamObserver responseObserver) {
switch (methodId) {
+ case METHODID_GENERIC_BI_STREAM:
+ return (io.grpc.stub.StreamObserver) serviceImpl.genericBiStream(
+ (io.grpc.stub.StreamObserver) responseObserver);
default:
throw new AssertionError();
}
@@ -267,6 +390,8 @@ public static io.grpc.ServiceDescriptor getServiceDescriptor() {
serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME)
.setSchemaDescriptor(new GenericServiceFileDescriptorSupplier())
.addMethod(getGenericMethod())
+ .addMethod(getGenericBiStreamMethod())
+ .addMethod(getGenericServerStreamMethod())
.build();
}
}
diff --git a/remoting/remoting-triple/build/generated/source/proto/main/java/triple/SofaGenericServiceTriple.java b/remoting/remoting-triple/build/generated/source/proto/main/java/triple/SofaGenericServiceTriple.java
index 0eb60e668..94f2b52bc 100644
--- a/remoting/remoting-triple/build/generated/source/proto/main/java/triple/SofaGenericServiceTriple.java
+++ b/remoting/remoting-triple/build/generated/source/proto/main/java/triple/SofaGenericServiceTriple.java
@@ -50,6 +50,23 @@ public void generic(triple.Request request, io.grpc.stub.StreamObserver genericServerStream(triple.Request request) {
+ return blockingStub
+ .withDeadlineAfter(timeout, TimeUnit.MILLISECONDS)
+ .genericServerStream(request);
+ }
+
+ public void genericServerStream(triple.Request request, io.grpc.stub.StreamObserver responseObserver) {
+ stub
+ .withDeadlineAfter(timeout, TimeUnit.MILLISECONDS)
+ .genericServerStream(request, responseObserver);
+ }
+
+ public io.grpc.stub.StreamObserver genericBiStream(io.grpc.stub.StreamObserver responseObserver) {
+ return stub
+ .withDeadlineAfter(timeout, TimeUnit.MILLISECONDS)
+ .genericBiStream(responseObserver);
+ }
}
public static SofaGenericServiceStub getSofaStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions,int timeout) {
@@ -71,6 +88,14 @@ default public com.google.common.util.concurrent.ListenableFuture responseObserver);
+ default public java.util.Iterator genericServerStream(triple.Request request) {
+ throw new UnsupportedOperationException("No need to override this method, extend XxxImplBase and override all methods it allows.");
+ }
+
+ public void genericServerStream(triple.Request request, io.grpc.stub.StreamObserver responseObserver);
+
+ public io.grpc.stub.StreamObserver genericBiStream(io.grpc.stub.StreamObserver responseObserver);
+
}
public static abstract class GenericServiceImplBase implements io.grpc.BindableService, IGenericService {
@@ -89,12 +114,25 @@ public final triple.Response generic(triple.Request request) {
@java.lang.Override
public final com.google.common.util.concurrent.ListenableFuture genericAsync(triple.Request request) {
throw new UnsupportedOperationException("No need to override this method, extend XxxImplBase and override all methods it allows.");
+ }
+
+ @java.lang.Override
+ public final java.util.Iterator genericServerStream(triple.Request request) {
+ throw new UnsupportedOperationException("No need to override this method, extend XxxImplBase and override all methods it allows.");
}
public void generic(triple.Request request,
io.grpc.stub.StreamObserver responseObserver) {
asyncUnimplementedUnaryCall(triple.GenericServiceGrpc.getGenericMethod(), responseObserver);
}
+ public io.grpc.stub.StreamObserver genericBiStream(
+ io.grpc.stub.StreamObserver responseObserver) {
+ return asyncUnimplementedStreamingCall(triple.GenericServiceGrpc.getGenericBiStreamMethod(), responseObserver);
+ }
+ public void genericServerStream(triple.Request request,
+ io.grpc.stub.StreamObserver responseObserver) {
+ asyncUnimplementedUnaryCall(triple.GenericServiceGrpc.getGenericServerStreamMethod(), responseObserver);
+ }
@java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
@@ -105,10 +143,26 @@ public void generic(triple.Request request,
triple.Request,
triple.Response>(
proxiedImpl, METHODID_GENERIC)))
+ .addMethod(
+ triple.GenericServiceGrpc.getGenericBiStreamMethod(),
+ asyncBidiStreamingCall(
+ new MethodHandlers<
+ triple.Request,
+ triple.Response>(
+ proxiedImpl, METHODID_GENERIC_BI_STREAM)))
+ .addMethod(
+ triple.GenericServiceGrpc.getGenericServerStreamMethod(),
+ asyncServerStreamingCall(
+ new MethodHandlers<
+ triple.Request,
+ triple.Response>(
+ proxiedImpl, METHODID_GENERIC_SERVER_STREAM)))
.build();
}
}
private static final int METHODID_GENERIC = 0;
+ private static final int METHODID_GENERIC_BI_STREAM = 1;
+ private static final int METHODID_GENERIC_SERVER_STREAM = 2;
private static final class MethodHandlers
implements
@@ -137,6 +191,10 @@ public void invoke(Req request, io.grpc.stub.StreamObserver
serviceImpl.generic((triple.Request) request,
(io.grpc.stub.StreamObserver) responseObserver);
break;
+ case METHODID_GENERIC_SERVER_STREAM:
+ serviceImpl.genericServerStream((triple.Request) request,
+ (io.grpc.stub.StreamObserver) responseObserver);
+ break;
default:
throw new java.lang.AssertionError();
}
@@ -148,6 +206,10 @@ public void invoke(Req request, io.grpc.stub.StreamObserver
invoke(io.grpc.stub.StreamObserver
responseObserver) {
switch (methodId) {
+ case METHODID_GENERIC_BI_STREAM:
+ return (io.grpc.stub.StreamObserver
+ ) serviceImpl.genericBiStream(
+ (io.grpc.stub.StreamObserver) responseObserver);
default:
throw new java.lang.AssertionError();
}
diff --git a/remoting/remoting-triple/pom.xml b/remoting/remoting-triple/pom.xml
index 6c9458d33..9c8dc44b7 100644
--- a/remoting/remoting-triple/pom.xml
+++ b/remoting/remoting-triple/pom.xml
@@ -66,5 +66,9 @@
com.alipay.sofatracer-core
+
+ com.alipay.sofa
+ sofa-rpc-tracer-opentracing
+
diff --git a/remoting/remoting-triple/src/main/java/com/alipay/sofa/rpc/interceptor/ClientHeaderClientInterceptor.java b/remoting/remoting-triple/src/main/java/com/alipay/sofa/rpc/interceptor/ClientHeaderClientInterceptor.java
index c7d27c801..ca63b66ba 100644
--- a/remoting/remoting-triple/src/main/java/com/alipay/sofa/rpc/interceptor/ClientHeaderClientInterceptor.java
+++ b/remoting/remoting-triple/src/main/java/com/alipay/sofa/rpc/interceptor/ClientHeaderClientInterceptor.java
@@ -16,12 +16,20 @@
*/
package com.alipay.sofa.rpc.interceptor;
+import com.alipay.common.tracer.core.context.trace.SofaTraceContext;
+import com.alipay.common.tracer.core.holder.SofaTraceContextHolder;
+import com.alipay.common.tracer.core.span.SofaTracerSpan;
import com.alipay.sofa.rpc.config.ConsumerConfig;
+import com.alipay.sofa.rpc.context.RpcInternalContext;
import com.alipay.sofa.rpc.context.RpcInvokeContext;
import com.alipay.sofa.rpc.context.RpcRunningState;
import com.alipay.sofa.rpc.core.request.SofaRequest;
+import com.alipay.sofa.rpc.core.response.SofaResponse;
+import com.alipay.sofa.rpc.event.ClientAsyncReceiveEvent;
+import com.alipay.sofa.rpc.event.EventBus;
import com.alipay.sofa.rpc.server.triple.TripleContants;
import com.alipay.sofa.rpc.tracer.sofatracer.TripleTracerAdapter;
+import com.alipay.sofa.rpc.utils.TripleExceptionUtils;
import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ClientCall;
@@ -59,12 +67,15 @@ public ClientCall interceptCall(MethodDescriptor responseListener, Metadata requestHeader) {
-
+ RpcInternalContext internalContext = RpcInternalContext.getContext();
RpcInvokeContext context = RpcInvokeContext.getContext();
SofaRequest sofaRequest = (SofaRequest) context.get(TripleContants.SOFA_REQUEST_KEY);
- ConsumerConfig consumerConfig = (ConsumerConfig) context.get(TripleContants.SOFA_CONSUMER_CONFIG_KEY);
- TripleTracerAdapter.beforeSend(sofaRequest, consumerConfig, requestHeader);
+ ConsumerConfig> consumerConfig = (ConsumerConfig>) context
+ .get(TripleContants.SOFA_CONSUMER_CONFIG_KEY);
+ TripleTracerAdapter.beforeSend(sofaRequest, consumerConfig, requestHeader, method);
+ SofaTraceContext sofaTraceContext = SofaTraceContextHolder.getSofaTraceContext();
+ SofaTracerSpan clientSpan = sofaTraceContext.getCurrentSpan();
if (RpcRunningState.isDebugMode()) {
LOGGER.info("[2]prepare to send from client:{}", requestHeader);
}
@@ -80,18 +91,48 @@ public void onHeaders(Metadata responseHeader) {
@Override
public void onMessage(RespT message) {
- if (RpcRunningState.isDebugMode()) {
- LOGGER.info("[4]response message received from server:{}", message);
+ // onMessage -> onNext()
+ try {
+ if (sofaRequest.isAsync()) {
+ RpcInvokeContext.setContext(context);
+ sofaTraceContext.push(clientSpan);
+ }
+ if (RpcRunningState.isDebugMode()) {
+ LOGGER.info("[4]response message received from server:{}", message);
+ }
+ super.onMessage(message);
+ } finally {
+ if (sofaRequest.isAsync()) {
+ sofaTraceContext.clear();
+ RpcInvokeContext.removeContext();
+ }
}
- super.onMessage(message);
}
@Override
public void onClose(Status status, Metadata trailers) {
- if (RpcRunningState.isDebugMode()) {
- LOGGER.info("[5]response close received from server:{},trailers:{}", status, trailers);
+ // onClose -> onComplete() or onError()
+ try {
+ if (sofaRequest.isAsync()) {
+ RpcInvokeContext.setContext(context);
+ sofaTraceContext.push(clientSpan);
+ }
+ if (RpcRunningState.isDebugMode()) {
+ LOGGER.info("[5]response close received from server:{},trailers:{}", status, trailers);
+ }
+ super.onClose(status, trailers);
+ } finally {
+ if (sofaRequest.isAsync()) {
+ Throwable throwable = TripleExceptionUtils.getThrowableFromStatus(status);
+ RpcInternalContext.setContext(internalContext);
+ if (EventBus.isEnable(ClientAsyncReceiveEvent.class)) {
+ EventBus.post(new ClientAsyncReceiveEvent(consumerConfig, null,
+ sofaRequest, new SofaResponse(), throwable));
+ }
+ RpcInvokeContext.removeContext();
+ RpcInternalContext.removeAllContext();
+ }
}
- super.onClose(status, trailers);
}
@Override
@@ -104,6 +145,15 @@ public void onReady() {
}, requestHeader);
}
+ @Override
+ public void sendMessage(ReqT message) {
+ try {
+ super.sendMessage(message);
+ } catch (Throwable t) {
+ LOGGER.error("Client invoke grpc sendMessage meet error:", t);
+ throw t;
+ }
+ }
};
}
}
\ No newline at end of file
diff --git a/remoting/remoting-triple/src/main/java/com/alipay/sofa/rpc/interceptor/ServerReqHeaderInterceptor.java b/remoting/remoting-triple/src/main/java/com/alipay/sofa/rpc/interceptor/ServerReqHeaderInterceptor.java
index b531ae5de..e80444d16 100644
--- a/remoting/remoting-triple/src/main/java/com/alipay/sofa/rpc/interceptor/ServerReqHeaderInterceptor.java
+++ b/remoting/remoting-triple/src/main/java/com/alipay/sofa/rpc/interceptor/ServerReqHeaderInterceptor.java
@@ -21,7 +21,6 @@
import com.alipay.common.tracer.core.span.SofaTracerSpan;
import com.alipay.sofa.rpc.context.RpcInvokeContext;
import com.alipay.sofa.rpc.context.RpcRunningState;
-import com.alipay.sofa.rpc.context.RpcRuntimeContext;
import com.alipay.sofa.rpc.core.request.SofaRequest;
import com.alipay.sofa.rpc.core.response.SofaResponse;
import com.alipay.sofa.rpc.tracer.sofatracer.TracingContextKey;
@@ -57,114 +56,178 @@ public ServerReqHeaderInterceptor(ServerServiceDefinition serverServiceDefinitio
public ServerCall.Listener interceptCall(final ServerCall call,
final Metadata requestHeaders,
ServerCallHandler next) {
- final ServerServiceDefinition serverServiceDefinition = this.getServerServiceDefinition();
+ try {
+ final ServerServiceDefinition serverServiceDefinition = this.getServerServiceDefinition();
- SofaResponse sofaResponse = new SofaResponse();
- final Throwable[] throwable = { null };
- SofaRequest sofaRequest = new SofaRequest();
- TripleTracerAdapter.serverReceived(sofaRequest, serverServiceDefinition, call, requestHeaders);
- SofaTraceContext sofaTraceContext = SofaTraceContextHolder.getSofaTraceContext();
- SofaTracerSpan serverSpan = sofaTraceContext.getCurrentSpan();
+ SofaResponse sofaResponse = new SofaResponse();
+ SofaRequest sofaRequest = new SofaRequest();
- Context ctxWithSpan = Context.current()
- .withValue(TracingContextKey.getKey(), serverSpan)
- .withValue(TracingContextKey.getSpanContextKey(), serverSpan.context())
- .withValue(TracingContextKey.getKeySofaRequest(), sofaRequest);
+ Context ctxWithSpan = convertHeaderToContext(call, requestHeaders, sofaRequest, serverServiceDefinition);
- //这里和下面不在一个线程
- if (RpcRunningState.isDebugMode()) {
- LOGGER.info("[1]header received from client:" + requestHeaders);
- }
+ //这里和下面不在一个线程
+ if (RpcRunningState.isDebugMode()) {
+ LOGGER.info("[1]header received from client:{}", requestHeaders);
+ }
- ServerCall realCall = new ForwardingServerCall.SimpleForwardingServerCall(call) {
- @Override
- public void sendHeaders(Metadata responseHeaders) {
- if (RpcRunningState.isDebugMode()) {
- LOGGER.info("[4]send response header:{}", responseHeaders);
+ ServerCall realCall = new ForwardingServerCall.SimpleForwardingServerCall(call) {
+ @Override
+ public void sendHeaders(Metadata responseHeaders) {
+ try {
+ if (RpcRunningState.isDebugMode()) {
+ LOGGER.info("[4]send response header:{}", responseHeaders);
+ }
+ super.sendHeaders(responseHeaders);
+ } catch (Throwable t) {
+ LOGGER.error("Server invoke grpc sendHeaders meet error:", t);
+ throw t;
+ }
}
- super.sendHeaders(responseHeaders);
- }
- //服务端发完了
- @Override
- public void sendMessage(RespT message) {
- if (RpcRunningState.isDebugMode()) {
- LOGGER.info("[5]send response message:{}", message);
+ //服务端发完了
+ @Override
+ public void sendMessage(RespT message) {
+ try {
+ if (RpcRunningState.isDebugMode()) {
+ LOGGER.info("[5]send response message:{}", message);
+ }
+ super.sendMessage(message);
+
+ sofaResponse.setAppResponse(message);
+ } catch (Throwable t) {
+ LOGGER.error("Server invoke grpc sendMessage meet error:", t);
+ throw t;
+ }
}
- super.sendMessage(message);
- sofaResponse.setAppResponse(message);
- }
+ @Override
+ public void close(Status status, Metadata trailers) {
+ // onError -> close
+ try {
+ if (RpcRunningState.isDebugMode()) {
+ LOGGER.info("[6]send response message:{},trailers:{}", status, trailers);
+ }
+ if (status.getCause() != null) {
+ status = status.withDescription(status.getCause().getMessage());
+ }
+ super.close(status, trailers);
+ } finally {
+ if (!status.isOk()) {
+ Throwable cause = status.getCause();
+ TripleTracerAdapter.serverSend(sofaRequest, requestHeaders, sofaResponse, cause,
+ ctxWithSpan);
+ }
+ }
- @Override
- public void close(Status status, Metadata trailers) {
- if (RpcRunningState.isDebugMode()) {
- LOGGER.info("[6]send response message:{},trailers:{}", status, trailers);
}
- super.close(status, trailers);
+ };
+ ServerCall.Listener listenerWithContext;
+ try {
+ listenerWithContext = Contexts.interceptCall(ctxWithSpan, realCall, requestHeaders, next);
+ } catch (Throwable t) {
+ LOGGER.error("Server invoke grpc interceptCall meet error:", t);
+ TripleTracerAdapter.serverSend(sofaRequest, requestHeaders, sofaResponse, t, ctxWithSpan);
+ Status status = Status.UNKNOWN.withDescription(t.getMessage()).withCause(t);
+ throw new StatusRuntimeException(status);
}
- };
-
- ServerCall.Listener listenerWithContext =
- Contexts.interceptCall(ctxWithSpan, realCall, requestHeaders, next);
-
- ForwardingServerCallListener.SimpleForwardingServerCallListener result = new ForwardingServerCallListener.SimpleForwardingServerCallListener(
- listenerWithContext) {
+ RpcInvokeContext invokeContext = RpcInvokeContext.getContext();
+ return new ForwardingServerCallListener.SimpleForwardingServerCallListener(
+ listenerWithContext) {
+
+ @Override
+ public void onCancel() {
+ // onCancel -> onError()
+ try {
+ SofaTraceContext sofaTraceContext = SofaTraceContextHolder.getSofaTraceContext();
+ SofaTracerSpan originalSpan = (SofaTracerSpan) TracingContextKey.getKey().get(ctxWithSpan);
+ sofaTraceContext.push(originalSpan);
+ RpcInvokeContext.setContext(invokeContext);
+ super.onCancel();
+ } finally {
+ TripleTracerAdapter.serverSend(sofaRequest, requestHeaders, sofaResponse,
+ new StatusRuntimeException(Status.CANCELLED, new Metadata()), ctxWithSpan);
+ }
+ }
- //完成的时候走到这里
- @Override
- public void onComplete() {
- // 和代码执行不一定在一个线程池
- super.onComplete();
- if (RpcRunningState.isDebugMode()) {
- LOGGER.info("[7]server processed done received from client:" + requestHeaders);
+ //完成的时候走到这里
+ @Override
+ public void onComplete() {
+ try {
+ // 和代码执行不一定在一个线程池
+ super.onComplete();
+ if (RpcRunningState.isDebugMode()) {
+ LOGGER.info("[7]server processed done received from client:" + requestHeaders);
+ }
+ } finally {
+ TripleTracerAdapter.serverSend(sofaRequest, requestHeaders, sofaResponse, null, ctxWithSpan);
+ }
}
- TripleTracerAdapter.serverReceived(sofaRequest, serverServiceDefinition, call, requestHeaders);
-
- //进行一下补偿
- SofaTraceContext sofaTraceContext = SofaTraceContextHolder.getSofaTraceContext();
- SofaTracerSpan serverSpan = sofaTraceContext.getCurrentSpan();
- SofaTracerSpan originalSpan = (SofaTracerSpan) TracingContextKey.getKey().get(ctxWithSpan);
- serverSpan.setStartTime(originalSpan.getStartTime());
- serverSpan.setTag("remote.ip", originalSpan.getTagsWithStr().get("remote.ip"));
- long endTime = RpcRuntimeContext.now();
- serverSpan.setTag("biz.impl.time", endTime - originalSpan.getStartTime());
- TripleTracerAdapter.serverSend(sofaRequest, requestHeaders, sofaResponse, throwable[0]);
- }
- //客户端发完了
- @Override
- public void onHalfClose() {
- try {
- doOnHalfClose();
- } finally {
- RpcInvokeContext.removeContext();
+ @Override
+ public void onMessage(ReqT message) {
+ // onMessage -> onNext()
+ SofaTraceContext sofaTraceContext = SofaTraceContextHolder.getSofaTraceContext();
+ try {
+ SofaTracerSpan originalSpan = (SofaTracerSpan) TracingContextKey.getKey().get(ctxWithSpan);
+ sofaTraceContext.push(originalSpan);
+ RpcInvokeContext.setContext(invokeContext);
+ super.onMessage(message);
+ } catch (Throwable t) {
+ LOGGER.error("Server invoke grpc onMessage meet error:", t);
+ throw t;
+ } finally {
+ RpcInvokeContext.removeContext();
+ sofaTraceContext.clear();
+ }
}
- }
- private void doOnHalfClose() {
- if (RpcRunningState.isDebugMode()) {
- LOGGER.info("[2]body received done from client:" + requestHeaders);
+ //客户端发完了
+ @Override
+ public void onHalfClose() {
+ // onHalfClose -> onComplete() -> close
+ SofaTraceContext sofaTraceContext = SofaTraceContextHolder.getSofaTraceContext();
+ try {
+ SofaTracerSpan originalSpan = (SofaTracerSpan) TracingContextKey.getKey().get(ctxWithSpan);
+ sofaTraceContext.push(originalSpan);
+ RpcInvokeContext.setContext(invokeContext);
+ doOnHalfClose();
+ } finally {
+ RpcInvokeContext.removeContext();
+ sofaTraceContext.clear();
+ }
}
- // 服务端收到所有信息
- TripleTracerAdapter.serverReceived(sofaRequest, serverServiceDefinition, call, requestHeaders);
- try {
- super.onHalfClose();
- } catch (Throwable t) {
- // 统一处理异常
- StatusRuntimeException exception = fromThrowable(t);
- // 调用 call.close() 发送 Status 和 metadata
- // 这个方式和 onError()本质是一样的
- call.close(exception.getStatus(), exception.getTrailers());
- throwable[0] = t;
+
+ private void doOnHalfClose() {
+ if (RpcRunningState.isDebugMode()) {
+ LOGGER.info("[2]body received done from client:" + requestHeaders);
+ }
+ try {
+ super.onHalfClose();
+ } catch (Throwable t) {
+ // 统一处理异常
+ final Metadata trailers = new Metadata();
+ Status status = Status.UNKNOWN.withDescription(t.getMessage()).withCause(t);
+ realCall.close(status, trailers);
+ }
}
- }
+ };
+ } finally {
+ RpcInvokeContext.removeContext();
+ SofaTraceContextHolder.getSofaTraceContext().clear();
+ }
+ }
- private StatusRuntimeException fromThrowable(Throwable t) {
- final Metadata trailers = new Metadata();
- return new StatusRuntimeException(Status.UNKNOWN, trailers);
- }
- };
- return result;
+ protected Context convertHeaderToContext(ServerCall call,
+ Metadata requestHeaders, SofaRequest sofaRequest,
+ ServerServiceDefinition serverServiceDefinition) {
+ TripleTracerAdapter.serverReceived(sofaRequest, serverServiceDefinition, call, requestHeaders);
+ String userId = TripleTracerAdapter.getUserId(requestHeaders);
+ SofaTraceContext sofaTraceContext = SofaTraceContextHolder.getSofaTraceContext();
+ SofaTracerSpan serverSpan = sofaTraceContext.getCurrentSpan();
+ return Context.current()
+ .withValue(TracingContextKey.getKey(), serverSpan)
+ .withValue(TracingContextKey.getSpanContextKey(), serverSpan.context())
+ .withValue(TracingContextKey.getKeySofaRequest(), sofaRequest)
+ .withValue(TracingContextKey.getKeyMetadata(), requestHeaders)
+ .withValue(TracingContextKey.getKeyUserId(), userId);
}
}
\ No newline at end of file
diff --git a/remoting/remoting-triple/src/main/java/com/alipay/sofa/rpc/message/triple/stream/ClientStreamObserverAdapter.java b/remoting/remoting-triple/src/main/java/com/alipay/sofa/rpc/message/triple/stream/ClientStreamObserverAdapter.java
new file mode 100644
index 000000000..ff72fa773
--- /dev/null
+++ b/remoting/remoting-triple/src/main/java/com/alipay/sofa/rpc/message/triple/stream/ClientStreamObserverAdapter.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.rpc.message.triple.stream;
+
+import com.alipay.sofa.rpc.codec.Serializer;
+import com.alipay.sofa.rpc.codec.SerializerFactory;
+import com.alipay.sofa.rpc.core.exception.RpcErrorType;
+import com.alipay.sofa.rpc.core.exception.SofaRpcException;
+import com.alipay.sofa.rpc.transport.ByteArrayWrapperByteBuf;
+import com.alipay.sofa.rpc.transport.SofaStreamObserver;
+import io.grpc.stub.StreamObserver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * ClientStreamObserverAdapter.
+ */
+public class ClientStreamObserverAdapter implements StreamObserver {
+
+ public static final Logger LOGGER = LoggerFactory.getLogger(ClientStreamObserverAdapter.class);
+
+ private final SofaStreamObserver