From e5a3c5a0a5eeda3a2e078d3c5a589771696aa1da Mon Sep 17 00:00:00 2001 From: yuanyuan Date: Mon, 25 Dec 2023 18:04:17 +0800 Subject: [PATCH 01/13] adapter dubbo 27 --- .../rpc/dubbo27/dubbo27base/pom.xml | 15 +- .../dubbo27/base/Dubb27BaseApplication.java | 14 +- .../base/controller/MasterController.java | 41 +++ .../base/service/MasterDemoServiceImpl.java | 24 ++ .../src/main/resources/provider.xml | 28 ++ .../src/main/resources/provider.xml | 4 +- samples/dubbo-samples/rpc/dubbo27/pom.xml | 2 +- sofa-serverless-runtime/pom.xml | 2 +- .../sofa-serverless-adapter-ext/pom.xml | 1 + .../sofa-serverless-adapter-dubbo2.7/pom.xml | 29 ++ .../AnnotatedBeanDefinitionRegistryUtils.java | 257 ++++++++++++++++++ .../dubbo/BizDubboBootstrapListener.java | 63 +++++ .../support/dubbo/ReflectionUtils.java | 69 +++++ .../dubbo/ServerlessConfigManager.java | 222 +++++++++++++++ ...g.apache.dubbo.common.context.FrameworkExt | 1 + .../main/resources/META-INF/spring.factories | 2 + 16 files changed, 764 insertions(+), 10 deletions(-) create mode 100644 samples/dubbo-samples/rpc/dubbo27/dubbo27base/src/main/java/com/alipay/sofa/rpc/dubbo27/base/controller/MasterController.java create mode 100644 samples/dubbo-samples/rpc/dubbo27/dubbo27base/src/main/java/com/alipay/sofa/rpc/dubbo27/base/service/MasterDemoServiceImpl.java create mode 100644 samples/dubbo-samples/rpc/dubbo27/dubbo27base/src/main/resources/provider.xml create mode 100644 sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/pom.xml create mode 100644 sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alibaba/spring/util/AnnotatedBeanDefinitionRegistryUtils.java create mode 100644 sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/BizDubboBootstrapListener.java create mode 100644 sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/ReflectionUtils.java create mode 100644 sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/ServerlessConfigManager.java create mode 100644 sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/resources/META-INF/services/org.apache.dubbo.common.context.FrameworkExt create mode 100644 sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/resources/META-INF/spring.factories diff --git a/samples/dubbo-samples/rpc/dubbo27/dubbo27base/pom.xml b/samples/dubbo-samples/rpc/dubbo27/dubbo27base/pom.xml index e156ab19d..2256e0d23 100644 --- a/samples/dubbo-samples/rpc/dubbo27/dubbo27base/pom.xml +++ b/samples/dubbo-samples/rpc/dubbo27/dubbo27base/pom.xml @@ -42,6 +42,11 @@ sofa-serverless-base-starter ${sofa.serverless.runtime.version} + + com.alipay.sofa.serverless + sofa-serverless-adapter-dubbo2.7 + ${sofa.serverless.runtime.version} + com.lmax disruptor @@ -52,11 +57,11 @@ 0.0.1-SNAPSHOT - - com.alipay.sofa.serverless - sofa-serverless-adapter-dubbo27 - 0.5.1 - + + + + + org.apache.dubbo dubbo-spring-boot-starter diff --git a/samples/dubbo-samples/rpc/dubbo27/dubbo27base/src/main/java/com/alipay/sofa/rpc/dubbo27/base/Dubb27BaseApplication.java b/samples/dubbo-samples/rpc/dubbo27/dubbo27base/src/main/java/com/alipay/sofa/rpc/dubbo27/base/Dubb27BaseApplication.java index 444dff7b0..31441c2b9 100644 --- a/samples/dubbo-samples/rpc/dubbo27/dubbo27base/src/main/java/com/alipay/sofa/rpc/dubbo27/base/Dubb27BaseApplication.java +++ b/samples/dubbo-samples/rpc/dubbo27/dubbo27base/src/main/java/com/alipay/sofa/rpc/dubbo27/base/Dubb27BaseApplication.java @@ -7,13 +7,18 @@ import java.io.File; import com.alipay.sofa.ark.api.ArkClient; +import com.alipay.sofa.rpc.dubbo27.base.controller.MasterController; +import com.alipay.sofa.rpc.dubbo27.model.DemoResponse; import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.ImportResource; /** * @@ -22,9 +27,13 @@ */ @SpringBootApplication +@ImportResource("classpath:provider.xml") public class Dubb27BaseApplication implements CommandLineRunner { private static Logger LOGGER = LoggerFactory.getLogger(Dubb27BaseApplication.class); +// @Autowired +// private MasterController masterController; + public static void main(String[] args) { //Prevent to get IPV6 address,this way only work in debug mode @@ -35,7 +44,10 @@ public static void main(String[] args) { System.setProperty("sofa.ark.embed.enable", "true"); System.setProperty("sofa.ark.plugin.export.class.enable", "true"); - SpringApplication.run(Dubb27BaseApplication.class, args); + ConfigurableApplicationContext context = SpringApplication.run(Dubb27BaseApplication.class, args); +// MasterController controller = context.getBean(MasterController.class); +// DemoResponse xxx = controller.handle("xxx"); +// System.out.println(xxx); } /** diff --git a/samples/dubbo-samples/rpc/dubbo27/dubbo27base/src/main/java/com/alipay/sofa/rpc/dubbo27/base/controller/MasterController.java b/samples/dubbo-samples/rpc/dubbo27/dubbo27base/src/main/java/com/alipay/sofa/rpc/dubbo27/base/controller/MasterController.java new file mode 100644 index 000000000..8f582c65f --- /dev/null +++ b/samples/dubbo-samples/rpc/dubbo27/dubbo27base/src/main/java/com/alipay/sofa/rpc/dubbo27/base/controller/MasterController.java @@ -0,0 +1,41 @@ +package com.alipay.sofa.rpc.dubbo27.base.controller; + +import com.alipay.sofa.rpc.dubbo27.model.DemoRequest; +import com.alipay.sofa.rpc.dubbo27.model.DemoResponse; +import com.alipay.sofa.rpc.dubbo27.model.DemoService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +/** + * @author: yuanyuan + * @date: 2023/12/22 4:12 下午 + */ +@RestController +public class MasterController { + + @Resource + private DemoService demoServiceRef; + + @Autowired + private ApplicationContext applicationContext; + + @RequestMapping(value = "/", method = RequestMethod.GET) + @ResponseBody + public DemoResponse handle(String ref) { + String appName = applicationContext.getId(); + DemoRequest demoRequest = new DemoRequest(); + demoRequest.setBiz(appName); + return demoServiceRef.handle(demoRequest); + } + + public void setDemoServiceRef(DemoService demoServiceRef) { + this.demoServiceRef = demoServiceRef; + } + +} diff --git a/samples/dubbo-samples/rpc/dubbo27/dubbo27base/src/main/java/com/alipay/sofa/rpc/dubbo27/base/service/MasterDemoServiceImpl.java b/samples/dubbo-samples/rpc/dubbo27/dubbo27base/src/main/java/com/alipay/sofa/rpc/dubbo27/base/service/MasterDemoServiceImpl.java new file mode 100644 index 000000000..8407222f7 --- /dev/null +++ b/samples/dubbo-samples/rpc/dubbo27/dubbo27base/src/main/java/com/alipay/sofa/rpc/dubbo27/base/service/MasterDemoServiceImpl.java @@ -0,0 +1,24 @@ +package com.alipay.sofa.rpc.dubbo27.base.service; + +import com.alipay.sofa.rpc.dubbo27.model.DemoRequest; +import com.alipay.sofa.rpc.dubbo27.model.DemoResponse; +import com.alipay.sofa.rpc.dubbo27.model.DemoService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; + +/** + * @author: yuanyuan + * @date: 2023/12/22 11:39 上午 + */ +public class MasterDemoServiceImpl implements DemoService { + + @Autowired + private ApplicationContext applicationContext; + + @Override + public DemoResponse handle(DemoRequest demoRequest) { + DemoResponse response = new DemoResponse(); + response.setResult(demoRequest.getBiz() + "->" + getClass().getName()); + return response; + } +} diff --git a/samples/dubbo-samples/rpc/dubbo27/dubbo27base/src/main/resources/provider.xml b/samples/dubbo-samples/rpc/dubbo27/dubbo27base/src/main/resources/provider.xml new file mode 100644 index 000000000..dcef24b0b --- /dev/null +++ b/samples/dubbo-samples/rpc/dubbo27/dubbo27base/src/main/resources/provider.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/dubbo-samples/rpc/dubbo27/dubbo27biz/src/main/resources/provider.xml b/samples/dubbo-samples/rpc/dubbo27/dubbo27biz/src/main/resources/provider.xml index 5731cd38b..389a03aa5 100644 --- a/samples/dubbo-samples/rpc/dubbo27/dubbo27biz/src/main/resources/provider.xml +++ b/samples/dubbo-samples/rpc/dubbo27/dubbo27biz/src/main/resources/provider.xml @@ -16,13 +16,13 @@ - + - + \ No newline at end of file diff --git a/samples/dubbo-samples/rpc/dubbo27/pom.xml b/samples/dubbo-samples/rpc/dubbo27/pom.xml index 88fd0ecff..fe2432b76 100644 --- a/samples/dubbo-samples/rpc/dubbo27/pom.xml +++ b/samples/dubbo-samples/rpc/dubbo27/pom.xml @@ -20,7 +20,7 @@ 2.7.23 2.7.16 2.2.5-SNAPSHOT - 0.5.0 + 0.5.6-SNAPSHOT 3.4.2 3.8.1 diff --git a/sofa-serverless-runtime/pom.xml b/sofa-serverless-runtime/pom.xml index 7deae8aef..c2cfa673d 100644 --- a/sofa-serverless-runtime/pom.xml +++ b/sofa-serverless-runtime/pom.xml @@ -12,7 +12,7 @@ 2.2.5-SNAPSHOT 2.7.15 1.2.9 - 0.5.5-SNAPSHOT + 0.5.6-SNAPSHOT UTF-8 UTF-8 1.8 diff --git a/sofa-serverless-runtime/sofa-serverless-adapter-ext/pom.xml b/sofa-serverless-runtime/sofa-serverless-adapter-ext/pom.xml index 968951fe9..af496963b 100644 --- a/sofa-serverless-runtime/sofa-serverless-adapter-ext/pom.xml +++ b/sofa-serverless-runtime/sofa-serverless-adapter-ext/pom.xml @@ -15,5 +15,6 @@ sofa-serverless-adapter-log4j2 sofa-serverless-adapter-apollo sofa-serverless-adapter-dubbo2.6 + sofa-serverless-adapter-dubbo2.7 diff --git a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/pom.xml b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/pom.xml new file mode 100644 index 000000000..e15328fa1 --- /dev/null +++ b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/pom.xml @@ -0,0 +1,29 @@ + + + + sofa-serverless-adapter-ext + com.alipay.sofa.serverless + ${revision} + ../pom.xml + + 4.0.0 + + sofa-serverless-adapter-dubbo2.7 + ${revision} + + + 8 + 8 + + + + + org.apache.dubbo + dubbo-spring-boot-starter + 2.7.23 + + + + \ No newline at end of file diff --git a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alibaba/spring/util/AnnotatedBeanDefinitionRegistryUtils.java b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alibaba/spring/util/AnnotatedBeanDefinitionRegistryUtils.java new file mode 100644 index 000000000..3cba2203d --- /dev/null +++ b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alibaba/spring/util/AnnotatedBeanDefinitionRegistryUtils.java @@ -0,0 +1,257 @@ +/* + * 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.alibaba.spring.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanDefinitionHolder; +import org.springframework.beans.factory.config.SingletonBeanRegistry; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanNameGenerator; +import org.springframework.context.annotation.AnnotatedBeanDefinitionReader; +import org.springframework.context.annotation.AnnotationBeanNameGenerator; +import org.springframework.context.annotation.AnnotationConfigUtils; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.annotation.ConfigurationClassPostProcessor; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import static com.alibaba.spring.util.ClassUtils.EMPTY_CLASS_ARRAY; +import static java.lang.String.format; +import static java.util.Arrays.asList; +import static org.springframework.context.annotation.AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR; +import static org.springframework.util.ClassUtils.resolveClassName; +import static org.springframework.util.ObjectUtils.nullSafeEquals; + +/** + * Annotated {@link BeanDefinition} Utilities + * + * @author Mercy + * @see BeanDefinition + * @since 2017.01.09 + */ +public abstract class AnnotatedBeanDefinitionRegistryUtils { + + private static final Log logger = LogFactory.getLog(AnnotatedBeanDefinitionRegistryUtils.class); + + /** + * Is present bean that was registered by the specified {@link Annotation annotated} {@link Class class} + * + * @param registry {@link BeanDefinitionRegistry} + * @param annotatedClass the {@link Annotation annotated} {@link Class class} + * @return if present, return true, or false + * @since 1.0.3 + */ + public static boolean isPresentBean(BeanDefinitionRegistry registry, Class annotatedClass) { + + boolean present = false; + + String[] beanNames = registry.getBeanDefinitionNames(); + + // ClassLoader classLoader = annotatedClass.getClassLoader(); + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + + for (String beanName : beanNames) { + BeanDefinition beanDefinition = registry.getBeanDefinition(beanName); + if (beanDefinition instanceof AnnotatedBeanDefinition) { + AnnotationMetadata annotationMetadata = ((AnnotatedBeanDefinition) beanDefinition) + .getMetadata(); + String className = annotationMetadata.getClassName(); + Class targetClass = resolveClassName(className, classLoader); + present = nullSafeEquals(targetClass, annotatedClass); + if (present) { + if (logger.isDebugEnabled()) { + logger + .debug(format( + "The annotatedClass[class : %s , bean name : %s] was present in registry[%s]", + className, beanName, registry)); + } + break; + } + } + } + + return present; + } + + /** + * Register Beans if not present in {@link BeanDefinitionRegistry registry} + * + * @param registry {@link BeanDefinitionRegistry} + * @param annotatedClasses {@link Annotation annotation} class + */ + public static void registerBeans(BeanDefinitionRegistry registry, Class... annotatedClasses) { + + if (ObjectUtils.isEmpty(annotatedClasses)) { + return; + } + + Set> classesToRegister = new LinkedHashSet>(asList(annotatedClasses)); + + // Remove all annotated-classes that have been registered + Iterator> iterator = classesToRegister.iterator(); + + while (iterator.hasNext()) { + Class annotatedClass = iterator.next(); + if (isPresentBean(registry, annotatedClass)) { + iterator.remove(); + } + } + + AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(registry); + + if (logger.isDebugEnabled()) { + logger.debug(registry.getClass().getSimpleName() + + " will register annotated classes : " + asList(annotatedClasses) + " ."); + } + + reader.register(classesToRegister.toArray(EMPTY_CLASS_ARRAY)); + + } + + /** + * Scan base packages for register {@link Component @Component}s + * + * @param registry {@link BeanDefinitionRegistry} + * @param basePackages base packages + * @return the count of registered components. + */ + public static int scanBasePackages(BeanDefinitionRegistry registry, String... basePackages) { + + int count = 0; + + if (!ObjectUtils.isEmpty(basePackages)) { + + boolean debugEnabled = logger.isDebugEnabled(); + + if (debugEnabled) { + logger.debug(registry.getClass().getSimpleName() + " will scan base packages " + + Arrays.asList(basePackages) + "."); + } + + List registeredBeanNames = Arrays.asList(registry.getBeanDefinitionNames()); + + ClassPathBeanDefinitionScanner classPathBeanDefinitionScanner = new ClassPathBeanDefinitionScanner( + registry); + count = classPathBeanDefinitionScanner.scan(basePackages); + + List scannedBeanNames = new ArrayList(count); + scannedBeanNames.addAll(Arrays.asList(registry.getBeanDefinitionNames())); + scannedBeanNames.removeAll(registeredBeanNames); + + if (debugEnabled) { + logger.debug("The Scanned Components[ count : " + count + "] under base packages " + + Arrays.asList(basePackages) + " : "); + } + + for (String scannedBeanName : scannedBeanNames) { + BeanDefinition scannedBeanDefinition = registry.getBeanDefinition(scannedBeanName); + if (debugEnabled) { + logger.debug("Component [ name : " + scannedBeanName + " , class : " + + scannedBeanDefinition.getBeanClassName() + " ]"); + } + } + } + + return count; + + } + + /** + * It'd better to use BeanNameGenerator instance that should reference + * {@link ConfigurationClassPostProcessor#componentScanBeanNameGenerator}, + * thus it maybe a potential problem on bean name generation. + * + * @param registry {@link BeanDefinitionRegistry} + * @return try to find the {@link BeanNameGenerator} bean named {@link AnnotationConfigUtils#CONFIGURATION_BEAN_NAME_GENERATOR}, + * if it can't be found, return an instance of {@link AnnotationBeanNameGenerator} + * @see SingletonBeanRegistry + * @see AnnotationConfigUtils#CONFIGURATION_BEAN_NAME_GENERATOR + * @see ConfigurationClassPostProcessor#processConfigBeanDefinitions + * @since 1.0.6 + */ + public static BeanNameGenerator resolveAnnotatedBeanNameGenerator(BeanDefinitionRegistry registry) { + BeanNameGenerator beanNameGenerator = null; + + if (registry instanceof SingletonBeanRegistry) { + SingletonBeanRegistry singletonBeanRegistry = SingletonBeanRegistry.class + .cast(registry); + beanNameGenerator = (BeanNameGenerator) singletonBeanRegistry + .getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR); + } + + if (beanNameGenerator == null) { + + if (logger.isInfoEnabled()) { + + logger.info("BeanNameGenerator bean can't be found in BeanFactory with name [" + + CONFIGURATION_BEAN_NAME_GENERATOR + "]"); + logger.info("BeanNameGenerator will be a instance of " + + AnnotationBeanNameGenerator.class.getName() + + " , it maybe a potential problem on bean name generation."); + } + + beanNameGenerator = new AnnotationBeanNameGenerator(); + + } + + return beanNameGenerator; + } + + /** + * Finds a {@link Set} of {@link BeanDefinitionHolder BeanDefinitionHolders} + * + * @param scanner {@link ClassPathBeanDefinitionScanner} + * @param packageToScan package to scan + * @param registry {@link BeanDefinitionRegistry} + * @param beanNameGenerator {@link BeanNameGenerator} + * @return non-null + */ + public static Set findBeanDefinitionHolders(ClassPathBeanDefinitionScanner scanner, + String packageToScan, + BeanDefinitionRegistry registry, + BeanNameGenerator beanNameGenerator) { + + Set beanDefinitions = scanner.findCandidateComponents(packageToScan); + + Set beanDefinitionHolders = new LinkedHashSet( + beanDefinitions.size()); + + for (BeanDefinition beanDefinition : beanDefinitions) { + + String beanName = beanNameGenerator.generateBeanName(beanDefinition, registry); + BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(beanDefinition, + beanName); + beanDefinitionHolders.add(beanDefinitionHolder); + + } + + return beanDefinitionHolders; + + } +} diff --git a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/BizDubboBootstrapListener.java b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/BizDubboBootstrapListener.java new file mode 100644 index 000000000..bf5468c0b --- /dev/null +++ b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/BizDubboBootstrapListener.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.serverless.support.dubbo; + +import org.apache.dubbo.config.bootstrap.DubboBootstrap; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; + +import java.lang.reflect.Method; + +/** + * @author: yuanyuan + * @date: 2023/12/25 4:14 下午 + */ +public class BizDubboBootstrapListener implements ApplicationListener { + + private final DubboBootstrap dubboBootstrap; + + // private final ConfigManager configManager; + // private final Environment environment; + + public BizDubboBootstrapListener() { + this.dubboBootstrap = DubboBootstrap.getInstance(); + // this.configManager = ApplicationModel.getConfigManager(); + // this.environment = ApplicationModel.getEnvironment(); + } + + @Override + public void onApplicationEvent(ApplicationEvent event) { + if (Thread.currentThread().getContextClassLoader() == this.getClass().getClassLoader()) { + return; + } + if (event instanceof ContextRefreshedEvent) { + onContextRefreshedEvent((ContextRefreshedEvent) event); + } + } + + private void onContextRefreshedEvent(ContextRefreshedEvent event) { + Method exportServices = ReflectionUtils.getMethod(DubboBootstrap.class, "exportServices"); + try { + ReflectionUtils.getMethod(DubboBootstrap.class, "exportServices") + .invoke(dubboBootstrap); + ReflectionUtils.getMethod(DubboBootstrap.class, "referServices").invoke(dubboBootstrap); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/ReflectionUtils.java b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/ReflectionUtils.java new file mode 100644 index 000000000..1f79fc239 --- /dev/null +++ b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/ReflectionUtils.java @@ -0,0 +1,69 @@ +/* + * 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.serverless.support.dubbo; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * @author: yuanyuan + * @date: 2023/12/25 4:24 下午 + */ +public class ReflectionUtils { + + public static T getField(Object target, Class clazz, String fieldName) { + try { + Field filed = clazz.getDeclaredField(fieldName); + filed.setAccessible(true); + Object value = filed.get(target); + return value == null ? null : (T) value; + } catch (Exception e) { + throw new RuntimeException( + "get field " + clazz.getName() + "@" + fieldName + " failed", e); + } + } + + public static void setField(Object target, Class clazz, String fieldName, T value) { + try { + Field filed = clazz.getDeclaredField(fieldName); + filed.setAccessible(true); + filed.set(target, value); + } catch (Exception e) { + throw new RuntimeException( + "set filed " + clazz.getName() + "@" + fieldName + " failed", e); + } + } + + public static T getStaticField(Class clazz, String fieldName) { + return getField(null, clazz, fieldName); + } + + public static void setStaticField(Class clazz, String fieldName, T value) { + setField(null, clazz, fieldName, value); + } + + public static Method getMethod(Class clazz, String name, Class... parameterTypes) { + try { + Method method = clazz.getDeclaredMethod(name, parameterTypes); + method.setAccessible(true); + return method; + } catch (NoSuchMethodException e) { + throw new RuntimeException("get method " + clazz.getName() + "@" + name + " failed", e); + } + } + +} diff --git a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/ServerlessConfigManager.java b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/ServerlessConfigManager.java new file mode 100644 index 000000000..4facdf950 --- /dev/null +++ b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/ServerlessConfigManager.java @@ -0,0 +1,222 @@ +/* + * 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.serverless.support.dubbo; + +import org.apache.dubbo.common.context.FrameworkExt; +import org.apache.dubbo.common.extension.Wrapper; +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.config.AbstractConfig; +import org.apache.dubbo.config.context.ConfigManager; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import static java.lang.Boolean.TRUE; +import static java.util.Collections.emptyMap; +import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY; +import static org.apache.dubbo.common.utils.ReflectUtils.getProperty; +import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty; +import static org.apache.dubbo.config.AbstractConfig.getTagName; + +/** + * @author: yuanyuan + * @date: 2023/12/22 8:02 下午 + */ +@Wrapper(matches = { "config" }) +public class ServerlessConfigManager extends ConfigManager { + + private static final Logger logger = LoggerFactory + .getLogger(ConfigManager.class); + + final Map>> globalConfigsCache = new HashMap<>(); + + private final ReadWriteLock lock = new ReentrantReadWriteLock(); + + private ConfigManager source; + + public ServerlessConfigManager(FrameworkExt frameworkExt) { + if (frameworkExt instanceof ConfigManager) { + this.source = (ConfigManager) frameworkExt; + } + } + + @Override + public void addConfig(AbstractConfig config, boolean unique) { + if (config == null) { + return; + } + + write(() -> { + Map configsMap = getCurrentConfigsCache().computeIfAbsent(getTagName(config.getClass()), type -> newMap()); + addIfAbsent(config, configsMap, unique); + }); + } + + private Map> getCurrentConfigsCache() { + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + globalConfigsCache.computeIfAbsent(contextClassLoader, k -> new HashMap<>()); + return globalConfigsCache.get(contextClassLoader); + } + + // @Override + // public Map getConfigsMap(String configType) { + // return read(() -> getCurrentConfigsCache().getOrDefault(configType, emptyMap())); + // } + + @Override + public Map getConfigsMap(String configType) { + return (Map) read(() -> getCurrentConfigsCache().getOrDefault(configType, emptyMap())); + } + + @Override + public Collection getConfigs(String configType) { + return (Collection) read(() -> getConfigsMap(configType).values()); + } + + @Override + public C getConfig(String configType, String id) { + return read(() -> { + Map configsMap = (Map) getCurrentConfigsCache().getOrDefault(configType, emptyMap()); + return configsMap.get(id); + }); + } + + @Override + public C getConfig(String configType) throws IllegalStateException { + return read(() -> { + Map configsMap = (Map) getCurrentConfigsCache().getOrDefault(configType, emptyMap()); + int size = configsMap.size(); + if (size < 1) { +// throw new IllegalStateException("No such " + configType.getName() + " is found"); + return null; + } else if (size > 1) { + + AtomicReference defaultConfig = new AtomicReference<>(); + configsMap.forEach((str, config) -> { + if (Boolean.TRUE.equals(config.isDefault())) { + defaultConfig.compareAndSet(null, config); + } + }); + + if (defaultConfig.get() != null) { + return defaultConfig.get(); + } + + logger.warn("Expected single matching of " + configType + ", but found " + size + " instances, will randomly pick the first one."); + } + + return configsMap.values().iterator().next(); + }); + } + + private void write(Runnable runnable) { + write(() -> { + runnable.run(); + return null; + }); + } + + private V write(Callable callable) { + V value = null; + Lock writeLock = lock.writeLock(); + try { + writeLock.lock(); + value = callable.call(); + } catch (RuntimeException e) { + throw e; + } catch (Throwable e) { + throw new RuntimeException(e.getCause()); + } finally { + writeLock.unlock(); + } + return value; + } + + private V read(Callable callable) { + Lock readLock = lock.readLock(); + V value = null; + try { + readLock.lock(); + value = callable.call(); + } catch (Throwable e) { + throw new RuntimeException(e); + } finally { + readLock.unlock(); + } + return value; + } + + private static Map newMap() { + return new HashMap<>(); + } + + static void addIfAbsent(C config, Map configsMap, boolean unique) + throws IllegalStateException { + + if (config == null || configsMap == null) { + return; + } + + if (unique) { // check duplicate + configsMap.values().forEach(c -> { + checkDuplicate(c, config); + }); + } + + String key = getId(config); + + C existedConfig = configsMap.get(key); + + if (existedConfig != null && !config.equals(existedConfig)) { + if (logger.isWarnEnabled()) { + String type = config.getClass().getSimpleName(); + logger.warn(String.format("Duplicate %s found, there already has one default %s or more than two %ss have the same id, " + + "you can try to give each %s a different id : %s", type, type, type, type, config)); + } + } else { + configsMap.put(key, config); + } + } + + private static void checkDuplicate(AbstractConfig oldOne, AbstractConfig newOne) + throws IllegalStateException { + if (oldOne != null && !oldOne.equals(newOne)) { + String configName = oldOne.getClass().getSimpleName(); + logger.warn("Duplicate Config found for " + configName + + ", you should use only one unique " + configName + + " for one application."); + } + } + + static String getId(C config) { + String id = config.getId(); + return isNotEmpty(id) ? id : isDefaultConfig(config) ? config.getClass().getSimpleName() + + "#" + DEFAULT_KEY : null; + } + + static boolean isDefaultConfig(C config) { + Boolean isDefault = getProperty(config, "isDefault"); + return isDefault == null || TRUE.equals(isDefault); + } +} diff --git a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/resources/META-INF/services/org.apache.dubbo.common.context.FrameworkExt b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/resources/META-INF/services/org.apache.dubbo.common.context.FrameworkExt new file mode 100644 index 000000000..6849eb0e2 --- /dev/null +++ b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/resources/META-INF/services/org.apache.dubbo.common.context.FrameworkExt @@ -0,0 +1 @@ +com.alipay.sofa.serverless.support.dubbo.ServerlessConfigManager \ No newline at end of file diff --git a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/resources/META-INF/spring.factories b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..0b5e76a75 --- /dev/null +++ b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.context.ApplicationListener=\ + com.alipay.sofa.serverless.support.dubbo.BizDubboBootstrapListener \ No newline at end of file From 472f36b17724a780641371953d4d362e0e4b387e Mon Sep 17 00:00:00 2001 From: yuanyuan Date: Tue, 26 Dec 2023 16:49:51 +0800 Subject: [PATCH 02/13] adapter for dubbo 27 --- .../dubbo26/base/Dubbo26BaseApplication.java | 2 +- .../dubbo27/base/Dubb27BaseApplication.java | 21 +- .../rpc/dubbo27/dubbo27biz/pom.xml | 6 + .../rpc/dubbo27/dubbo27biz2/pom.xml | 6 + .../dubbo/ServerlessServiceRepository.java | 91 ++++++++ .../dubbo/DecodeableRpcInvocation.java | 195 ++++++++++++++++++ ...g.apache.dubbo.common.context.FrameworkExt | 3 +- 7 files changed, 315 insertions(+), 9 deletions(-) create mode 100644 sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/ServerlessServiceRepository.java create mode 100644 sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java diff --git a/samples/dubbo-samples/rpc/dubbo26/dubbo26base/src/main/java/com/alipay/sofa/rpc/dubbo26/base/Dubbo26BaseApplication.java b/samples/dubbo-samples/rpc/dubbo26/dubbo26base/src/main/java/com/alipay/sofa/rpc/dubbo26/base/Dubbo26BaseApplication.java index 80134585c..abc4b37f4 100644 --- a/samples/dubbo-samples/rpc/dubbo26/dubbo26base/src/main/java/com/alipay/sofa/rpc/dubbo26/base/Dubbo26BaseApplication.java +++ b/samples/dubbo-samples/rpc/dubbo26/dubbo26base/src/main/java/com/alipay/sofa/rpc/dubbo26/base/Dubbo26BaseApplication.java @@ -52,7 +52,7 @@ public void run(String... args) throws Exception { } protected void installBiz(String bizDir) throws Throwable { - String pathRoot = "rpc/dubbo26/"; + String pathRoot = "samples/dubbo-samples/rpc/dubbo26/"; File bizFile = new File(pathRoot + bizDir); if (bizFile.exists()) { File tmpFile = new File(pathRoot + "target/" + bizFile.getName()); diff --git a/samples/dubbo-samples/rpc/dubbo27/dubbo27base/src/main/java/com/alipay/sofa/rpc/dubbo27/base/Dubb27BaseApplication.java b/samples/dubbo-samples/rpc/dubbo27/dubbo27base/src/main/java/com/alipay/sofa/rpc/dubbo27/base/Dubb27BaseApplication.java index 31441c2b9..a97e6b8a5 100644 --- a/samples/dubbo-samples/rpc/dubbo27/dubbo27base/src/main/java/com/alipay/sofa/rpc/dubbo27/base/Dubb27BaseApplication.java +++ b/samples/dubbo-samples/rpc/dubbo27/dubbo27base/src/main/java/com/alipay/sofa/rpc/dubbo27/base/Dubb27BaseApplication.java @@ -28,7 +28,7 @@ @SpringBootApplication @ImportResource("classpath:provider.xml") -public class Dubb27BaseApplication implements CommandLineRunner { +public class Dubb27BaseApplication { private static Logger LOGGER = LoggerFactory.getLogger(Dubb27BaseApplication.class); // @Autowired @@ -45,9 +45,16 @@ public static void main(String[] args) { System.setProperty("sofa.ark.plugin.export.class.enable", "true"); ConfigurableApplicationContext context = SpringApplication.run(Dubb27BaseApplication.class, args); -// MasterController controller = context.getBean(MasterController.class); -// DemoResponse xxx = controller.handle("xxx"); -// System.out.println(xxx); + MasterController controller = context.getBean(MasterController.class); + DemoResponse xxx = controller.handle("xxx"); + System.out.println(xxx.getResult()); + + System.out.println("start to deploy biz"); + try { + run(args); + } catch (Exception e) { + e.printStackTrace(); + } } /** @@ -55,8 +62,8 @@ public static void main(String[] args) { * @param args * @throws Exception */ - @Override - public void run(String... args) throws Exception { +// @Override + public static void run(String... args) throws Exception { try { installBiz("dubbo27biz/target/dubbo27biz-0.0.1-SNAPSHOT-ark-biz.jar"); installBiz("dubbo27biz2/target/dubbo27biz2-0.0.1-SNAPSHOT-ark-biz.jar"); @@ -65,7 +72,7 @@ public void run(String... args) throws Exception { } } - protected void installBiz(String bizDir) throws Throwable { + protected static void installBiz(String bizDir) throws Throwable { String pathRoot = "samples/dubbo-samples/rpc/dubbo27/"; File bizFile = new File(pathRoot + bizDir); if (bizFile.exists()) { diff --git a/samples/dubbo-samples/rpc/dubbo27/dubbo27biz/pom.xml b/samples/dubbo-samples/rpc/dubbo27/dubbo27biz/pom.xml index 115aae8be..9c847d80b 100644 --- a/samples/dubbo-samples/rpc/dubbo27/dubbo27biz/pom.xml +++ b/samples/dubbo-samples/rpc/dubbo27/dubbo27biz/pom.xml @@ -40,6 +40,12 @@ ${sofa.serverless.runtime.version} provided + + com.alipay.sofa.serverless + sofa-serverless-adapter-dubbo2.7 + ${sofa.serverless.runtime.version} + provided + org.projectlombok lombok diff --git a/samples/dubbo-samples/rpc/dubbo27/dubbo27biz2/pom.xml b/samples/dubbo-samples/rpc/dubbo27/dubbo27biz2/pom.xml index add4e7a6c..134823c5d 100644 --- a/samples/dubbo-samples/rpc/dubbo27/dubbo27biz2/pom.xml +++ b/samples/dubbo-samples/rpc/dubbo27/dubbo27biz2/pom.xml @@ -40,6 +40,12 @@ ${sofa.serverless.runtime.version} provided + + com.alipay.sofa.serverless + sofa-serverless-adapter-dubbo2.7 + ${sofa.serverless.runtime.version} + provided + org.projectlombok lombok diff --git a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/ServerlessServiceRepository.java b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/ServerlessServiceRepository.java new file mode 100644 index 000000000..6b3bfd676 --- /dev/null +++ b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/ServerlessServiceRepository.java @@ -0,0 +1,91 @@ +/* + * 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.serverless.support.dubbo; + +import org.apache.dubbo.common.context.FrameworkExt; +import org.apache.dubbo.common.extension.Wrapper; +import org.apache.dubbo.rpc.model.ServiceDescriptor; +import org.apache.dubbo.rpc.model.ServiceRepository; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * @author: yuanyuan + * @date: 2023/12/25 9:07 下午 + */ +@Wrapper(matches = { "repository" }) +public class ServerlessServiceRepository extends ServiceRepository { + + // services + private static ConcurrentMap, ServiceDescriptor> globalClassServices = new ConcurrentHashMap<>(); + + private static ConcurrentMap globalPathServices = new ConcurrentHashMap<>(); + + private ServiceRepository source; + + public ServerlessServiceRepository(FrameworkExt frameworkExt) { + if (frameworkExt instanceof ServiceRepository) { + this.source = (ServiceRepository) frameworkExt; + } + } + + @Override + public ServiceDescriptor registerService(Class interfaceClazz) { + ServiceDescriptor serviceDescriptor = globalClassServices.computeIfAbsent(interfaceClazz, + _k -> new ServiceDescriptor(interfaceClazz)); + globalPathServices.putIfAbsent(interfaceClazz.getName(), serviceDescriptor); + return serviceDescriptor; + } + + @Override + public ServiceDescriptor registerService(String path, Class interfaceClass) { + ServiceDescriptor serviceDescriptor = registerService(interfaceClass); + // if path is different with interface name, add extra path mapping + if (!interfaceClass.getName().equals(path)) { + globalPathServices.putIfAbsent(path, serviceDescriptor); + } + return serviceDescriptor; + } + + @Override + public void unregisterService(String path) { + globalPathServices.remove(path); + } + + @Override + public List getAllServices() { + return Collections.unmodifiableList(new ArrayList<>(globalClassServices.values())); + } + + @Override + public ServiceDescriptor lookupService(String pathOrInterfaceName) { + return globalPathServices.get(pathOrInterfaceName); + } + + @Override + public void destroy() throws IllegalStateException { + // currently works for unit test + globalClassServices.clear(); + globalPathServices.clear(); + super.destroy(); + } + +} diff --git a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java new file mode 100644 index 000000000..fd79646d3 --- /dev/null +++ b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java @@ -0,0 +1,195 @@ +/* + * 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 org.apache.dubbo.rpc.protocol.dubbo; + + +import org.apache.dubbo.common.config.ConfigurationUtils; +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.serialize.Cleanable; +import org.apache.dubbo.common.serialize.ObjectInput; +import org.apache.dubbo.common.utils.Assert; +import org.apache.dubbo.common.utils.ReflectUtils; +import org.apache.dubbo.common.utils.StringUtils; +import org.apache.dubbo.remoting.Channel; +import org.apache.dubbo.remoting.Codec; +import org.apache.dubbo.remoting.Decodeable; +import org.apache.dubbo.remoting.exchange.Request; +import org.apache.dubbo.remoting.transport.CodecSupport; +import org.apache.dubbo.rpc.RpcInvocation; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.MethodDescriptor; +import org.apache.dubbo.rpc.model.ServiceDescriptor; +import org.apache.dubbo.rpc.model.ServiceRepository; +import org.apache.dubbo.rpc.support.RpcUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Map; + +import static org.apache.dubbo.common.URL.buildKey; +import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; +import static org.apache.dubbo.rpc.Constants.SERIALIZATION_ID_KEY; +import static org.apache.dubbo.rpc.Constants.SERIALIZATION_SECURITY_CHECK_KEY; +import static org.apache.dubbo.rpc.protocol.dubbo.CallbackServiceCodec.decodeInvocationArgument; + +public class DecodeableRpcInvocation extends RpcInvocation implements Codec, Decodeable { + + private static final Logger log = LoggerFactory.getLogger(DecodeableRpcInvocation.class); + + private Channel channel; + + private byte serializationType; + + private InputStream inputStream; + + private Request request; + + private volatile boolean hasDecoded; + + public DecodeableRpcInvocation(Channel channel, Request request, InputStream is, byte id) { + Assert.notNull(channel, "channel == null"); + Assert.notNull(request, "request == null"); + Assert.notNull(is, "inputStream == null"); + this.channel = channel; + this.request = request; + this.inputStream = is; + this.serializationType = id; + } + + @Override + public void decode() throws Exception { + if (!hasDecoded && channel != null && inputStream != null) { + try { + decode(channel, inputStream); + } catch (Throwable e) { + if (log.isWarnEnabled()) { + log.warn("Decode rpc invocation failed: " + e.getMessage(), e); + } + request.setBroken(true); + request.setData(e); + } finally { + hasDecoded = true; + } + } + } + + @Override + public void encode(Channel channel, OutputStream output, Object message) throws IOException { + throw new UnsupportedOperationException(); + } + + private void checkSerializationTypeFromRemote() { + + } + + @Override + public Object decode(Channel channel, InputStream input) throws IOException { + ObjectInput in = CodecSupport.getSerialization(channel.getUrl(), serializationType) + .deserialize(channel.getUrl(), input); + this.put(SERIALIZATION_ID_KEY, serializationType); + + String dubboVersion = in.readUTF(); + request.setVersion(dubboVersion); + setAttachment(DUBBO_VERSION_KEY, dubboVersion); + + String path = in.readUTF(); + setAttachment(PATH_KEY, path); + String version = in.readUTF(); + setAttachment(VERSION_KEY, version); + + setMethodName(in.readUTF()); + + String desc = in.readUTF(); + setParameterTypesDesc(desc); + + try { + if (ConfigurationUtils.getSystemConfiguration().getBoolean(SERIALIZATION_SECURITY_CHECK_KEY, false)) { + CodecSupport.checkSerialization(path, version, serializationType); + } + Object[] args = DubboCodec.EMPTY_OBJECT_ARRAY; + Class[] pts = DubboCodec.EMPTY_CLASS_ARRAY; + if (desc.length() > 0) { +// if (RpcUtils.isGenericCall(path, getMethodName()) || RpcUtils.isEcho(path, getMethodName())) { +// pts = ReflectUtils.desc2classArray(desc); +// } else { + ServiceRepository repository = ApplicationModel.getServiceRepository(); + ServiceDescriptor serviceDescriptor = repository.lookupService(path); + if (serviceDescriptor != null) { + MethodDescriptor methodDescriptor = serviceDescriptor.getMethod(getMethodName(), desc); + if (methodDescriptor != null) { + pts = methodDescriptor.getParameterClasses(); + this.setReturnTypes(methodDescriptor.getReturnTypes()); + } + } + if (pts == DubboCodec.EMPTY_CLASS_ARRAY) { + if (!RpcUtils.isGenericCall(desc, getMethodName()) && !RpcUtils.isEcho(desc, getMethodName())) { + throw new IllegalArgumentException("Service not found:" + path + ", " + getMethodName()); + } + pts = ReflectUtils.desc2classArray(desc); + } +// } + + args = new Object[pts.length]; + for (int i = 0; i < args.length; i++) { + try { + args[i] = in.readObject(pts[i]); + } catch (Exception e) { + if (log.isWarnEnabled()) { + log.warn("Decode argument failed: " + e.getMessage(), e); + } + } + } + } + setParameterTypes(pts); + + Map map = in.readAttachments(); + if (map != null && map.size() > 0) { + Map attachment = getObjectAttachments(); + if (attachment == null) { + attachment = new HashMap<>(); + } + attachment.putAll(map); + setObjectAttachments(attachment); + } + + //decode argument ,may be callback + for (int i = 0; i < args.length; i++) { + args[i] = decodeInvocationArgument(channel, this, pts, i, args[i]); + } + + setArguments(args); + String targetServiceName = buildKey(getAttachment(PATH_KEY), + getAttachment(GROUP_KEY), + getAttachment(VERSION_KEY)); + setTargetServiceUniqueName(targetServiceName); + } catch (ClassNotFoundException e) { + throw new IOException(StringUtils.toString("Read invocation data failed.", e)); + } finally { + if (in instanceof Cleanable) { + ((Cleanable) in).cleanup(); + } + } + return this; + } + +} diff --git a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/resources/META-INF/services/org.apache.dubbo.common.context.FrameworkExt b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/resources/META-INF/services/org.apache.dubbo.common.context.FrameworkExt index 6849eb0e2..686e6684e 100644 --- a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/resources/META-INF/services/org.apache.dubbo.common.context.FrameworkExt +++ b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/resources/META-INF/services/org.apache.dubbo.common.context.FrameworkExt @@ -1 +1,2 @@ -com.alipay.sofa.serverless.support.dubbo.ServerlessConfigManager \ No newline at end of file +com.alipay.sofa.serverless.support.dubbo.ServerlessConfigManager +com.alipay.sofa.serverless.support.dubbo.ServerlessServiceRepository \ No newline at end of file From a93c416869c5bf7179bfee7811cd1bfbfbb9c78d Mon Sep 17 00:00:00 2001 From: yuanyuan Date: Wed, 3 Jan 2024 15:25:46 +0800 Subject: [PATCH 03/13] support dubbo 2.7 --- .../sofa-serverless-adapter-dubbo2.7/pom.xml | 3 +- .../dubbo/BizDubboBootstrapListener.java | 3 - .../dubbo/ConsumerRedefinePathFilter.java | 44 ++++ .../java/ClassLoaderJavaObjectInput.java | 98 +++++++++ .../java/ClassLoaderObjectInputStream.java | 102 +++++++++ .../serialize/java/JavaSerialization.java | 59 +++++ .../dubbo/DecodeableRpcInvocation.java | 51 +++-- .../protocol/dubbo/DecodeableRpcResult.java | 204 ++++++++++++++++++ .../services/org.apache.dubbo.rpc.Filter | 1 + 9 files changed, 542 insertions(+), 23 deletions(-) create mode 100644 sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/ConsumerRedefinePathFilter.java create mode 100644 sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/common/serialize/java/ClassLoaderJavaObjectInput.java create mode 100644 sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/common/serialize/java/ClassLoaderObjectInputStream.java create mode 100644 sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/common/serialize/java/JavaSerialization.java create mode 100644 sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java create mode 100644 sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/resources/META-INF/services/org.apache.dubbo.rpc.Filter diff --git a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/pom.xml b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/pom.xml index e15328fa1..aa239d149 100644 --- a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/pom.xml +++ b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/pom.xml @@ -3,15 +3,14 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - sofa-serverless-adapter-ext com.alipay.sofa.serverless + sofa-serverless-adapter-ext ${revision} ../pom.xml 4.0.0 sofa-serverless-adapter-dubbo2.7 - ${revision} 8 diff --git a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/BizDubboBootstrapListener.java b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/BizDubboBootstrapListener.java index bf5468c0b..34cf63d9b 100644 --- a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/BizDubboBootstrapListener.java +++ b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/BizDubboBootstrapListener.java @@ -21,8 +21,6 @@ import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; -import java.lang.reflect.Method; - /** * @author: yuanyuan * @date: 2023/12/25 4:14 下午 @@ -51,7 +49,6 @@ public void onApplicationEvent(ApplicationEvent event) { } private void onContextRefreshedEvent(ContextRefreshedEvent event) { - Method exportServices = ReflectionUtils.getMethod(DubboBootstrap.class, "exportServices"); try { ReflectionUtils.getMethod(DubboBootstrap.class, "exportServices") .invoke(dubboBootstrap); diff --git a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/ConsumerRedefinePathFilter.java b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/ConsumerRedefinePathFilter.java new file mode 100644 index 000000000..41054b425 --- /dev/null +++ b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/ConsumerRedefinePathFilter.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.serverless.support.dubbo; + +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.rpc.Filter; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.Result; +import org.apache.dubbo.rpc.RpcException; +import org.apache.dubbo.rpc.RpcInvocation; + +import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; + +/** + * @author: yuanyuan + * @date: 2023/12/28 5:38 下午 + */ +@Activate(group = CONSUMER) +public class ConsumerRedefinePathFilter implements Filter { + + @Override + public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { + if (invocation instanceof RpcInvocation) { + RpcInvocation rpcInvocation = (RpcInvocation) invocation; + rpcInvocation.setAttachment("interface", rpcInvocation.getTargetServiceUniqueName()); + } + return invoker.invoke(invocation); + } +} diff --git a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/common/serialize/java/ClassLoaderJavaObjectInput.java b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/common/serialize/java/ClassLoaderJavaObjectInput.java new file mode 100644 index 000000000..4195e7bd4 --- /dev/null +++ b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/common/serialize/java/ClassLoaderJavaObjectInput.java @@ -0,0 +1,98 @@ +/* + * 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 org.apache.dubbo.common.serialize.java; + +import org.apache.dubbo.common.serialize.nativejava.NativeJavaObjectInput; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.lang.reflect.Type; + +/** + * @author: yuanyuan + * @date: 2023/12/28 8:26 下午 + */ +public class ClassLoaderJavaObjectInput extends NativeJavaObjectInput { + + public static final int MAX_BYTE_ARRAY_LENGTH = 8 * 1024 * 1024; + + public ClassLoaderJavaObjectInput(InputStream is) throws IOException { + super( + (ObjectInputStream) (is instanceof ObjectInputStream ? is : new ObjectInputStream(is))); + } + + public ClassLoaderJavaObjectInput(InputStream is, boolean compacted) throws IOException { + super(compacted ? new CompactedObjectInputStream(is) + : ((ObjectInputStream) (is instanceof ObjectInputStream ? is + : new ObjectInputStream(is)))); + } + + @Override + public byte[] readBytes() throws IOException { + int len = getObjectInputStream().readInt(); + if (len < 0) { + return null; + } + if (len == 0) { + return new byte[0]; + } + if (len > MAX_BYTE_ARRAY_LENGTH) { + throw new IOException("Byte array length too large. " + len); + } + + byte[] b = new byte[len]; + getObjectInputStream().readFully(b); + return b; + } + + @Override + public String readUTF() throws IOException { + int len = getObjectInputStream().readInt(); + if (len < 0) { + return null; + } + + return getObjectInputStream().readUTF(); + } + + @Override + public Object readObject() throws IOException, ClassNotFoundException { + byte b = getObjectInputStream().readByte(); + if (b == 0) { + return null; + } + + return getObjectInputStream().readObject(); + } + + @Override + @SuppressWarnings("unchecked") + public T readObject(Class cls) throws IOException, ClassNotFoundException { + return (T) readObject(); + } + + @Override + @SuppressWarnings("unchecked") + public T readObject(Class cls, Type type) throws IOException, ClassNotFoundException { + return (T) readObject(); + } + + public InputStream getInputStream() { + return getObjectInputStream(); + } +} diff --git a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/common/serialize/java/ClassLoaderObjectInputStream.java b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/common/serialize/java/ClassLoaderObjectInputStream.java new file mode 100644 index 000000000..c697991eb --- /dev/null +++ b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/common/serialize/java/ClassLoaderObjectInputStream.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 org.apache.dubbo.common.serialize.java; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectStreamClass; +import java.io.StreamCorruptedException; +import java.lang.reflect.Proxy; + +/** + * @author: yuanyuan + * @date: 2023/12/28 8:28 下午 + */ +public class ClassLoaderObjectInputStream extends ObjectInputStream { + + private ClassLoader classLoader; + + /** + * Constructs a new ClassLoaderObjectInputStream. + * + * @param classLoader the ClassLoader from which classes should be loaded + * @param inputStream the InputStream to work on + * @throws IOException in case of an I/O error + * @throws StreamCorruptedException if the stream is corrupted + */ + public ClassLoaderObjectInputStream(final ClassLoader classLoader, final InputStream inputStream) + throws IOException, + StreamCorruptedException { + super(inputStream); + this.classLoader = classLoader; + } + + /** + * Resolve a class specified by the descriptor using the + * specified ClassLoader or the super ClassLoader. + * + * @param objectStreamClass descriptor of the class + * @return the Class object described by the ObjectStreamClass + * @throws IOException in case of an I/O error + * @throws ClassNotFoundException if the Class cannot be found + */ + @Override + protected Class resolveClass(final ObjectStreamClass objectStreamClass) throws IOException, + ClassNotFoundException { + + try { + return Class.forName(objectStreamClass.getName(), false, classLoader); + } catch (final ClassNotFoundException cnfe) { + // delegate to super class loader which can resolve primitives + return super.resolveClass(objectStreamClass); + } + } + + /** + * Create a proxy class that implements the specified interfaces using + * the specified ClassLoader or the super ClassLoader. + * + * @param interfaces the interfaces to implement + * @return a proxy class implementing the interfaces + * @throws IOException in case of an I/O error + * @throws ClassNotFoundException if the Class cannot be found + * @see ObjectInputStream#resolveProxyClass(String[]) + * @since 2.1 + */ + @Override + protected Class resolveProxyClass(final String[] interfaces) throws IOException, + ClassNotFoundException { + final Class[] interfaceClasses = new Class[interfaces.length]; + for (int i = 0; i < interfaces.length; i++) { + interfaceClasses[i] = Class.forName(interfaces[i], false, classLoader); + } + try { + return Proxy.getProxyClass(classLoader, interfaceClasses); + } catch (final IllegalArgumentException e) { + return super.resolveProxyClass(interfaces); + } + } + + public ClassLoader getClassLoader() { + return classLoader; + } + + public void setClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } +} diff --git a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/common/serialize/java/JavaSerialization.java b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/common/serialize/java/JavaSerialization.java new file mode 100644 index 000000000..c7d6f8415 --- /dev/null +++ b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/common/serialize/java/JavaSerialization.java @@ -0,0 +1,59 @@ +/* + * 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 org.apache.dubbo.common.serialize.java; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.serialize.ObjectInput; +import org.apache.dubbo.common.serialize.ObjectOutput; +import org.apache.dubbo.common.serialize.Serialization; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import static org.apache.dubbo.common.serialize.Constants.JAVA_SERIALIZATION_ID; + +/** + * Java serialization implementation + * + *
+ *     e.g. <dubbo:protocol serialization="java" />
+ * 
+ */ +public class JavaSerialization implements Serialization { + + @Override + public byte getContentTypeId() { + return JAVA_SERIALIZATION_ID; + } + + @Override + public String getContentType() { + return "x-application/java"; + } + + @Override + public ObjectOutput serialize(URL url, OutputStream out) throws IOException { + return new JavaObjectOutput(out); + } + + @Override + public ObjectInput deserialize(URL url, InputStream is) throws IOException { + return new ClassLoaderJavaObjectInput(new ClassLoaderObjectInputStream(null, is)); + } + +} diff --git a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java index fd79646d3..e2e5df0ae 100644 --- a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java +++ b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java @@ -16,12 +16,13 @@ */ package org.apache.dubbo.rpc.protocol.dubbo; - import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.serialize.Cleanable; import org.apache.dubbo.common.serialize.ObjectInput; +import org.apache.dubbo.common.serialize.java.ClassLoaderJavaObjectInput; +import org.apache.dubbo.common.serialize.java.ClassLoaderObjectInputStream; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.common.utils.StringUtils; @@ -56,15 +57,15 @@ public class DecodeableRpcInvocation extends RpcInvocation implements Codec, Dec private static final Logger log = LoggerFactory.getLogger(DecodeableRpcInvocation.class); - private Channel channel; + private Channel channel; - private byte serializationType; + private byte serializationType; - private InputStream inputStream; + private InputStream inputStream; - private Request request; + private Request request; - private volatile boolean hasDecoded; + private volatile boolean hasDecoded; public DecodeableRpcInvocation(Channel channel, Request request, InputStream is, byte id) { Assert.notNull(channel, "channel == null"); @@ -105,7 +106,7 @@ private void checkSerializationTypeFromRemote() { @Override public Object decode(Channel channel, InputStream input) throws IOException { ObjectInput in = CodecSupport.getSerialization(channel.getUrl(), serializationType) - .deserialize(channel.getUrl(), input); + .deserialize(channel.getUrl(), input); this.put(SERIALIZATION_ID_KEY, serializationType); String dubboVersion = in.readUTF(); @@ -123,31 +124,46 @@ public Object decode(Channel channel, InputStream input) throws IOException { setParameterTypesDesc(desc); try { - if (ConfigurationUtils.getSystemConfiguration().getBoolean(SERIALIZATION_SECURITY_CHECK_KEY, false)) { + if (ConfigurationUtils.getSystemConfiguration().getBoolean( + SERIALIZATION_SECURITY_CHECK_KEY, false)) { CodecSupport.checkSerialization(path, version, serializationType); } Object[] args = DubboCodec.EMPTY_OBJECT_ARRAY; Class[] pts = DubboCodec.EMPTY_CLASS_ARRAY; if (desc.length() > 0) { -// if (RpcUtils.isGenericCall(path, getMethodName()) || RpcUtils.isEcho(path, getMethodName())) { -// pts = ReflectUtils.desc2classArray(desc); -// } else { + // if (RpcUtils.isGenericCall(path, getMethodName()) || RpcUtils.isEcho(path, getMethodName())) { + // pts = ReflectUtils.desc2classArray(desc); + // } else { ServiceRepository repository = ApplicationModel.getServiceRepository(); ServiceDescriptor serviceDescriptor = repository.lookupService(path); if (serviceDescriptor != null) { - MethodDescriptor methodDescriptor = serviceDescriptor.getMethod(getMethodName(), desc); + MethodDescriptor methodDescriptor = serviceDescriptor.getMethod( + getMethodName(), desc); if (methodDescriptor != null) { pts = methodDescriptor.getParameterClasses(); this.setReturnTypes(methodDescriptor.getReturnTypes()); } } if (pts == DubboCodec.EMPTY_CLASS_ARRAY) { - if (!RpcUtils.isGenericCall(desc, getMethodName()) && !RpcUtils.isEcho(desc, getMethodName())) { - throw new IllegalArgumentException("Service not found:" + path + ", " + getMethodName()); + if (!RpcUtils.isGenericCall(desc, getMethodName()) + && !RpcUtils.isEcho(desc, getMethodName())) { + throw new IllegalArgumentException("Service not found:" + path + ", " + + getMethodName()); } pts = ReflectUtils.desc2classArray(desc); } -// } + // } + + // patch begin + if (in instanceof ClassLoaderJavaObjectInput) { + InputStream is = ((ClassLoaderJavaObjectInput) in).getInputStream(); + if (is instanceof ClassLoaderObjectInputStream) { + ClassLoader cl = serviceDescriptor.getServiceInterfaceClass() + .getClassLoader(); + ((ClassLoaderObjectInputStream) is).setClassLoader(cl); + } + } + // patch end args = new Object[pts.length]; for (int i = 0; i < args.length; i++) { @@ -178,9 +194,8 @@ public Object decode(Channel channel, InputStream input) throws IOException { } setArguments(args); - String targetServiceName = buildKey(getAttachment(PATH_KEY), - getAttachment(GROUP_KEY), - getAttachment(VERSION_KEY)); + String targetServiceName = buildKey(getAttachment(PATH_KEY), getAttachment(GROUP_KEY), + getAttachment(VERSION_KEY)); setTargetServiceUniqueName(targetServiceName); } catch (ClassNotFoundException e) { throw new IOException(StringUtils.toString("Read invocation data failed.", e)); diff --git a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java new file mode 100644 index 000000000..aba61d1e9 --- /dev/null +++ b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java @@ -0,0 +1,204 @@ +/* + * 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 org.apache.dubbo.rpc.protocol.dubbo; + +import org.apache.dubbo.common.config.ConfigurationUtils; +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.serialize.Cleanable; +import org.apache.dubbo.common.serialize.ObjectInput; +import org.apache.dubbo.common.serialize.java.ClassLoaderJavaObjectInput; +import org.apache.dubbo.common.serialize.java.ClassLoaderObjectInputStream; +import org.apache.dubbo.common.utils.ArrayUtils; +import org.apache.dubbo.common.utils.Assert; +import org.apache.dubbo.common.utils.StringUtils; +import org.apache.dubbo.remoting.Channel; +import org.apache.dubbo.remoting.Codec; +import org.apache.dubbo.remoting.Decodeable; +import org.apache.dubbo.remoting.exchange.Response; +import org.apache.dubbo.remoting.transport.CodecSupport; +import org.apache.dubbo.rpc.AppResponse; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.RpcInvocation; +import org.apache.dubbo.rpc.support.RpcUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Type; + +import static org.apache.dubbo.rpc.Constants.SERIALIZATION_ID_KEY; +import static org.apache.dubbo.rpc.Constants.SERIALIZATION_SECURITY_CHECK_KEY; + +public class DecodeableRpcResult extends AppResponse implements Codec, Decodeable { + + private static final Logger log = LoggerFactory.getLogger(DecodeableRpcResult.class); + + private Channel channel; + + private byte serializationType; + + private InputStream inputStream; + + private Response response; + + private Invocation invocation; + + private volatile boolean hasDecoded; + + public DecodeableRpcResult(Channel channel, Response response, InputStream is, + Invocation invocation, byte id) { + Assert.notNull(channel, "channel == null"); + Assert.notNull(response, "response == null"); + Assert.notNull(is, "inputStream == null"); + this.channel = channel; + this.response = response; + this.inputStream = is; + this.invocation = invocation; + this.serializationType = id; + } + + @Override + public void encode(Channel channel, OutputStream output, Object message) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public Object decode(Channel channel, InputStream input) throws IOException { + if (log.isDebugEnabled()) { + Thread thread = Thread.currentThread(); + log.debug("Decoding in thread -- [" + thread.getName() + "#" + thread.getId() + "]"); + } + + ObjectInput in = CodecSupport.getSerialization(channel.getUrl(), serializationType) + .deserialize(channel.getUrl(), input); + + // patch begin + if (in instanceof ClassLoaderJavaObjectInput) { + InputStream is = ((ClassLoaderJavaObjectInput) in).getInputStream(); + if (is instanceof ClassLoaderObjectInputStream) { + ClassLoader cl = invocation.getInvoker().getInterface().getClassLoader(); + ((ClassLoaderObjectInputStream) is).setClassLoader(cl); + } + } + // patch end + + byte flag = in.readByte(); + switch (flag) { + case DubboCodec.RESPONSE_NULL_VALUE: + break; + case DubboCodec.RESPONSE_VALUE: + handleValue(in); + break; + case DubboCodec.RESPONSE_WITH_EXCEPTION: + handleException(in); + break; + case DubboCodec.RESPONSE_NULL_VALUE_WITH_ATTACHMENTS: + handleAttachment(in); + break; + case DubboCodec.RESPONSE_VALUE_WITH_ATTACHMENTS: + handleValue(in); + handleAttachment(in); + break; + case DubboCodec.RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS: + handleException(in); + handleAttachment(in); + break; + default: + throw new IOException( + "Unknown result flag, expect '0' '1' '2' '3' '4' '5', but received: " + flag); + } + if (in instanceof Cleanable) { + ((Cleanable) in).cleanup(); + } + return this; + } + + @Override + public void decode() throws Exception { + if (!hasDecoded && channel != null && inputStream != null) { + try { + if (invocation != null) { + if (ConfigurationUtils.getSystemConfiguration().getBoolean( + SERIALIZATION_SECURITY_CHECK_KEY, false)) { + Object serializationTypeObj = invocation.get(SERIALIZATION_ID_KEY); + if (serializationTypeObj != null) { + if ((byte) serializationTypeObj != serializationType) { + throw new IOException( + "Unexpected serialization id:" + + serializationType + + " received from network, please check if the peer send the right id."); + } + } + } + } + decode(channel, inputStream); + } catch (Throwable e) { + if (log.isWarnEnabled()) { + log.warn("Decode rpc result failed: " + e.getMessage(), e); + } + response.setStatus(Response.CLIENT_ERROR); + response.setErrorMessage(StringUtils.toString(e)); + } finally { + hasDecoded = true; + } + } + } + + private void handleValue(ObjectInput in) throws IOException { + try { + Type[] returnTypes; + if (invocation instanceof RpcInvocation) { + returnTypes = ((RpcInvocation) invocation).getReturnTypes(); + } else { + returnTypes = RpcUtils.getReturnTypes(invocation); + } + Object value = null; + if (ArrayUtils.isEmpty(returnTypes)) { + // This almost never happens? + value = in.readObject(); + } else if (returnTypes.length == 1) { + value = in.readObject((Class) returnTypes[0]); + } else { + value = in.readObject((Class) returnTypes[0], returnTypes[1]); + } + setValue(value); + } catch (ClassNotFoundException e) { + rethrow(e); + } + } + + private void handleException(ObjectInput in) throws IOException { + try { + setException(in.readThrowable()); + } catch (ClassNotFoundException e) { + rethrow(e); + } + } + + private void handleAttachment(ObjectInput in) throws IOException { + try { + addObjectAttachments(in.readAttachments()); + } catch (ClassNotFoundException e) { + rethrow(e); + } + } + + private void rethrow(Exception e) throws IOException { + throw new IOException(StringUtils.toString("Read response data failed.", e)); + } +} diff --git a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/resources/META-INF/services/org.apache.dubbo.rpc.Filter b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/resources/META-INF/services/org.apache.dubbo.rpc.Filter new file mode 100644 index 000000000..186964048 --- /dev/null +++ b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/resources/META-INF/services/org.apache.dubbo.rpc.Filter @@ -0,0 +1 @@ +com.alipay.sofa.serverless.support.dubbo.ConsumerRedefinePathFilter \ No newline at end of file From 775f6337ec904d23d650cb5736d14ba7a41acf35 Mon Sep 17 00:00:00 2001 From: yuanyuan Date: Wed, 3 Jan 2024 17:25:33 +0800 Subject: [PATCH 04/13] README --- samples/dubbo-samples/rpc/dubbo27/README.md | 214 ++++++++++++++++++ .../tunnel/http/netty/NettyHttpServer.java | 6 +- 2 files changed, 218 insertions(+), 2 deletions(-) create mode 100644 samples/dubbo-samples/rpc/dubbo27/README.md diff --git a/samples/dubbo-samples/rpc/dubbo27/README.md b/samples/dubbo-samples/rpc/dubbo27/README.md new file mode 100644 index 000000000..a42711006 --- /dev/null +++ b/samples/dubbo-samples/rpc/dubbo27/README.md @@ -0,0 +1,214 @@ +# Dubbo 2.7.x 在模块中使用 +## 基座新增依赖 +base 为普通 dubbo 应用改造而成,改造内容只需在主 pom 里增加如下依赖 +```xml + + + com.alipay.sofa.serverless + sofa-serverless-adapter-dubbo2.7 + 0.5.6-SNAPSHOT + + + + com.alipay.sofa.serverless + sofa-serverless-base-starter + + + + com.alipay.sofa + web-ark-plugin + + + + com.alipay.sofa + dubbo27model + 0.0.1-SNAPSHOT + +``` + +这里基座发布了 RPC 服务 +```xml + + +``` + +## 模块中使用 +用例提供了 biz 和 biz2 两个模块动态安装到基座JVM中,也是普通 dubbo 应用,进行如下改造即可变成可合并部署的 ark biz 模块 + +### 1.修改模块打包插件 +```xml + + com.alipay.sofa + sofa-ark-maven-plugin + 2.2.3 + + + default-cli + + repackage + + + + + true + ./target + + biz + + /biz + true + + +``` +### 2. 添加依赖 +另外模块还额外将基座里有的依赖,设置为了 provided,这样可以尽可能的复用基座的spring、dubbo依赖等。通信类 dubbo27model 不用 provided,让biz类加载器自己加载 +```xml + + + com.alipay.sofa.serverless + sofa-serverless-adapter-log4j2 + ${sofa.serverless.runtime.version} + provided + + + + com.alipay.sofa.serverless + sofa-serverless-adapter-dubbo2.7 + ${sofa.serverless.runtime.version} + provided + + + + com.alipay.sofa + dubbo27model + 0.0.1-SNAPSHOT + +``` +### 3. 声明dubbo服务和引用 +biz1声明了三个rpc服务: +> biz1/com.alipay.sofa.rpc.dubbo27.model.DemoService +> biz1Second/com.alipay.sofa.rpc.dubbo27.model.DemoService +> biz1/com.alipay.sofa.rpc.dubbo27.model.HelloService + +三个rpc引用,remote调用biz2发布的rpc服务 +> biz2/com.alipay.sofa.rpc.dubbo27.model.DemoService +> biz2Second/com.alipay.sofa.rpc.dubbo27.model.DemoService +> biz2/com.alipay.sofa.rpc.dubbo27.model.HelloService + +服务发布与引用配置如下: +```xml + + + + + + + + + + + + +``` + +类似的,biz2声明了三个rpc服务: +> biz2/com.alipay.sofa.rpc.dubbo27.model.DemoService +> biz2Second/com.alipay.sofa.rpc.dubbo27.model.DemoService +> biz2/com.alipay.sofa.rpc.dubbo27.model.HelloService + +三个rpc引用,remote调用biz1发布的rpc服务 +> biz1/com.alipay.sofa.rpc.dubbo27.model.DemoService +> biz1Second/com.alipay.sofa.rpc.dubbo27.model.DemoService +> biz1/com.alipay.sofa.rpc.dubbo27.model.HelloService + + + + + + +### 测试调用代码 +发布一个服务 +``` xml + + + + +``` + +在 BizController 里引用了模块自己发布的RPC,基座发布的injvm服务。 +``` xml + + + + +``` + + +### 运行代码 +1. 【重要】首次运行,先编译安装下用于通信包 +```shell +cd samples/dubbo-samples/rpc/dubbo27/dubbo27model +mvn clean install +``` + +2. 进入目录`samples/dubbo-samples/rpc/dubbo27` 编译打包模块的代码 +```shell +cd ../ +mvn clean package +``` +3. 启动基座应用Dubbo26BaseApplication.java,为了方便本地测试用,启动基座时,默认也启动模块 +```java +public static void run(String... args) throws Exception { + try { + installBiz("dubbo27biz/target/dubbo27biz-0.0.1-SNAPSHOT-ark-biz.jar"); + installBiz("dubbo27biz2/target/dubbo27biz2-0.0.1-SNAPSHOT-ark-biz.jar"); + } catch (Throwable e) { + LOGGER.error("Install biz failed", e); + } +} +``` +也可以用curl、telnet、arkctl等部署工具手动安装,此处不再赘述。确保基座和模块启动成功。 +4. 查看模块安装是否成功 +```shell +curl --location --request POST 'localhost:1238/queryAllBiz' +``` +可以查看到所有安装好的模块列表 + +5. 验证模块的RPC调用 +模块biz远程调用biz2发布的dubbo服务(因为有dubbo网络调用,执行前请关闭vpn,否则可能出现调用超时) +```shell +curl localhost:8080/biz +``` +返回 +```shell +{ + "result": "biz->com.alipay.sofa.rpc.dubbo26.biz.service.BizDemoServiceImpl" +} +``` +模块injvm调用基座发布的服务 +```shell +curl localhost:8080/biz/baseInJvm +``` +返回 +```json +{ + "result": "biz->com.alipay.sofa.rpc.dubbo26.base.service.BaseDemoService" +} +``` +6. 验证基座的 RPC/JVM调用 +基座调用biz模块发布的injvm服务 +```shell +curl http://localhost:8080/bizInJvm +``` +返回 +```shell +{ + "result": "base->com.alipay.sofa.rpc.dubbo26.biz.service.BizDemoServiceImpl" +} +``` + +### 说明 +因为有dubbo网络调用,执行前请关闭vpn,否则可能出现调用超时。 + +dubbo2.7 暂时只支持java序列化,不支持热部署能力,如有需要请提一个issue告诉我们。 + diff --git a/sofa-serverless-runtime/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/api/tunnel/http/netty/NettyHttpServer.java b/sofa-serverless-runtime/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/api/tunnel/http/netty/NettyHttpServer.java index bd16dc167..ac923f958 100644 --- a/sofa-serverless-runtime/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/api/tunnel/http/netty/NettyHttpServer.java +++ b/sofa-serverless-runtime/arklet-core/src/main/java/com/alipay/sofa/serverless/arklet/core/api/tunnel/http/netty/NettyHttpServer.java @@ -145,8 +145,10 @@ protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) returnResponse(ctx, response); } } catch (Throwable e) { - returnResponse(ctx, Response.internalError("Internal Error: " + e.getMessage(), - ExceptionUtils.getStackTraceAsString(e))); + returnResponse( + ctx, + Response.internalError("Internal Error: " + e.getMessage(), + ExceptionUtils.getStackTraceAsString(e))); LOGGER.error("arklet process exception, cmd: {}", validation.getCmd(), e); } } From 21807c955a62a7cfd37b0f97d04e006addb701ed Mon Sep 17 00:00:00 2001 From: yuanyuan Date: Wed, 3 Jan 2024 17:56:46 +0800 Subject: [PATCH 05/13] add README --- samples/dubbo-samples/rpc/dubbo27/README.md | 49 +++++++++++---------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/samples/dubbo-samples/rpc/dubbo27/README.md b/samples/dubbo-samples/rpc/dubbo27/README.md index a42711006..f5a4b6ce2 100644 --- a/samples/dubbo-samples/rpc/dubbo27/README.md +++ b/samples/dubbo-samples/rpc/dubbo27/README.md @@ -175,36 +175,37 @@ curl --location --request POST 'localhost:1238/queryAllBiz' 可以查看到所有安装好的模块列表 5. 验证模块的RPC调用 + 模块biz远程调用biz2发布的dubbo服务(因为有dubbo网络调用,执行前请关闭vpn,否则可能出现调用超时) ```shell -curl localhost:8080/biz -``` -返回 -```shell -{ - "result": "biz->com.alipay.sofa.rpc.dubbo26.biz.service.BizDemoServiceImpl" -} -``` -模块injvm调用基座发布的服务 -```shell -curl localhost:8080/biz/baseInJvm -``` -返回 -```json -{ - "result": "biz->com.alipay.sofa.rpc.dubbo26.base.service.BaseDemoService" -} +curl localhost:8080/biz/ +{"result":"bizcom.alipay.sofa.rpc.dubbo27.biz2.service.BizDemoServiceImpl"} + +curl "localhost:8080/biz/?ref=second" +{"result":"biz->com.alipay.sofa.rpc.dubbo27.biz2.service.SecondDemoServiceImpl"} + +curl localhost:8080/biz/hello +{"data":"null->com.alipay.sofa.rpc.dubbo27.biz2.service.HelloServiceImpl"} ``` -6. 验证基座的 RPC/JVM调用 -基座调用biz模块发布的injvm服务 + +模块biz2远程调用biz发布的dubbo服务 ```shell -curl http://localhost:8080/bizInJvm +curl localhost:8080/biz2/ +{"result":"biz2->com.alipay.sofa.rpc.dubbo27.biz.service.BizDemoServiceImpl"} + +curl "localhost:8080/biz2/?ref=second" +{"result":"biz2->com.alipay.sofa.rpc.dubbo27.biz.service.SecondDemoServiceImpl"} + +curl localhost:8080/biz2/hello +{"data":"null->com.alipay.sofa.rpc.dubbo27.biz.service.HelloServiceImpl"} ``` -返回 + +6. 验证基座的 RPC 调用自己 + +基座rpc调用自己发布的dubbo服务 ```shell -{ - "result": "base->com.alipay.sofa.rpc.dubbo26.biz.service.BizDemoServiceImpl" -} +curl localhost:8080 +{"result":"base->com.alipay.sofa.rpc.dubbo27.base.service.MasterDemoServiceImpl"} ``` ### 说明 From 474884fb85edfc4eb335a77405fce1a6f81c707c Mon Sep 17 00:00:00 2001 From: yuanyuan Date: Wed, 3 Jan 2024 18:00:49 +0800 Subject: [PATCH 06/13] add README --- samples/dubbo-samples/rpc/dubbo27/README.md | 30 ++++++--------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/samples/dubbo-samples/rpc/dubbo27/README.md b/samples/dubbo-samples/rpc/dubbo27/README.md index f5a4b6ce2..62bae3e77 100644 --- a/samples/dubbo-samples/rpc/dubbo27/README.md +++ b/samples/dubbo-samples/rpc/dubbo27/README.md @@ -87,12 +87,16 @@ base 为普通 dubbo 应用改造而成,改造内容只需在主 pom 里增加 ### 3. 声明dubbo服务和引用 biz1声明了三个rpc服务: > biz1/com.alipay.sofa.rpc.dubbo27.model.DemoService +> > biz1Second/com.alipay.sofa.rpc.dubbo27.model.DemoService +> > biz1/com.alipay.sofa.rpc.dubbo27.model.HelloService 三个rpc引用,remote调用biz2发布的rpc服务 > biz2/com.alipay.sofa.rpc.dubbo27.model.DemoService +> > biz2Second/com.alipay.sofa.rpc.dubbo27.model.DemoService +> > biz2/com.alipay.sofa.rpc.dubbo27.model.HelloService 服务发布与引用配置如下: @@ -113,37 +117,19 @@ biz1声明了三个rpc服务: 类似的,biz2声明了三个rpc服务: > biz2/com.alipay.sofa.rpc.dubbo27.model.DemoService +> > biz2Second/com.alipay.sofa.rpc.dubbo27.model.DemoService +> > biz2/com.alipay.sofa.rpc.dubbo27.model.HelloService 三个rpc引用,remote调用biz1发布的rpc服务 > biz1/com.alipay.sofa.rpc.dubbo27.model.DemoService +> > biz1Second/com.alipay.sofa.rpc.dubbo27.model.DemoService +> > biz1/com.alipay.sofa.rpc.dubbo27.model.HelloService - - - - -### 测试调用代码 -发布一个服务 -``` xml - - - - -``` - -在 BizController 里引用了模块自己发布的RPC,基座发布的injvm服务。 -``` xml - - - - -``` - - ### 运行代码 1. 【重要】首次运行,先编译安装下用于通信包 ```shell From e900d4d6837389b44dd4b38abbb1b31c1451a382 Mon Sep 17 00:00:00 2001 From: yuanyuan Date: Mon, 15 Jan 2024 21:09:31 +0800 Subject: [PATCH 07/13] fix samples/service/biz2 --- .../main/java/com/alipay/sofa/biz2/rest/SampleController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/springboot-samples/service/biz2/biz2-bootstrap/src/main/java/com/alipay/sofa/biz2/rest/SampleController.java b/samples/springboot-samples/service/biz2/biz2-bootstrap/src/main/java/com/alipay/sofa/biz2/rest/SampleController.java index 06bf396d8..3201f3cc1 100644 --- a/samples/springboot-samples/service/biz2/biz2-bootstrap/src/main/java/com/alipay/sofa/biz2/rest/SampleController.java +++ b/samples/springboot-samples/service/biz2/biz2-bootstrap/src/main/java/com/alipay/sofa/biz2/rest/SampleController.java @@ -77,10 +77,10 @@ public String hello() { Result provide2 = provider.provide(new Param()); } - Provider teacherProvider1 = SpringServiceFinder.getModuleService("biz", "0.0.1-SNAPSHOT", "teacherProvider", Provider.class); + Provider teacherProvider1 = SpringServiceFinder.getModuleService("biz1", "0.0.1-SNAPSHOT", "teacherProvider", Provider.class); Result result1 = teacherProvider1.provide(new Param()); - Map providerMap = SpringServiceFinder.listModuleServices("biz", "0.0.1-SNAPSHOT", Provider.class); + Map providerMap = SpringServiceFinder.listModuleServices("biz1", "0.0.1-SNAPSHOT", Provider.class); for (String beanName : providerMap.keySet()) { Result result2 = providerMap.get(beanName).provide(new Param()); } From 0c400f45727a6f556346ad3626a9c600a4338ac5 Mon Sep 17 00:00:00 2001 From: yuanyuan Date: Thu, 18 Jan 2024 22:12:38 +0800 Subject: [PATCH 08/13] stash --- .../rpc/dubbo26/base/Dubbo26BaseApplication.java | 4 ++-- .../dubbo-samples/rpc/dubbo27/dubbo27base/pom.xml | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/samples/dubbo-samples/rpc/dubbo26/dubbo26base/src/main/java/com/alipay/sofa/rpc/dubbo26/base/Dubbo26BaseApplication.java b/samples/dubbo-samples/rpc/dubbo26/dubbo26base/src/main/java/com/alipay/sofa/rpc/dubbo26/base/Dubbo26BaseApplication.java index abc4b37f4..bc09fe9e6 100644 --- a/samples/dubbo-samples/rpc/dubbo26/dubbo26base/src/main/java/com/alipay/sofa/rpc/dubbo26/base/Dubbo26BaseApplication.java +++ b/samples/dubbo-samples/rpc/dubbo26/dubbo26base/src/main/java/com/alipay/sofa/rpc/dubbo26/base/Dubbo26BaseApplication.java @@ -44,8 +44,8 @@ public static void main(String[] args) { @Override public void run(String... args) throws Exception { try { - installBiz("dubbo26biz/target/dubbo26biz-0.0.1-SNAPSHOT-ark-biz.jar"); - installBiz("dubbo26biz2/target/dubbo26biz2-0.0.1-SNAPSHOT-ark-biz.jar"); +// installBiz("dubbo26biz/target/dubbo26biz-0.0.1-SNAPSHOT-ark-biz.jar"); +// installBiz("dubbo26biz2/target/dubbo26biz2-0.0.1-SNAPSHOT-ark-biz.jar"); } catch (Throwable e) { LOGGER.error("Install biz failed", e); } diff --git a/samples/dubbo-samples/rpc/dubbo27/dubbo27base/pom.xml b/samples/dubbo-samples/rpc/dubbo27/dubbo27base/pom.xml index d12d9b5a2..4bacdad7e 100644 --- a/samples/dubbo-samples/rpc/dubbo27/dubbo27base/pom.xml +++ b/samples/dubbo-samples/rpc/dubbo27/dubbo27base/pom.xml @@ -29,8 +29,8 @@
com.alipay.sofa.serverless - sofa-serverless-adapter-dubbo27 - ${sofa.serverless.runtime.version} + sofa-serverless-adapter-dubbo2.7 + 0.5.7-SNAPSHOT com.alipay.sofa @@ -57,11 +57,11 @@ spring-boot-starter-log4j2 - - com.alipay.sofa.serverless - sofa-serverless-adapter-dubbo2.7 - ${sofa.serverless.runtime.version} - + + + + + com.lmax disruptor From a089cbfa88b6f164a9a6ffa6496c96b961737319 Mon Sep 17 00:00:00 2001 From: yuanyuan Date: Fri, 19 Jan 2024 17:53:25 +0800 Subject: [PATCH 09/13] =?UTF-8?q?1.=20=E6=A8=A1=E5=9D=97=E7=A6=81=E6=AD=A2?= =?UTF-8?q?DubboBootstrapApplicationListener=202.=20BizDubboBootstrapListe?= =?UTF-8?q?ner=E5=A2=9E=E5=8A=A0biz=E5=8D=B8=E8=BD=BD=E6=97=B6unexport?= =?UTF-8?q?=E5=BD=93=E5=89=8Dbiz=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rpc/dubbo27/dubbo27biz/pom.xml | 2 +- .../rpc/dubbo27/dubbo27biz2/pom.xml | 2 +- .../biz2/service/BizDemoServiceImpl.java | 2 +- .../dubbo/BizDubboBootstrapListener.java | 41 ++++++++- .../DubboBootstrapApplicationListener.java | 90 +++++++++++++++++++ 5 files changed, 132 insertions(+), 5 deletions(-) create mode 100644 sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapApplicationListener.java diff --git a/samples/dubbo-samples/rpc/dubbo27/dubbo27biz/pom.xml b/samples/dubbo-samples/rpc/dubbo27/dubbo27biz/pom.xml index 07bd502c5..947ca2857 100644 --- a/samples/dubbo-samples/rpc/dubbo27/dubbo27biz/pom.xml +++ b/samples/dubbo-samples/rpc/dubbo27/dubbo27biz/pom.xml @@ -43,7 +43,7 @@ com.alipay.sofa.serverless sofa-serverless-adapter-dubbo2.7 - ${sofa.serverless.runtime.version} + 0.5.7-SNAPSHOT provided diff --git a/samples/dubbo-samples/rpc/dubbo27/dubbo27biz2/pom.xml b/samples/dubbo-samples/rpc/dubbo27/dubbo27biz2/pom.xml index ac3192c37..35da1bc61 100644 --- a/samples/dubbo-samples/rpc/dubbo27/dubbo27biz2/pom.xml +++ b/samples/dubbo-samples/rpc/dubbo27/dubbo27biz2/pom.xml @@ -43,7 +43,7 @@ com.alipay.sofa.serverless sofa-serverless-adapter-dubbo2.7 - ${sofa.serverless.runtime.version} + 0.5.7-SNAPSHOT provided diff --git a/samples/dubbo-samples/rpc/dubbo27/dubbo27biz2/src/main/java/com/alipay/sofa/rpc/dubbo27/biz2/service/BizDemoServiceImpl.java b/samples/dubbo-samples/rpc/dubbo27/dubbo27biz2/src/main/java/com/alipay/sofa/rpc/dubbo27/biz2/service/BizDemoServiceImpl.java index d68be17c8..030c4766b 100644 --- a/samples/dubbo-samples/rpc/dubbo27/dubbo27biz2/src/main/java/com/alipay/sofa/rpc/dubbo27/biz2/service/BizDemoServiceImpl.java +++ b/samples/dubbo-samples/rpc/dubbo27/dubbo27biz2/src/main/java/com/alipay/sofa/rpc/dubbo27/biz2/service/BizDemoServiceImpl.java @@ -23,7 +23,7 @@ public class BizDemoServiceImpl implements DemoService { @Override public DemoResponse handle(DemoRequest demoRequest) { DemoResponse response = new DemoResponse(); - response.setResult(demoRequest.getBiz() + getClass().getName()); + response.setResult(demoRequest.getBiz() + "->" + getClass().getName()); return response; } } \ No newline at end of file diff --git a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/BizDubboBootstrapListener.java b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/BizDubboBootstrapListener.java index 34cf63d9b..7d1aaf3a6 100644 --- a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/BizDubboBootstrapListener.java +++ b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/BizDubboBootstrapListener.java @@ -16,11 +16,21 @@ */ package com.alipay.sofa.serverless.support.dubbo; +import org.apache.dubbo.config.ServiceConfigBase; import org.apache.dubbo.config.bootstrap.DubboBootstrap; +import org.apache.dubbo.config.context.ConfigManager; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.ServiceRepository; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextRefreshedEvent; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + /** * @author: yuanyuan * @date: 2023/12/25 4:14 下午 @@ -29,12 +39,14 @@ public class BizDubboBootstrapListener implements ApplicationListener { private final DubboBootstrap dubboBootstrap; - // private final ConfigManager configManager; + private final ConfigManager configManager; + private final ServiceRepository serviceRepository; // private final Environment environment; public BizDubboBootstrapListener() { this.dubboBootstrap = DubboBootstrap.getInstance(); - // this.configManager = ApplicationModel.getConfigManager(); + this.configManager = ApplicationModel.getConfigManager(); + this.serviceRepository = ApplicationModel.getServiceRepository(); // this.environment = ApplicationModel.getEnvironment(); } @@ -46,6 +58,9 @@ public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ContextRefreshedEvent) { onContextRefreshedEvent((ContextRefreshedEvent) event); } + if (event instanceof ContextClosedEvent) { + onContextClosedEvent((ContextClosedEvent) event); + } } private void onContextRefreshedEvent(ContextRefreshedEvent event) { @@ -57,4 +72,26 @@ private void onContextRefreshedEvent(ContextRefreshedEvent event) { e.printStackTrace(); } } + + private void onContextClosedEvent(ContextClosedEvent event) { + // DubboBootstrap.unexportServices 会 unexport 所有服务,只需要 unexport 当前 biz 的服务即可 + Map> exportedServices = ReflectionUtils.getField(dubboBootstrap, DubboBootstrap.class, "exportedServices"); + + Set bizUnexportServices = new HashSet<>(); + for (Map.Entry> entry : exportedServices.entrySet()) { + String serviceKey = entry.getKey(); + ServiceConfigBase sc = entry.getValue(); + if (sc.getRef().getClass().getClassLoader() == Thread.currentThread().getContextClassLoader()) { + bizUnexportServices.add(serviceKey); + configManager.removeConfig(sc); + sc.unexport(); + + serviceRepository.unregisterService(sc.getUniqueServiceName()); + } + } + for (String service : bizUnexportServices) { + exportedServices.remove(service); + } + + } } diff --git a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapApplicationListener.java b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapApplicationListener.java new file mode 100644 index 000000000..b3e345746 --- /dev/null +++ b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapApplicationListener.java @@ -0,0 +1,90 @@ +/* + * 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 org.apache.dubbo.config.spring.context; + +import org.apache.dubbo.config.DubboShutdownHook; +import org.apache.dubbo.config.bootstrap.DubboBootstrap; + +import com.alibaba.spring.context.OnceApplicationContextEventListener; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ApplicationContextEvent; +import org.springframework.context.event.ContextClosedEvent; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.core.Ordered; + +/** + * The {@link ApplicationListener} for {@link DubboBootstrap}'s lifecycle when the {@link ContextRefreshedEvent} + * and {@link ContextClosedEvent} raised + * + * @since 2.7.5 + */ +public class DubboBootstrapApplicationListener extends OnceApplicationContextEventListener + implements + Ordered { + + /** + * The bean name of {@link DubboBootstrapApplicationListener} + * + * @since 2.7.6 + */ + public static final String BEAN_NAME = "dubboBootstrapApplicationListener"; + + private final DubboBootstrap dubboBootstrap; + + public DubboBootstrapApplicationListener() { + this.dubboBootstrap = DubboBootstrap.getInstance(); + } + + public DubboBootstrapApplicationListener(ApplicationContext applicationContext) { + super(applicationContext); + this.dubboBootstrap = DubboBootstrap.getInstance(); + DubboBootstrapStartStopListenerSpringAdapter.applicationContext = applicationContext; + } + + @Override + public void onApplicationContextEvent(ApplicationContextEvent event) { + // patch begin + if (this.getClass().getClassLoader() != Thread.currentThread().getContextClassLoader()) { + return; + } + // patch end + + if (DubboBootstrapStartStopListenerSpringAdapter.applicationContext == null) { + DubboBootstrapStartStopListenerSpringAdapter.applicationContext = event + .getApplicationContext(); + } + if (event instanceof ContextRefreshedEvent) { + onContextRefreshedEvent((ContextRefreshedEvent) event); + } else if (event instanceof ContextClosedEvent) { + onContextClosedEvent((ContextClosedEvent) event); + } + } + + private void onContextRefreshedEvent(ContextRefreshedEvent event) { + dubboBootstrap.start(); + } + + private void onContextClosedEvent(ContextClosedEvent event) { + DubboShutdownHook.getDubboShutdownHook().run(); + } + + @Override + public int getOrder() { + return LOWEST_PRECEDENCE; + } +} From be388b40b111443311461a664ec536d4f169cf49 Mon Sep 17 00:00:00 2001 From: yuanyuan Date: Mon, 22 Jan 2024 12:21:34 +0800 Subject: [PATCH 10/13] =?UTF-8?q?logback=E9=80=82=E9=85=8D=E6=96=87?= =?UTF-8?q?=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../runtime/logback.md | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 docs/content/zh-cn/docs/contribution-guidelines/runtime/logback.md diff --git a/docs/content/zh-cn/docs/contribution-guidelines/runtime/logback.md b/docs/content/zh-cn/docs/contribution-guidelines/runtime/logback.md new file mode 100644 index 000000000..389390e90 --- /dev/null +++ b/docs/content/zh-cn/docs/contribution-guidelines/runtime/logback.md @@ -0,0 +1,114 @@ +--- +title: logback 的多模块化适配 +date: 2024-1-18T15:32:35+08:00 +weight: 1 +--- + +## 为什么需要做适配 +原生 logback 只有默认日志上下文,各个模块间日志配置无法隔离,无法支持独立的模块日志配置,最终导致在合并部署多模块场景下,模块只能使用基座的日志配置,对模块日志打印带来不便。 + +## 多模块适配方案 +Logback 支持原生扩展 ch.qos.logback.classic.selector.ContextSelector,该接口支持自定义上下文选择器,Ark 默认实现了 ContextSelector 对多个模块的 LoggerContext 进行隔离 (参考 com.alipay.sofa.ark.common.adapter.ArkLogbackContextSelector),不同模块使用各自独立的 LoggerContext,确保日志配置隔离 + +启动期,经由 spring 日志系统 LogbackLoggingSystem 对模块日志配置以及日志上下文进行初始化 + +指定上下文选择器为 com.alipay.sofa.ark.common.adapter.ArkLogbackContextSelector,添加JVM启动参数 +> -Dlogback.ContextSelector=com.alipay.sofa.ark.common.adapter.ArkLogbackContextSelector + +当使用 slf4j 作为日志门面,logback 作为日志实现框架时,在基座启动时,首次进行 slf4j 静态绑定时,将初始化具体的 ContextSelector,当没有自定义上下文选择器时,将使用 DefaultContextSelector, 当我们指定上下文选择器时,将会初始化 ArkLogbackContextSelector 作为上下文选择器 + +ch.qos.logback.classic.util.ContextSelectorStaticBinder.init + +```java +public void init(LoggerContext defaultLoggerContext, Object key) { + ... + + String contextSelectorStr = OptionHelper.getSystemProperty(ClassicConstants.LOGBACK_CONTEXT_SELECTOR); + if (contextSelectorStr == null) { + contextSelector = new DefaultContextSelector(defaultLoggerContext); + } else if (contextSelectorStr.equals("JNDI")) { + // if jndi is specified, let's use the appropriate class + contextSelector = new ContextJNDISelector(defaultLoggerContext); + } else { + contextSelector = dynamicalContextSelector(defaultLoggerContext, contextSelectorStr); + } +} + +static ContextSelector dynamicalContextSelector(LoggerContext defaultLoggerContext, String contextSelectorStr) { + Class contextSelectorClass = Loader.loadClass(contextSelectorStr); + Constructor cons = contextSelectorClass.getConstructor(new Class[] { LoggerContext.class }); + return (ContextSelector) cons.newInstance(defaultLoggerContext); +} +``` + +在 ArkLogbackContextSelector 中,我们使用 ClassLoader 区分不同模块,将模块 LoggerContext 根据 ClassLoader 缓存 + +根据 classloader 获取不同的 LoggerContext,在 Spring 环境启动时,根据 spring 日志系统初始化日志上下文,通过 org.springframework.boot.logging.logback.LogbackLoggingSystem.getLoggerContext 获取日志上下文,此时将会使用 Ark 实现的自定义上下文选择器 com.alipay.sofa.ark.common.adapter.ArkLogbackContextSelector.getLoggerContext() 返回不同模块各自的 LoggerContext + +```java +public LoggerContext getLoggerContext() { + ClassLoader classLoader = this.findClassLoader(); + if (classLoader == null) { + return defaultLoggerContext; + } + return getContext(classLoader); +} +``` + +获取 classloader 时,首先获取线程上下文 classloader,当发现是模块的classloader时,直接返回,若tccl不是模块classloader,则从ClassContext中获取调用Class堆栈,遍历堆栈,当发现模块classloader时直接返回,这样做的目的是为了兼容tccl没有保证为模块classloader时的场景, +比如在模块代码中使用logger打印日志时,当前类由模块classloader自己加载,通过ClassContext遍历可以最终获得当前类,获取到模块classloader,以便确保使用模块对应的 LoggerContext + +```java +private ClassLoader findClassLoader() { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader != null && CONTAINER_CLASS_LOADER.equals(classLoader.getClass().getName())) { + return null; + } + if (classLoader != null && BIZ_CLASS_LOADER.equals(classLoader.getClass().getName())) { + return classLoader; + } + + Class[] context = new SecurityManager() { + @Override + public Class[] getClassContext() { + return super.getClassContext(); + } + }.getClassContext(); + if (context == null || context.length == 0) { + return null; + } + for (Class cls : context) { + if (cls.getClassLoader() != null + && BIZ_CLASS_LOADER.equals(cls.getClassLoader().getClass().getName())) { + return cls.getClassLoader(); + } + } + + return null; +} +``` + +获取到合适 classloader 后,为不同 classloader选择不同的 LoggerContext 实例,所有模块上下文缓存在 com.alipay.sofa.ark.common.adapter.ArkLogbackContextSelector.CLASS_LOADER_LOGGER_CONTEXT 中,以 classloader 为 key + +```java +private LoggerContext getContext(ClassLoader cls) { + LoggerContext loggerContext = CLASS_LOADER_LOGGER_CONTEXT.get(cls); + if (null == loggerContext) { + synchronized (ArkLogbackContextSelector.class) { + loggerContext = CLASS_LOADER_LOGGER_CONTEXT.get(cls); + if (null == loggerContext) { + loggerContext = new LoggerContext(); + loggerContext.setName(Integer.toHexString(System.identityHashCode(cls))); + CLASS_LOADER_LOGGER_CONTEXT.put(cls, loggerContext); + } + } + } + return loggerContext; +} +``` + +## 多模块 logback 使用样例 +[多模块 logback 使用样例](https://github.com/sofastack/sofa-serverless/tree/master/samples/springboot-samples/logging/logback/README.md) + +[详细查看ArkLogbackContextSelector源码](https://github.com/sofastack/sofa-ark/blob/master/sofa-ark-parent/core/common/src/main/java/com/alipay/sofa/ark/common/adapter/ArkLogbackContextSelector.java) + From 6433bccfbae0595f16667619ca509af309feba01 Mon Sep 17 00:00:00 2001 From: yuanyuan Date: Mon, 22 Jan 2024 15:44:51 +0800 Subject: [PATCH 11/13] =?UTF-8?q?dubbo2.7=E9=80=82=E9=85=8D=E6=96=87?= =?UTF-8?q?=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../runtime/dubbo2.7.md | 194 ++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 docs/content/zh-cn/docs/contribution-guidelines/runtime/dubbo2.7.md diff --git a/docs/content/zh-cn/docs/contribution-guidelines/runtime/dubbo2.7.md b/docs/content/zh-cn/docs/contribution-guidelines/runtime/dubbo2.7.md new file mode 100644 index 000000000..f9cd83238 --- /dev/null +++ b/docs/content/zh-cn/docs/contribution-guidelines/runtime/dubbo2.7.md @@ -0,0 +1,194 @@ +--- +title: dubbo2.7 的多模块化适配 +date: 2024-1-19T19:55:35+08:00 +weight: 1 +--- + +## 为什么需要做适配 +原生 dubbo2.7 在多模块场景下,无法支持模块发布自己的dubbo服务,调用时存在序列化、类加载异常等一系列问题。 + +## 多模块适配方案 + +dubbo2.7多模块适配SDK +```xml + + com.alipay.sofa.serverless + sofa-serverless-adapter-dubbo2.7 + 0.5.7-SNAPSHOT + +``` + +主要从类加载、服务发布、服务卸载、服务隔离、模块维度服务管理、配置管理、序列化等方面进行适配。 + +### 1. AnnotatedBeanDefinitionRegistryUtils使用基座classloader无法加载模块类 +com.alibaba.spring.util.AnnotatedBeanDefinitionRegistryUtils#isPresentBean + +```java +public static boolean isPresentBean(BeanDefinitionRegistry registry, Class annotatedClass) { + ... + + // ClassLoader classLoader = annotatedClass.getClassLoader(); // 原生逻辑 + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // 改为使用tccl加载类 + + for (String beanName : beanNames) { + BeanDefinition beanDefinition = registry.getBeanDefinition(beanName); + if (beanDefinition instanceof AnnotatedBeanDefinition) { + ... + String className = annotationMetadata.getClassName(); + Class targetClass = resolveClassName(className, classLoader); + ... + } + } + + return present; +} +``` + +### 2. 模块维度的服务、配置资源管理 +1. com.alipay.sofa.serverless.support.dubbo.ServerlessServiceRepository 替代原生 org.apache.dubbo.rpc.model.ServiceRepository + +原生service采用interfaceName作为缓存,在基座、模块发布同样interface,不同group服务时,无法区分,替代原生service缓存模型,采用Interface Class类型作为key,同时采用包含有group的path作为key,支持基座、模块发布同interface不同group的场景 +```java +private static ConcurrentMap, ServiceDescriptor> globalClassServices = new ConcurrentHashMap<>(); + +private static ConcurrentMap globalPathServices = new ConcurrentHashMap<>(); +``` + +2. com.alipay.sofa.serverless.support.dubbo.ServerlessConfigManager 替代原生 org.apache.dubbo.config.context.ConfigManager + + 为原生config添加classloader维度的key,不同模块根据classloader隔离不同的配置 + +```java +final Map>> globalConfigsCache = new HashMap<>(); + +public void addConfig(AbstractConfig config, boolean unique) { + ... + write(() -> { + Map configsMap = getCurrentConfigsCache().computeIfAbsent(getTagName(config.getClass()), type -> newMap()); + addIfAbsent(config, configsMap, unique); + }); +} +private Map> getCurrentConfigsCache() { + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); // 根据当前线程classloader隔离不同配置缓存 + globalConfigsCache.computeIfAbsent(contextClassLoader, k -> new HashMap<>()); + return globalConfigsCache.get(contextClassLoader); +} +``` + +ServerlessServiceRepository 和 ServerlessConfigManager 都依赖 dubbo ExtensionLoader 的扩展机制,从而替代原生逻辑,具体原理可参考 org.apache.dubbo.common.extension.ExtensionLoader.createExtension + +### 3. 模块维度服务发布、服务卸载 +override DubboBootstrapApplicationListener 禁止原生dubbo模块启动、卸载时发布、卸载服务 + +- com.alipay.sofa.serverless.support.dubbo.BizDubboBootstrapListener + +原生dubbo2.7只在基座启动完成后发布dubbo服务,在多模块时,无法支持模块的服务发布,Ark采用监听器监听模块启动事件,并手动调用dubbo进行模块维度的服务发布 + +```java +private void onContextRefreshedEvent(ContextRefreshedEvent event) { + try { + ReflectionUtils.getMethod(DubboBootstrap.class, "exportServices") + .invoke(dubboBootstrap); + ReflectionUtils.getMethod(DubboBootstrap.class, "referServices").invoke(dubboBootstrap); + } catch (Exception e) { + + } +} +``` + +原生dubbo2.7在模块卸载时会调用DubboShutdownHook,将JVM中所有dubbo service unexport,导致模块卸载后基座、其余模块服务均被卸载,Ark采用监听器监听模块spring上下文关闭事件,手动卸载当前模块的dubbo服务,保留基座、其余模块的dubbo服务 + +```java +private void onContextClosedEvent(ContextClosedEvent event) { + // DubboBootstrap.unexportServices 会 unexport 所有服务,只需要 unexport 当前 biz 的服务即可 + Map> exportedServices = ReflectionUtils.getField(dubboBootstrap, DubboBootstrap.class, "exportedServices"); + + Set bizUnexportServices = new HashSet<>(); + for (Map.Entry> entry : exportedServices.entrySet()) { + String serviceKey = entry.getKey(); + ServiceConfigBase sc = entry.getValue(); + if (sc.getRef().getClass().getClassLoader() == Thread.currentThread().getContextClassLoader()) { // 根据ref服务实现的类加载器区分模块服务 + bizUnexportServices.add(serviceKey); + configManager.removeConfig(sc); // 从configManager配置管理中移除服务配置 + sc.unexport(); // 进行服务unexport + serviceRepository.unregisterService(sc.getUniqueServiceName()); // 从serviceRepository服务管理中移除配置 + } + } + for (String service : bizUnexportServices) { + exportedServices.remove(service); // 从DubboBootstrap中移除该service + } + } +``` + +### 4. 服务路由 +- com.alipay.sofa.serverless.support.dubbo.ConsumerRedefinePathFilter + +dubbo服务调用时通过path从ServiceRepository中获取正确的服务端服务模型(包括interface、param、return类型等)进行服务调用、参数、返回值的序列化,原生dubbo2.7采用interfaceName作为path查找service model,无法支持多模块下基座模块发布同interface的场景,Ark自定义consumer端filter添加group信息到path中,以便provider端进行正确的服务路由 + +```java +public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { + if (invocation instanceof RpcInvocation) { + RpcInvocation rpcInvocation = (RpcInvocation) invocation; + // 原生path为interfaceName,如com.alipay.sofa.rpc.dubbo27.model.DemoService + // 修改后path为serviceUniqueName,如masterBiz/com.alipay.sofa.rpc.dubbo27.model.DemoService + rpcInvocation.setAttachment("interface", rpcInvocation.getTargetServiceUniqueName()); // 原生path为interfaceName,如 + } + return invoker.invoke(invocation); +} +``` + +### 5. 序列化 +- org.apache.dubbo.common.serialize.java.JavaSerialization +- org.apache.dubbo.common.serialize.java.ClassLoaderJavaObjectInput +- org.apache.dubbo.common.serialize.java.ClassLoaderObjectInputStream + +在获取序列化工具JavaSerialization时,使用ClassLoaderJavaObjectInput替代原生JavaObjectInput,传递provider端service classloader信息 + +```java +// org.apache.dubbo.common.serialize.java.JavaSerialization +public ObjectInput deserialize(URL url, InputStream is) throws IOException { + return new ClassLoaderJavaObjectInput(new ClassLoaderObjectInputStream(null, is)); // 使用ClassLoaderJavaObjectInput替代原生JavaObjectInput,传递provider端service classloader信息 +} + +// org.apache.dubbo.common.serialize.java.ClassLoaderObjectInputStream +private ClassLoader classLoader; + +public ClassLoaderObjectInputStream(final ClassLoader classLoader, final InputStream inputStream) { + super(inputStream); + this.classLoader = classLoader; +} +``` + +- org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation 服务端反序列化参数 + +```java +// patch begin +if (in instanceof ClassLoaderJavaObjectInput) { + InputStream is = ((ClassLoaderJavaObjectInput) in).getInputStream(); + if (is instanceof ClassLoaderObjectInputStream) { + ClassLoader cl = serviceDescriptor.getServiceInterfaceClass().getClassLoader(); // 设置provider端service classloader信息到ClassLoaderObjectInputStream中 + ((ClassLoaderObjectInputStream) is).setClassLoader(cl); + } +} +// patch end +``` +- org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcResult 客户端反序列化返回值 + +```java +// patch begin +if (in instanceof ClassLoaderJavaObjectInput) { + InputStream is = ((ClassLoaderJavaObjectInput) in).getInputStream(); + if (is instanceof ClassLoaderObjectInputStream) { + ClassLoader cl = invocation.getInvoker().getInterface().getClassLoader(); // 设置consumer端service classloader信息到ClassLoaderObjectInputStream中 + ((ClassLoaderObjectInputStream) is).setClassLoader(cl); + } +} +// patch end +``` + +## 多模块 dubbo2.7 使用样例 + +[多模块 dubbo2.7 使用样例](https://github.com/sofastack/sofa-serverless/tree/master/samples/dubbo-samples/rpc/dubbo27/README.md) + +[dubbo2.7多模块适配sdk源码](https://github.com/sofastack/sofa-serverless/tree/master/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7) + From 4b4ed7fbca91f58aeea3e511076204bf9b2b31e8 Mon Sep 17 00:00:00 2001 From: yuanyuan Date: Mon, 22 Jan 2024 17:19:33 +0800 Subject: [PATCH 12/13] fix dubbo2.6 / dubbo3 sample --- .../alipay/sofa/rpc/dubbo26/base/Dubbo26BaseApplication.java | 4 ++-- .../dubbo26biz/src/main/resources/application.properties | 2 ++ .../dubbo26biz2/src/main/resources/application.properties | 2 ++ .../rpc/dubbo3/dubbo3base/src/main/resources/application.yml | 2 ++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/samples/dubbo-samples/rpc/dubbo26/dubbo26base/src/main/java/com/alipay/sofa/rpc/dubbo26/base/Dubbo26BaseApplication.java b/samples/dubbo-samples/rpc/dubbo26/dubbo26base/src/main/java/com/alipay/sofa/rpc/dubbo26/base/Dubbo26BaseApplication.java index bc09fe9e6..abc4b37f4 100644 --- a/samples/dubbo-samples/rpc/dubbo26/dubbo26base/src/main/java/com/alipay/sofa/rpc/dubbo26/base/Dubbo26BaseApplication.java +++ b/samples/dubbo-samples/rpc/dubbo26/dubbo26base/src/main/java/com/alipay/sofa/rpc/dubbo26/base/Dubbo26BaseApplication.java @@ -44,8 +44,8 @@ public static void main(String[] args) { @Override public void run(String... args) throws Exception { try { -// installBiz("dubbo26biz/target/dubbo26biz-0.0.1-SNAPSHOT-ark-biz.jar"); -// installBiz("dubbo26biz2/target/dubbo26biz2-0.0.1-SNAPSHOT-ark-biz.jar"); + installBiz("dubbo26biz/target/dubbo26biz-0.0.1-SNAPSHOT-ark-biz.jar"); + installBiz("dubbo26biz2/target/dubbo26biz2-0.0.1-SNAPSHOT-ark-biz.jar"); } catch (Throwable e) { LOGGER.error("Install biz failed", e); } diff --git a/samples/dubbo-samples/rpc/dubbo26/dubbo26biz/src/main/resources/application.properties b/samples/dubbo-samples/rpc/dubbo26/dubbo26biz/src/main/resources/application.properties index 96ccd47ae..7e9103d60 100644 --- a/samples/dubbo-samples/rpc/dubbo26/dubbo26biz/src/main/resources/application.properties +++ b/samples/dubbo-samples/rpc/dubbo26/dubbo26biz/src/main/resources/application.properties @@ -5,6 +5,8 @@ logging.level.root=INFO logging.level.com.alipay.sofa.arklet=INFO #logging.config=classpath:log4j2-spring.xml +spring.main.allow-bean-definition-overriding=true + dubbo.application.logger=slf4j dubbo.application.name=biz dubbo.consumer.serialization=java diff --git a/samples/dubbo-samples/rpc/dubbo26/dubbo26biz2/src/main/resources/application.properties b/samples/dubbo-samples/rpc/dubbo26/dubbo26biz2/src/main/resources/application.properties index e115314f4..9bc9582f6 100644 --- a/samples/dubbo-samples/rpc/dubbo26/dubbo26biz2/src/main/resources/application.properties +++ b/samples/dubbo-samples/rpc/dubbo26/dubbo26biz2/src/main/resources/application.properties @@ -5,6 +5,8 @@ logging.level.root=INFO logging.level.com.alipay.sofa.arklet=INFO #logging.config=classpath:log4j2-spring.xml +spring.main.allow-bean-definition-overriding=true + dubbo.application.logger=slf4j dubbo.application.name=biz2 dubbo.consumer.serialization=java diff --git a/samples/dubbo-samples/rpc/dubbo3/dubbo3base/src/main/resources/application.yml b/samples/dubbo-samples/rpc/dubbo3/dubbo3base/src/main/resources/application.yml index 3d9e3f67a..cd69197c3 100644 --- a/samples/dubbo-samples/rpc/dubbo3/dubbo3base/src/main/resources/application.yml +++ b/samples/dubbo-samples/rpc/dubbo3/dubbo3base/src/main/resources/application.yml @@ -8,6 +8,8 @@ dubbo: name: tri port: 50051 +server: + port: 8089 spring: application: From 7c1ffc976d41c1ec6da5933335500196fd2e1413 Mon Sep 17 00:00:00 2001 From: yuanyuan Date: Mon, 22 Jan 2024 17:38:22 +0800 Subject: [PATCH 13/13] fix ut --- .../support/dubbo/BizDubboBootstrapListener.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/BizDubboBootstrapListener.java b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/BizDubboBootstrapListener.java index 7d1aaf3a6..4fb129e80 100644 --- a/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/BizDubboBootstrapListener.java +++ b/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-dubbo2.7/src/main/java/com/alipay/sofa/serverless/support/dubbo/BizDubboBootstrapListener.java @@ -37,10 +37,11 @@ */ public class BizDubboBootstrapListener implements ApplicationListener { - private final DubboBootstrap dubboBootstrap; + private final DubboBootstrap dubboBootstrap; + + private final ConfigManager configManager; + private final ServiceRepository serviceRepository; - private final ConfigManager configManager; - private final ServiceRepository serviceRepository; // private final Environment environment; public BizDubboBootstrapListener() { @@ -75,13 +76,15 @@ private void onContextRefreshedEvent(ContextRefreshedEvent event) { private void onContextClosedEvent(ContextClosedEvent event) { // DubboBootstrap.unexportServices 会 unexport 所有服务,只需要 unexport 当前 biz 的服务即可 - Map> exportedServices = ReflectionUtils.getField(dubboBootstrap, DubboBootstrap.class, "exportedServices"); + Map> exportedServices = ReflectionUtils.getField( + dubboBootstrap, DubboBootstrap.class, "exportedServices"); Set bizUnexportServices = new HashSet<>(); for (Map.Entry> entry : exportedServices.entrySet()) { String serviceKey = entry.getKey(); ServiceConfigBase sc = entry.getValue(); - if (sc.getRef().getClass().getClassLoader() == Thread.currentThread().getContextClassLoader()) { + if (sc.getRef().getClass().getClassLoader() == Thread.currentThread() + .getContextClassLoader()) { bizUnexportServices.add(serviceKey); configManager.removeConfig(sc); sc.unexport();