From a378db134d05419d105d897c05adcff980586ca8 Mon Sep 17 00:00:00 2001 From: kimmking Date: Tue, 29 Nov 2022 17:26:57 +0800 Subject: [PATCH 01/13] add multiple beans registrar --- 04fx/demo/pom.xml | 9 +- .../kimmking/javacourse/demo/DemoConfig.java | 17 +++ .../demo/DemoConfigBindingsRegistrar.java | 124 ++++++++++++++++++ .../demo/EnableDemoConfigBindings.java | 36 +++++ .../javacourse/demo/PropertySourcesUtils.java | 91 +++++++++++++ .../src/main/resources/application.properties | 8 ++ .../javacourse/demo/DemoApplicationTests.java | 37 +++++- 7 files changed, 319 insertions(+), 3 deletions(-) create mode 100644 04fx/demo/src/main/java/io/kimmking/javacourse/demo/DemoConfig.java create mode 100644 04fx/demo/src/main/java/io/kimmking/javacourse/demo/DemoConfigBindingsRegistrar.java create mode 100644 04fx/demo/src/main/java/io/kimmking/javacourse/demo/EnableDemoConfigBindings.java create mode 100644 04fx/demo/src/main/java/io/kimmking/javacourse/demo/PropertySourcesUtils.java diff --git a/04fx/demo/pom.xml b/04fx/demo/pom.xml index cb3b13d3..943bcbe0 100644 --- a/04fx/demo/pom.xml +++ b/04fx/demo/pom.xml @@ -27,7 +27,14 @@ spring-boot-starter-test test - + + + org.projectlombok + lombok + 1.18.22 + compile + + diff --git a/04fx/demo/src/main/java/io/kimmking/javacourse/demo/DemoConfig.java b/04fx/demo/src/main/java/io/kimmking/javacourse/demo/DemoConfig.java new file mode 100644 index 00000000..fa484b74 --- /dev/null +++ b/04fx/demo/src/main/java/io/kimmking/javacourse/demo/DemoConfig.java @@ -0,0 +1,17 @@ +package io.kimmking.javacourse.demo; + +import lombok.Data; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2022/11/29 16:05 + */ +@Data +public class DemoConfig { + + private String demoName; + private String demoDesc; + +} diff --git a/04fx/demo/src/main/java/io/kimmking/javacourse/demo/DemoConfigBindingsRegistrar.java b/04fx/demo/src/main/java/io/kimmking/javacourse/demo/DemoConfigBindingsRegistrar.java new file mode 100644 index 00000000..1107dc03 --- /dev/null +++ b/04fx/demo/src/main/java/io/kimmking/javacourse/demo/DemoConfigBindingsRegistrar.java @@ -0,0 +1,124 @@ +package io.kimmking.javacourse.demo; + +import org.springframework.beans.MutablePropertyValues; +import org.springframework.beans.factory.support.*; +import org.springframework.context.EnvironmentAware; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.Environment; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import java.util.*; + +import static io.kimmking.javacourse.demo.PropertySourcesUtils.getSubProperties; +import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition; + +/** + * This class is cloned and modified from https://github.com/apache/dubbo/blob/58d1259cb9d89528594e43db5d8667179005dcfc/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/context/annotation/DubboConfigBindingsRegistrar.java. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2022/11/29 16:09 + */ +public class DemoConfigBindingsRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware { + + private ConfigurableEnvironment environment; + + @Override + public void setEnvironment(Environment _environment) { + this.environment = (ConfigurableEnvironment) _environment; + } + + @Override + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) { + registerBeanDefinitions(importingClassMetadata, registry); + } + + @Override + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { + + AnnotationAttributes attributes = AnnotationAttributes.fromMap( + importingClassMetadata.getAnnotationAttributes(EnableDemoConfigBindings.class.getName())); + String prefix = environment.resolvePlaceholders(attributes.getString("prefix")); + Class configClass = attributes.getClass("type"); + boolean multiple = attributes.getBoolean("multiple"); + registerDubboConfigBeans(prefix, configClass, multiple, registry); + } + + private void registerDubboConfigBeans(String prefix, + Class configClass, + boolean multiple, + BeanDefinitionRegistry registry) { + Map properties = getSubProperties(environment.getPropertySources(), prefix); + if (CollectionUtils.isEmpty(properties)) { + System.out.println("There is no property for binding to demo config class [" + configClass.getName() + + "] within prefix [" + prefix + "]"); + return; + } + Set beanNames = multiple ? resolveMultipleBeanNames(properties) : + Collections.singleton(resolveSingleBeanName(properties, configClass, registry)); + Map> groupProperties = getGroupProperties(properties, beanNames); + for (String beanName : beanNames) { + registerDemoConfigBean(beanName, configClass, registry, groupProperties.get(beanName)); + //registerDubboConfigBindingBeanPostProcessor(prefix, beanName, multiple, registry); + } + //registerDubboConfigBeanCustomizers(registry); + } + + private Map> getGroupProperties(Map properties, Set beanNames) { + Map> map = new HashMap<>(); + for (String propertyName : properties.keySet()) { + int index = propertyName.indexOf("."); + if (index > 0) { + String beanName = propertyName.substring(0, index); + String beanPropertyName = propertyName.substring(index + 1); + if (beanNames.contains(beanName)) { + Map group = map.get(beanName); + if (group == null) { + group = new HashMap<>(); + map.put(beanName, group); + } + group.put(beanPropertyName, properties.get(propertyName)); + } + } + } + return map; + } + + private void registerDemoConfigBean(String beanName, Class configClass, + BeanDefinitionRegistry registry, Map properties) { + BeanDefinitionBuilder builder = rootBeanDefinition(configClass); + AbstractBeanDefinition beanDefinition = builder.getBeanDefinition(); + // Convert Map to MutablePropertyValues + MutablePropertyValues propertyValues = new MutablePropertyValues(properties); + beanDefinition.setPropertyValues(propertyValues); + registry.registerBeanDefinition(beanName, beanDefinition); + System.out.println("The demo config bean definition [name : " + beanName + ", class : " + configClass.getName() + + "] has been registered."); + } + + private Set resolveMultipleBeanNames(Map properties) { + Set beanNames = new LinkedHashSet(); + for (String propertyName : properties.keySet()) { + int index = propertyName.indexOf("."); + if (index > 0) { + String beanName = propertyName.substring(0, index); + beanNames.add(beanName); + } + } + return beanNames; + } + + private String resolveSingleBeanName(Map properties, Class configClass, + BeanDefinitionRegistry registry) { + String beanName = (String) properties.get("demoName"); + if (!StringUtils.hasText(beanName)) { + BeanDefinitionBuilder builder = rootBeanDefinition(configClass); + beanName = BeanDefinitionReaderUtils.generateBeanName(builder.getRawBeanDefinition(), registry); + } + return beanName; + } + +} diff --git a/04fx/demo/src/main/java/io/kimmking/javacourse/demo/EnableDemoConfigBindings.java b/04fx/demo/src/main/java/io/kimmking/javacourse/demo/EnableDemoConfigBindings.java new file mode 100644 index 00000000..3a002944 --- /dev/null +++ b/04fx/demo/src/main/java/io/kimmking/javacourse/demo/EnableDemoConfigBindings.java @@ -0,0 +1,36 @@ +package io.kimmking.javacourse.demo; + +import org.springframework.context.annotation.Import; + +import java.lang.annotation.*; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2022/11/29 16:10 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Import(DemoConfigBindingsRegistrar.class) +public @interface EnableDemoConfigBindings { + /** + * The name prefix of the properties that are valid to bind. + * + * @return the name prefix of the properties to bind + */ + String prefix(); + + /** + * @return The binding type. + */ + Class type(); + + /** + * It indicates whether {@link #prefix()} binding to multiple Spring Beans. + * + * @return the default value is true + */ + boolean multiple() default true; +} diff --git a/04fx/demo/src/main/java/io/kimmking/javacourse/demo/PropertySourcesUtils.java b/04fx/demo/src/main/java/io/kimmking/javacourse/demo/PropertySourcesUtils.java new file mode 100644 index 00000000..f436313e --- /dev/null +++ b/04fx/demo/src/main/java/io/kimmking/javacourse/demo/PropertySourcesUtils.java @@ -0,0 +1,91 @@ +package io.kimmking.javacourse.demo; + + +import org.springframework.core.env.*; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Properties; + +/** + * {@link PropertySources} Utilities + *

+ * The source code is cloned from https://github.com/alibaba/spring-context-support/blob/1.0.2/src/main/java/com/alibaba/spring/util/PropertySourcesUtils.java + * + * @since 2.6.6 + */ +public abstract class PropertySourcesUtils { + + /** + * Get Sub {@link Properties} + * + * @param propertySources {@link PropertySource} Iterable + * @param prefix the prefix of property name + * @return Map + * @see Properties + */ + public static Map getSubProperties(Iterable> propertySources, String prefix) { + + // Non-Extension AbstractEnvironment + AbstractEnvironment environment = new AbstractEnvironment() { + }; + + MutablePropertySources mutablePropertySources = environment.getPropertySources(); + + for (PropertySource source : propertySources) { + mutablePropertySources.addLast(source); + } + + return getSubProperties(environment, prefix); + + } + + /** + * Get Sub {@link Properties} + * + * @param environment {@link ConfigurableEnvironment} + * @param prefix the prefix of property name + * @return Map + * @see Properties + */ + public static Map getSubProperties(ConfigurableEnvironment environment, String prefix) { + + Map subProperties = new LinkedHashMap(); + + MutablePropertySources propertySources = environment.getPropertySources(); + + String normalizedPrefix = normalizePrefix(prefix); + + for (PropertySource source : propertySources) { + if (source instanceof EnumerablePropertySource) { + for (String name : ((EnumerablePropertySource) source).getPropertyNames()) { + if (!subProperties.containsKey(name) && name.startsWith(normalizedPrefix)) { + String subName = name.substring(normalizedPrefix.length()); + if (!subProperties.containsKey(subName)) { // take first one + Object value = source.getProperty(name); + if (value instanceof String) { + // Resolve placeholder + value = environment.resolvePlaceholders((String) value); + } + subProperties.put(subName, value); + } + } + } + } + } + + return Collections.unmodifiableMap(subProperties); + + } + + /** + * Normalize the prefix + * + * @param prefix the prefix + * @return the prefix + */ + public static String normalizePrefix(String prefix) { + return prefix.endsWith(".") ? prefix : prefix + "."; + } +} diff --git a/04fx/demo/src/main/resources/application.properties b/04fx/demo/src/main/resources/application.properties index 8b137891..f7a98d9a 100644 --- a/04fx/demo/src/main/resources/application.properties +++ b/04fx/demo/src/main/resources/application.properties @@ -1 +1,9 @@ +demo.config.a1.demoName = d1 +demo.config.a1.demoDesc = demo1 + +demo.config.a2.demoName = d2 +demo.config.a2.demoDesc = demo2 + +demo.config.a3.demoName = d3 +demo.config.a3.demoDesc = demo3 \ No newline at end of file diff --git a/04fx/demo/src/test/java/io/kimmking/javacourse/demo/DemoApplicationTests.java b/04fx/demo/src/test/java/io/kimmking/javacourse/demo/DemoApplicationTests.java index 23f5cda4..a2c023ad 100644 --- a/04fx/demo/src/test/java/io/kimmking/javacourse/demo/DemoApplicationTests.java +++ b/04fx/demo/src/test/java/io/kimmking/javacourse/demo/DemoApplicationTests.java @@ -2,12 +2,45 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.PropertySource; + +import java.util.Arrays; + +import static org.junit.jupiter.api.Assertions.*; @SpringBootTest class DemoApplicationTests { - @Test - void contextLoads() { + void testDemoConfig() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(TestConfig.class); + context.refresh(); + + System.out.println(Arrays.toString(context.getBeanNamesForType(DemoConfig.class))); + assertEquals(3, context.getBeansOfType(DemoConfig.class).size()); + + DemoConfig demoConfig = context.getBean("a1", DemoConfig.class); + System.out.println("a1=" + demoConfig.toString()); + assertEquals("d1", demoConfig.getDemoName()); + assertEquals("demo1", demoConfig.getDemoDesc()); + + demoConfig = context.getBean("a2", DemoConfig.class); + System.out.println("a2=" + demoConfig.toString()); + assertEquals("d2", demoConfig.getDemoName()); + assertEquals("demo2", demoConfig.getDemoDesc()); + + demoConfig = context.getBean("a3", DemoConfig.class); + System.out.println("a3=" + demoConfig.toString()); + assertEquals("d3", demoConfig.getDemoName()); + assertEquals("demo3", demoConfig.getDemoDesc()); + + } + + @EnableDemoConfigBindings(prefix = "demo.config", type = DemoConfig.class) + @PropertySource("application.properties") + private static class TestConfig { + } } From e84d39fef2dc2ad63a0f17790027be1d1558dc08 Mon Sep 17 00:00:00 2001 From: kimmking Date: Fri, 23 Dec 2022 08:31:51 +0800 Subject: [PATCH 02/13] add registrar --- 04fx/demo/src/main/java/A.java | 8 +++ .../kimmking/javacourse/demo/ConsConfig.java | 18 +++++ .../demo/ConsConfigAutoConfiguration.java | 71 +++++++++++++++++++ .../kimmking/javacourse/demo/ConsConfigs.java | 20 ++++++ .../javacourse/demo/DemoApplication.java | 61 +++++++++++++++- .../demo/DemoConfigBindingsRegistrar.java | 3 + .../javacourse/demo/DemoApplicationTests.java | 17 +++-- 7 files changed, 191 insertions(+), 7 deletions(-) create mode 100644 04fx/demo/src/main/java/A.java create mode 100644 04fx/demo/src/main/java/io/kimmking/javacourse/demo/ConsConfig.java create mode 100644 04fx/demo/src/main/java/io/kimmking/javacourse/demo/ConsConfigAutoConfiguration.java create mode 100644 04fx/demo/src/main/java/io/kimmking/javacourse/demo/ConsConfigs.java diff --git a/04fx/demo/src/main/java/A.java b/04fx/demo/src/main/java/A.java new file mode 100644 index 00000000..117e905c --- /dev/null +++ b/04fx/demo/src/main/java/A.java @@ -0,0 +1,8 @@ +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2022/12/16 10:55 + */ +public class A { +} diff --git a/04fx/demo/src/main/java/io/kimmking/javacourse/demo/ConsConfig.java b/04fx/demo/src/main/java/io/kimmking/javacourse/demo/ConsConfig.java new file mode 100644 index 00000000..c506234e --- /dev/null +++ b/04fx/demo/src/main/java/io/kimmking/javacourse/demo/ConsConfig.java @@ -0,0 +1,18 @@ +package io.kimmking.javacourse.demo; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2022/11/29 16:05 + */ +@Data +public class ConsConfig { + + private String demoName; + private String demoDesc; + +} diff --git a/04fx/demo/src/main/java/io/kimmking/javacourse/demo/ConsConfigAutoConfiguration.java b/04fx/demo/src/main/java/io/kimmking/javacourse/demo/ConsConfigAutoConfiguration.java new file mode 100644 index 00000000..4461b3a8 --- /dev/null +++ b/04fx/demo/src/main/java/io/kimmking/javacourse/demo/ConsConfigAutoConfiguration.java @@ -0,0 +1,71 @@ +package io.kimmking.javacourse.demo; + +import org.springframework.beans.factory.config.ConstructorArgumentValues; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.context.EnvironmentAware; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.env.Environment; +import org.springframework.core.type.AnnotationMetadata; + + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2022/12/22 16:43 + */ + +@Configuration +//@EnableConfigurationProperties(ConsConfigs.class) +@Import(ConsConfigAutoConfiguration.ConsRegistrar.class) +public class ConsConfigAutoConfiguration { + public static class ConsRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware { + + Environment environment; +// ApplicationContext applicationContext; +// @Override +// public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { +// this.applicationContext = applicationContext; +// } + + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + + + @Override + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { + //ImportBeanDefinitionRegistrar.super.registerBeanDefinitions(importingClassMetadata, registry); + + RootBeanDefinition def = new RootBeanDefinition(); + def.setBeanClass(ConsConfig.class); + registry.registerBeanDefinition("cons", def); + + RootBeanDefinition definition = new RootBeanDefinition(); + definition.setBeanClass(ConsConfigs.class); + definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); + definition.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); + //ConstructorArgumentValues argumentValues = new ConstructorArgumentValues(); + + //ConsConfig config= applicationContext.getBean(ConsConfig.class); +// String name = ConsConfig.class.getCanonicalName(); +// boolean exist = registry.containsBeanDefinition(name); +// if(exist) { +// argumentValues.addGenericArgumentValue(registry.getBeanDefinition(name)); +// } else { +// ConsConfig cf = new ConsConfig(); +// cf.setDemoName("defaultName1"); +// cf.setDemoDesc("defaultDesc1"); +// argumentValues.addGenericArgumentValue(cf); +// } + //definition.setConstructorArgumentValues(argumentValues); + registry.registerBeanDefinition("conss", definition); + } + } +} diff --git a/04fx/demo/src/main/java/io/kimmking/javacourse/demo/ConsConfigs.java b/04fx/demo/src/main/java/io/kimmking/javacourse/demo/ConsConfigs.java new file mode 100644 index 00000000..8ef243e6 --- /dev/null +++ b/04fx/demo/src/main/java/io/kimmking/javacourse/demo/ConsConfigs.java @@ -0,0 +1,20 @@ +package io.kimmking.javacourse.demo; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.util.List; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2022/12/22 16:47 + */ +@Data +@AllArgsConstructor +public class ConsConfigs { + + private List config; + +} diff --git a/04fx/demo/src/main/java/io/kimmking/javacourse/demo/DemoApplication.java b/04fx/demo/src/main/java/io/kimmking/javacourse/demo/DemoApplication.java index 70a3c8d7..401643fc 100644 --- a/04fx/demo/src/main/java/io/kimmking/javacourse/demo/DemoApplication.java +++ b/04fx/demo/src/main/java/io/kimmking/javacourse/demo/DemoApplication.java @@ -1,13 +1,72 @@ package io.kimmking.javacourse.demo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.context.ApplicationContext; +import org.springframework.context.EnvironmentAware; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.Environment; + +import javax.annotation.Resource; @SpringBootApplication -public class DemoApplication { +public class DemoApplication implements EnvironmentAware, ApplicationRunner { + + Environment environment; + + @Resource(name="a1") + DemoConfig a1; public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Autowired + ApplicationContext context; + + @Bean + //@ConditionalOnMissingBean + public ConsConfig defaultConsConfig() { + ConsConfig cf = new ConsConfig(); + cf.setDemoName("defaultName"); + cf.setDemoDesc("defaultDesc"); + return cf; + } + + @Override + public void run(ApplicationArguments args) throws Exception { + System.out.println(environment.getProperty("a")); + System.out.println(a1.getDemoName()); + + DemoConfig a4 = (DemoConfig) context.getBean("a4"); + System.out.println(a4.getDemoName()); + + System.out.println(context.getBean(ConsConfigs.class)); + + } + + @EnableDemoConfigBindings(prefix = "demo.config", type = DemoConfig.class) + @PropertySource("application.properties") + public static class TestDemoConfig { + + } + + @Bean("a4") + @ConditionalOnClass(name="A") + DemoConfig createA4() { + DemoConfig config = new DemoConfig(); + config.setDemoName("a4"); + config.setDemoDesc("this is a4"); + return config; + } } diff --git a/04fx/demo/src/main/java/io/kimmking/javacourse/demo/DemoConfigBindingsRegistrar.java b/04fx/demo/src/main/java/io/kimmking/javacourse/demo/DemoConfigBindingsRegistrar.java index 1107dc03..2d201027 100644 --- a/04fx/demo/src/main/java/io/kimmking/javacourse/demo/DemoConfigBindingsRegistrar.java +++ b/04fx/demo/src/main/java/io/kimmking/javacourse/demo/DemoConfigBindingsRegistrar.java @@ -61,6 +61,9 @@ private void registerDubboConfigBeans(String prefix, Collections.singleton(resolveSingleBeanName(properties, configClass, registry)); Map> groupProperties = getGroupProperties(properties, beanNames); for (String beanName : beanNames) { + + System.out.println(" ==> registerDemoConfigBean:" + beanName); + registerDemoConfigBean(beanName, configClass, registry, groupProperties.get(beanName)); //registerDubboConfigBindingBeanPostProcessor(prefix, beanName, multiple, registry); } diff --git a/04fx/demo/src/test/java/io/kimmking/javacourse/demo/DemoApplicationTests.java b/04fx/demo/src/test/java/io/kimmking/javacourse/demo/DemoApplicationTests.java index a2c023ad..5101ce0e 100644 --- a/04fx/demo/src/test/java/io/kimmking/javacourse/demo/DemoApplicationTests.java +++ b/04fx/demo/src/test/java/io/kimmking/javacourse/demo/DemoApplicationTests.java @@ -14,9 +14,11 @@ class DemoApplicationTests { @Test void testDemoConfig() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); - context.register(TestConfig.class); + context.register(DemoApplication.TestDemoConfig.class); context.refresh(); + + System.out.println(Arrays.toString(context.getBeanNamesForType(DemoConfig.class))); assertEquals(3, context.getBeansOfType(DemoConfig.class).size()); @@ -35,12 +37,15 @@ void testDemoConfig() { assertEquals("d3", demoConfig.getDemoName()); assertEquals("demo3", demoConfig.getDemoDesc()); - } - - @EnableDemoConfigBindings(prefix = "demo.config", type = DemoConfig.class) - @PropertySource("application.properties") - private static class TestConfig { +// context.refresh(); +// System.out.println(Arrays.toString(context.getBeanNamesForType(DemoConfig.class))); } +// @EnableDemoConfigBindings(prefix = "demo.config", type = DemoConfig.class) +// @PropertySource("application.properties") +// private static class TestConfig { +// +// } + } From 6ebd3b2ef9a7514478e0aa7720d976fe4405019b Mon Sep 17 00:00:00 2001 From: kimmking Date: Mon, 1 Jan 2024 22:05:12 +0800 Subject: [PATCH 03/13] complete registy for zk --- 01jvm/README.md | 2 +- 01jvm/jvm/HelloClassLoader.java | 4 +- 01jvm/lib/Hello.java | 23 ++-- 01jvm/out/production/01jvm/README.md | 2 +- 06db/shardingsphere/init.sql | 6 + 07rpc/rpc01/pom.xml | 2 +- 07rpc/rpc01/rpcfx-core/pom.xml | 7 +- .../rpcfx/annotation/RpcfxReference.java | 17 +++ .../rpcfx/annotation/RpcfxService.java | 18 +++ .../java/io/kimmking/rpcfx/client/Rpcfx.java | 98 +++++++++++++++-- .../kimmking/rpcfx/server/RpcfxInvoker.java | 4 + .../demo/consumer/RpcfxClientApplication.java | 44 ++++++-- .../rpcfx/demo/provider/DemoResolver.java | 2 + .../demo/provider/RpcfxServerApplication.java | 103 +++++++++++------- .../rpcfx/demo/provider/UserServiceImpl.java | 10 +- .../src/main/resources/application.yml | 11 +- 08cache/ha/conf/sentinel0.conf | 13 +-- 08cache/ha/conf/sentinel1.conf | 12 +- 18 files changed, 282 insertions(+), 96 deletions(-) create mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/annotation/RpcfxReference.java create mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/annotation/RpcfxService.java diff --git a/01jvm/README.md b/01jvm/README.md index fc2d2818..51c885e3 100644 --- a/01jvm/README.md +++ b/01jvm/README.md @@ -31,7 +31,7 @@ 2. 编译代码, 执行命令: `javac -g HelloNum.java` 3. 查看反编译的代码。 - 3.1 可以安装并使用idea的jclasslib插件, 选中 [HelloNum.java](./HelloNum.java) 文件, 选择 `View --> Show Bytecode With jclasslib` 即可。 - - 3.2 或者直接通过命令行工具 javap, 执行命令: `javap -v HelloNum.class` + - 3.2 或者直接通过命令行工具 javap, 执行命令: `javap -c -v -p -l HelloNum.class` 4. 分析相关的字节码。【此步骤需要各位同学自己进行分析】 diff --git a/01jvm/jvm/HelloClassLoader.java b/01jvm/jvm/HelloClassLoader.java index ab080620..51d906f1 100644 --- a/01jvm/jvm/HelloClassLoader.java +++ b/01jvm/jvm/HelloClassLoader.java @@ -6,12 +6,12 @@ public class HelloClassLoader extends ClassLoader { public static void main(String[] args) throws Exception { - new HelloClassLoader().findClass("jvm.lib.Hello").newInstance(); + new HelloClassLoader().findClass("lib.Hello").newInstance(); } @Override protected Class findClass(String name) throws ClassNotFoundException { - String helloBase64 = "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAAg8Y2xpbml0PgEAClNvdXJjZUZpbGUBAApIZWxsby5qYXZhDAAHAAgHABYMABcAGAEAGEhlbGxvIENsYXNzIEluaXRpYWxpemVkIQcAGQwAGgAbAQAJanZtL0hlbGxvAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAhAAUABgAAAAAAAgABAAcACAABAAkAAAAdAAEAAQAAAAUqtwABsQAAAAEACgAAAAYAAQAAAAMACAALAAgAAQAJAAAAJQACAAAAAAAJsgACEgO2AASxAAAAAQAKAAAACgACAAAABQAIAAYAAQAMAAAAAgAN"; + String helloBase64 = "yv66vgAAADQAHwoABwAQCQARABIIABMKABQAFQgAFgcAFwcAGAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAAVoZWxsbwEACDxjbGluaXQ+AQAKU291cmNlRmlsZQEACkhlbGxvLmphdmEMAAgACQcAGQwAGgAbAQAdSGVsbG8gY2xhc3Mgc2F5IGhlbGxvIG1ldGhvZC4HABwMAB0AHgEAGEhlbGxvIENsYXNzIEluaXRpYWxpemVkIQEACWxpYi9IZWxsbwEAEGphdmEvbGFuZy9PYmplY3QBABBqYXZhL2xhbmcvU3lzdGVtAQADb3V0AQAVTGphdmEvaW8vUHJpbnRTdHJlYW07AQATamF2YS9pby9QcmludFN0cmVhbQEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYAIQAGAAcAAAAAAAMAAQAIAAkAAQAKAAAAHQABAAEAAAAFKrcAAbEAAAABAAsAAAAGAAEAAAADAAEADAAJAAEACgAAACUAAgABAAAACbIAAhIDtgAEsQAAAAEACwAAAAoAAgAAAAgACAAJAAgADQAJAAEACgAAACUAAgAAAAAACbIAAhIFtgAEsQAAAAEACwAAAAoAAgAAAAUACAAGAAEADgAAAAIADw==+AQAKU291cmNlRmlsZQEACkhlbGxvLmphdmEMAAgACQcAGQwAGgAbAQAdSGVsbG8gY2xhc3Mgc2F5IGhlbGxvIG1ldGhvZC4HABwMAB0AHgEAGEhlbGxvIENsYXNzIEluaXRpYWxpemVkIQEACWxpYi9IZWxsbwEAEGphdmEvbGFuZy9PYmplY3QBABBqYXZhL2xhbmcvU3lzdGVtAQADb3V0AQAVTGphdmEvaW8vUHJpbnRTdHJlYW07AQATamF2YS9pby9QcmludFN0cmVhbQEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYAIQAGAAcAAAAAAAMAAQAIAAkAAQAKAAAAHQABAAEAAAAFKrcAAbEAAAABAAsAAAAGAAEAAAADAAEADAAJAAEACgAAACUAAgABAAAACbIAAhIDtgAEsQAAAAEACwAAAAoAAgAAAAgACAAJAAgADQAJAAEACgAAACUAAgAAAAAACbIAAhIFtgAEsQAAAAEACwAAAAoAAgAAAAUACAAGAAEADgAAAAIADw=="; byte[] bytes = decode(helloBase64); return defineClass(name,bytes,0,bytes.length); } diff --git a/01jvm/lib/Hello.java b/01jvm/lib/Hello.java index 79a24651..5d951d97 100644 --- a/01jvm/lib/Hello.java +++ b/01jvm/lib/Hello.java @@ -1,11 +1,12 @@ -//package lib; -// -//public class Hello { -// static { -// System.out.println("Hello Class Initialized!"); -// } -// public void hello() { -// System.out.println("Hello class say hello method."); -// } -// -//} \ No newline at end of file +package lib; + +public class Hello { + static { + System.out.println("Hello Class Initialized!"); + } + public void hello() { + System.out.println("Hello class say hello method."); + System.gc(); // JMX MBean server + } + +} \ No newline at end of file diff --git a/01jvm/out/production/01jvm/README.md b/01jvm/out/production/01jvm/README.md index fc2d2818..51c885e3 100644 --- a/01jvm/out/production/01jvm/README.md +++ b/01jvm/out/production/01jvm/README.md @@ -31,7 +31,7 @@ 2. 编译代码, 执行命令: `javac -g HelloNum.java` 3. 查看反编译的代码。 - 3.1 可以安装并使用idea的jclasslib插件, 选中 [HelloNum.java](./HelloNum.java) 文件, 选择 `View --> Show Bytecode With jclasslib` 即可。 - - 3.2 或者直接通过命令行工具 javap, 执行命令: `javap -v HelloNum.class` + - 3.2 或者直接通过命令行工具 javap, 执行命令: `javap -c -v -p -l HelloNum.class` 4. 分析相关的字节码。【此步骤需要各位同学自己进行分析】 diff --git a/06db/shardingsphere/init.sql b/06db/shardingsphere/init.sql index 7c1adb0c..9b930268 100644 --- a/06db/shardingsphere/init.sql +++ b/06db/shardingsphere/init.sql @@ -29,3 +29,9 @@ CREATE TABLE IF NOT EXISTS demo_ds_0.t_order_item_1 (order_item_id BIGINT NOT NU CREATE TABLE IF NOT EXISTS demo_ds_1.t_order_item_0 (order_item_id BIGINT NOT NULL AUTO_INCREMENT, order_id BIGINT NOT NULL, user_id INT NOT NULL, status VARCHAR(50), PRIMARY KEY (order_item_id)); CREATE TABLE IF NOT EXISTS demo_ds_1.t_order_item_1 (order_item_id BIGINT NOT NULL AUTO_INCREMENT, order_id BIGINT NOT NULL, user_id INT NOT NULL, status VARCHAR(50), PRIMARY KEY (order_item_id)); + + + + +# CREATE TABLE t_order (order_id BIGINT NOT NULL AUTO_INCREMENT, user_id INT NOT NULL, status VARCHAR(50), PRIMARY KEY (order_id)); +# CREATE TABLE t_order_item (order_item_id BIGINT NOT NULL AUTO_INCREMENT, order_id BIGINT NOT NULL, user_id INT NOT NULL, status VARCHAR(50), PRIMARY KEY (order_item_id)); diff --git a/07rpc/rpc01/pom.xml b/07rpc/rpc01/pom.xml index 62b29db1..8c924dcf 100644 --- a/07rpc/rpc01/pom.xml +++ b/07rpc/rpc01/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.0.9.RELEASE + 2.7.3 io.kimmking diff --git a/07rpc/rpc01/rpcfx-core/pom.xml b/07rpc/rpc01/rpcfx-core/pom.xml index 4570a59d..d8b62724 100644 --- a/07rpc/rpc01/rpcfx-core/pom.xml +++ b/07rpc/rpc01/rpcfx-core/pom.xml @@ -70,6 +70,11 @@ - + + org.apache.curator + curator-recipes + 5.1.0 + + diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/annotation/RpcfxReference.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/annotation/RpcfxReference.java new file mode 100644 index 00000000..8fd1f4af --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/annotation/RpcfxReference.java @@ -0,0 +1,17 @@ +package io.kimmking.rpcfx.annotation; + +import java.lang.annotation.*; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2024/1/1 20:00 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Inherited +public @interface RpcfxReference { + +} \ No newline at end of file diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/annotation/RpcfxService.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/annotation/RpcfxService.java new file mode 100644 index 00000000..9db3c7e3 --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/annotation/RpcfxService.java @@ -0,0 +1,18 @@ +package io.kimmking.rpcfx.annotation; + +import java.lang.annotation.*; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2024/1/1 20:00 + */ + +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Inherited +public @interface RpcfxService { + +} \ No newline at end of file diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/Rpcfx.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/Rpcfx.java index 5d1ae517..375e6813 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/Rpcfx.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/Rpcfx.java @@ -8,11 +8,20 @@ import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; +import org.apache.curator.RetryPolicy; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.framework.recipes.cache.TreeCache; +import org.apache.curator.framework.recipes.cache.TreeCacheEvent; +import org.apache.curator.framework.recipes.cache.TreeCacheListener; +import org.apache.curator.retry.ExponentialBackoffRetry; +import org.apache.zookeeper.CreateMode; import java.io.IOException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.net.InetAddress; import java.util.ArrayList; import java.util.List; @@ -26,24 +35,72 @@ public static T createFromRegistry(final Class serviceClass, fin // 加filte之一 - // curator Provider list from zk + String service = serviceClass.getCanonicalName();//"io.kimking.rpcfx.demo.api.UserService"; + System.out.println("====> "+service); List invokers = new ArrayList<>(); + + RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); + CuratorFramework client = CuratorFrameworkFactory.builder().connectString("localhost:2181").namespace("rpcfx").retryPolicy(retryPolicy).build(); + client.start(); + + try { +// ServiceProviderDesc userServiceSesc = ServiceProviderDesc.builder() +// .host(InetAddress.getLocalHost().getHostAddress()) +// .port(8082).serviceClass(service).build(); + // String userServiceSescJson = JSON.toJSONString(userServiceSesc); + + + if ( null == client.checkExists().forPath("/" + service)) { + return null; + } + + fetchInvokers(client, service, invokers); + + final TreeCache treeCache = TreeCache.newBuilder(client, "/" + service).setCacheData(true).setMaxDepth(2).build(); + treeCache.getListenable().addListener(new TreeCacheListener() { + public void childEvent(CuratorFramework curatorFramework, TreeCacheEvent treeCacheEvent) throws Exception { + System.out.println("treeCacheEvent: "+treeCacheEvent); + fetchInvokers(client, service, invokers); + } + }); + treeCache.start(); + + } catch (Exception ex) { + ex.printStackTrace(); + } + +// +// +// // register service +// // xxx "io.kimmking.rpcfx.demo.api.UserService" +// + + + // curator Provider list from zk + // 1. 简单:从zk拿到服务提供的列表 // 2. 挑战:监听zk的临时节点,根据事件更新这个list(注意,需要做个全局map保持每个服务的提供者List) - List urls = router.route(invokers); + return (T) create(serviceClass, invokers, router, loadBalance, filter); - String url = loadBalance.select(urls); // router, loadbalance + } - return (T) create(serviceClass, url, filter); - } - public static T create(final Class serviceClass, final String url, Filter... filters) { + private static void fetchInvokers(CuratorFramework client, String service, List invokers) throws Exception { + List services = client.getChildren().forPath("/" + service); + invokers.clear(); + for (String svc : services) { + System.out.println(svc); + String url = svc.replace("_", ":"); + invokers.add("http://" + url); + } + } + private static Object create(Class serviceClass, List invokers, Router router, LoadBalancer loadBalance, Filter... filters) { // 0. 替换动态代理 -> 字节码生成 - return (T) Proxy.newProxyInstance(Rpcfx.class.getClassLoader(), new Class[]{serviceClass}, new RpcfxInvocationHandler(serviceClass, url, filters)); - + return (T) Proxy.newProxyInstance(Rpcfx.class.getClassLoader(), new Class[]{serviceClass}, + new RpcfxInvocationHandler(serviceClass, invokers, router,loadBalance, filters)); } public static class RpcfxInvocationHandler implements InvocationHandler { @@ -51,12 +108,16 @@ public static class RpcfxInvocationHandler implements InvocationHandler { public static final MediaType JSONTYPE = MediaType.get("application/json; charset=utf-8"); private final Class serviceClass; - private final String url; + private final List invokers; + private final Router router; + private final LoadBalancer loadBalance; private final Filter[] filters; - public RpcfxInvocationHandler(Class serviceClass, String url, Filter... filters) { + public RpcfxInvocationHandler(Class serviceClass, List invokers, Router router, LoadBalancer loadBalance, Filter... filters) { this.serviceClass = serviceClass; - this.url = url; + this.invokers = invokers; + this.router = router; + this.loadBalance = loadBalance; this.filters = filters; } @@ -67,6 +128,17 @@ public RpcfxInvocationHandler(Class serviceClass, String url, Filter... f @Override public Object invoke(Object proxy, Method method, Object[] params) throws Throwable { + List urls = router.route(invokers); + System.out.println("router.route => "); + urls.forEach(System.out::println); + String url = loadBalance.select(urls); // router, loadbalance + System.out.println("loadBalance.select => "); + System.out.println("final => " + url); + + if (url == null) { + throw new RuntimeException("No available providers from registry center."); + } + // 加filter地方之二 // mock == true, new Student("hubao"); @@ -94,13 +166,15 @@ public Object invoke(Object proxy, Method method, Object[] params) throws Throwa return JSON.parse(response.getResult().toString()); } + OkHttpClient client = new OkHttpClient(); + private RpcfxResponse post(RpcfxRequest req, String url) throws IOException { String reqJson = JSON.toJSONString(req); System.out.println("req json: "+reqJson); // 1.可以复用client // 2.尝试使用httpclient或者netty client - OkHttpClient client = new OkHttpClient(); + final Request request = new Request.Builder() .url(url) .post(RequestBody.create(JSONTYPE, reqJson)) diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/server/RpcfxInvoker.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/server/RpcfxInvoker.java index a6f77dac..faeb1c85 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/server/RpcfxInvoker.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/server/RpcfxInvoker.java @@ -5,13 +5,17 @@ import io.kimmking.rpcfx.api.RpcfxRequest; import io.kimmking.rpcfx.api.RpcfxResolver; import io.kimmking.rpcfx.api.RpcfxResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; +@Component public class RpcfxInvoker { + @Autowired private RpcfxResolver resolver; public RpcfxInvoker(RpcfxResolver resolver){ diff --git a/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/RpcfxClientApplication.java b/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/RpcfxClientApplication.java index 760e4cdc..d72425f4 100644 --- a/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/RpcfxClientApplication.java +++ b/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/RpcfxClientApplication.java @@ -1,22 +1,28 @@ package io.kimmking.rpcfx.demo.consumer; -import io.kimmking.rpcfx.api.Filter; -import io.kimmking.rpcfx.api.LoadBalancer; -import io.kimmking.rpcfx.api.Router; -import io.kimmking.rpcfx.api.RpcfxRequest; +import io.kimmking.rpcfx.api.*; import io.kimmking.rpcfx.client.Rpcfx; import io.kimmking.rpcfx.demo.api.Order; import io.kimmking.rpcfx.demo.api.OrderService; import io.kimmking.rpcfx.demo.api.User; import io.kimmking.rpcfx.demo.api.UserService; import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.ComponentScans; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; import java.util.List; import java.util.Random; +import java.util.concurrent.atomic.AtomicInteger; @SpringBootApplication -public class RpcfxClientApplication { +@RestController +//@ComponentScan("io.kimmking.rpcfx") +public class RpcfxClientApplication implements CommandLineRunner { // 二方库 // 三方库 lib @@ -36,12 +42,25 @@ public static void main(String[] args) { // Order order = orderService.findOrderById(1992129); // System.out.println(String.format("find order name=%s, amount=%f",order.getName(),order.getAmount())); +// UserService userService2 = Rpcfx.createFromRegistry(UserService.class, "localhost:2181", new TagRouter(), new RandomLoadBalancer(), new CuicuiFilter()); +// User user = userService2.findById(1); +// System.out.println(user.getName()); - UserService userService2 = Rpcfx.createFromRegistry(UserService.class, "localhost:2181", new TagRouter(), new RandomLoadBalancer(), new CuicuiFilter()); - User user = userService2.findById(1); - System.out.println(user); + SpringApplication.run(RpcfxClientApplication.class, args); + } + + UserService userService2;// = Rpcfx.createFromRegistry(UserService.class, "localhost:2181", new TagRouter(), new RandomLoadBalancer(), new CuicuiFilter()); -// SpringApplication.run(RpcfxClientApplication.class, args); + @GetMapping("/api/hello") + public User invoke() { + return userService2.findById(100); + } + + @Override + public void run(String... args) throws Exception { + userService2 = Rpcfx.createFromRegistry(UserService.class, "localhost:2181", new TagRouter(), new RandomLoadBalancer(), new CuicuiFilter()); + User user = userService2.findById(1); + System.out.println(user.getName()); } private static class TagRouter implements Router { @@ -52,9 +71,11 @@ public List route(List urls) { } private static class RandomLoadBalancer implements LoadBalancer { + private final Random random = new Random(); @Override public String select(List urls) { - return urls.get(0); + if(urls.isEmpty()) return null; + return urls.get(random.nextInt(urls.size())); } } @@ -62,7 +83,8 @@ public String select(List urls) { private static class CuicuiFilter implements Filter { @Override public boolean filter(RpcfxRequest request) { - log.info("filter {} -> {}", this.getClass().getName(), request.toString()); + //log.info("filter {} -> {}", this.getClass().getName(), request.toString()); + System.out.printf("filter %s -> %s%n", this.getClass().getName(), request.toString()); return true; } } diff --git a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/DemoResolver.java b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/DemoResolver.java index 65cffbd2..83d9b545 100644 --- a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/DemoResolver.java +++ b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/DemoResolver.java @@ -3,7 +3,9 @@ import io.kimmking.rpcfx.api.RpcfxResolver; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; +@Component public class DemoResolver implements RpcfxResolver, ApplicationContextAware { private ApplicationContext applicationContext; diff --git a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/RpcfxServerApplication.java b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/RpcfxServerApplication.java index e0a0c4e9..2b349d89 100644 --- a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/RpcfxServerApplication.java +++ b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/RpcfxServerApplication.java @@ -14,47 +14,39 @@ import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.zookeeper.CreateMode; 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.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.core.env.Environment; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +import javax.annotation.PreDestroy; import java.net.InetAddress; import java.net.InterfaceAddress; import java.net.UnknownHostException; +import java.util.Objects; @SpringBootApplication @RestController -public class RpcfxServerApplication { +@ComponentScan("io.kimmking.rpcfx") +public class RpcfxServerApplication implements CommandLineRunner { - public static void main(String[] args) throws Exception { - -// // start zk client - RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); - CuratorFramework client = CuratorFrameworkFactory.builder().connectString("localhost:2181").namespace("rpcfx").retryPolicy(retryPolicy).build(); - client.start(); -// -// -// // register service -// // xxx "io.kimmking.rpcfx.demo.api.UserService" -// - String userService = "io.kimking.rpcfx.demo.api.UserService"; - registerService(client, userService); -// String orderService = "io.kimking.rpcfx.demo.api.OrderService"; -// registerService(client, orderService); - - - // 进一步的优化,是在spring加载完成后,从里面拿到特定注解的bean,自动注册到zk + @Autowired + Environment environment; + public static void main(String[] args) throws Exception { SpringApplication.run(RpcfxServerApplication.class, args); } - private static void registerService(CuratorFramework client, String service) throws Exception { + private static void registerService(CuratorFramework client, String service, String host, int port) throws Exception { ServiceProviderDesc userServiceSesc = ServiceProviderDesc.builder() - .host(InetAddress.getLocalHost().getHostAddress()) - .port(8081).serviceClass(service).build(); + .host(host) + .port(port).serviceClass(service).build(); // String userServiceSescJson = JSON.toJSONString(userServiceSesc); try { @@ -77,30 +69,65 @@ public RpcfxResponse invoke(@RequestBody RpcfxRequest request) { return invoker.invoke(request); } - @Bean - public RpcfxInvoker createInvoker(@Autowired RpcfxResolver resolver){ - return new RpcfxInvoker(resolver); + @GetMapping("/api/hello") + public RpcfxResponse invoke() { + RpcfxRequest request = new RpcfxRequest(); + request.setServiceClass("io.kimmking.rpcfx.demo.api.UserService"); + request.setParams(new Object[]{1}); + request.setMethod("findById"); + return invoker.invoke(request); } - @Bean - public RpcfxResolver createResolver(){ - return new DemoResolver(); - } +// @Bean +// public RpcfxInvoker createInvoker(@Autowired RpcfxResolver resolver){ +// return new RpcfxInvoker(resolver); +// } - // 能否去掉name - // +// @Bean +// public RpcfxResolver createResolver(){ +// return new DemoResolver(); +// } - // annotation + @Override + public void run(String... args) throws Exception { + // // start zk client + RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); + CuratorFramework client = CuratorFrameworkFactory.builder().connectString("localhost:2181").namespace("rpcfx").retryPolicy(retryPolicy).build(); + client.start(); +// +// +// // register service +// // xxx "io.kimmking.rpcfx.demo.api.UserService" +// + String userService = "io.kimmking.rpcfx.demo.api.UserService"; + registerService(client, userService, InetAddress.getLocalHost().getHostAddress(), Integer.parseInt(Objects.requireNonNull(environment.getProperty("server.port")))); +// String orderService = "io.kimmking.rpcfx.demo.api.OrderService"; +// registerService(client, orderService); - @Bean(name = "io.kimmking.rpcfx.demo.api.UserService") - public UserService createUserService(){ - return new UserServiceImpl(); + // 进一步的优化,是在spring加载完成后,从里面拿到特定注解的bean,自动注册到zk } - @Bean(name = "io.kimmking.rpcfx.demo.api.OrderService") - public OrderService createOrderService(){ - return new OrderServiceImpl(); + @PreDestroy + public void onExit() { + System.out.println("unregistry all services from zk..."); + } + // 能否去掉name + // + + // annotation + + +// @Bean(name = "io.kimmking.rpcfx.demo.api.UserService") +// public UserService createUserService(){ +// return new UserServiceImpl(); +// } +// +// @Bean(name = "io.kimmking.rpcfx.demo.api.OrderService") +// public OrderService createOrderService(){ +// return new OrderServiceImpl(); +// } + } diff --git a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/UserServiceImpl.java b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/UserServiceImpl.java index 5c37d60a..a7cc9c5f 100644 --- a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/UserServiceImpl.java +++ b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/UserServiceImpl.java @@ -2,11 +2,19 @@ import io.kimmking.rpcfx.demo.api.User; import io.kimmking.rpcfx.demo.api.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; +@Component("io.kimmking.rpcfx.demo.api.UserService") public class UserServiceImpl implements UserService { + @Autowired + Environment environment; + @Override public User findById(int id) { - return new User(id, "KK" + System.currentTimeMillis()); + return new User(id, "KK-" + + environment.getProperty("server.port") + "_" + System.currentTimeMillis()); } } diff --git a/07rpc/rpc01/rpcfx-demo-provider/src/main/resources/application.yml b/07rpc/rpc01/rpcfx-demo-provider/src/main/resources/application.yml index 2acb33a5..bec5b5f8 100644 --- a/07rpc/rpc01/rpcfx-demo-provider/src/main/resources/application.yml +++ b/07rpc/rpc01/rpcfx-demo-provider/src/main/resources/application.yml @@ -1,6 +1,9 @@ server: - port: 8081 - - profiles: - active: true + port: 8083 + shutdown: graceful +spring: + lifecycle: + timeout-per-shutdown-phase: 20s + main: + allow-circular-references: true diff --git a/08cache/ha/conf/sentinel0.conf b/08cache/ha/conf/sentinel0.conf index df262af1..eb9f8db1 100644 --- a/08cache/ha/conf/sentinel0.conf +++ b/08cache/ha/conf/sentinel0.conf @@ -1,15 +1,14 @@ sentinel myid 8d992c54df8f8677b0b345825f61fb733c73d14c sentinel deny-scripts-reconfig yes -sentinel monitor mymaster 127.0.0.1 6380 2 -sentinel down-after-milliseconds mymaster 10000 +sentinel monitor mymaster 127.0.0.1 6380 1 +sentinel down-after-milliseconds mymaster 2000 # Generated by CONFIG REWRITE protected-mode no port 26379 user default on nopass sanitize-payload ~* &* +@all dir "/Users/kimmking/kimmking/JavaCourseCodes/08cache/ha/conf" - -sentinel config-epoch mymaster 1 -sentinel leader-epoch mymaster 1 -sentinel known-replica mymaster 127.0.0.1 6379 -sentinel current-epoch 1 +sentinel config-epoch mymaster 4 +sentinel leader-epoch mymaster 5 sentinel known-sentinel mymaster 127.0.0.1 26380 8d992c54df8f8677b0b345825f61fb733c73d14d +sentinel current-epoch 5 +sentinel known-replica mymaster 127.0.0.1 6379 diff --git a/08cache/ha/conf/sentinel1.conf b/08cache/ha/conf/sentinel1.conf index ad487fd0..0c35a846 100644 --- a/08cache/ha/conf/sentinel1.conf +++ b/08cache/ha/conf/sentinel1.conf @@ -1,14 +1,14 @@ sentinel myid 8d992c54df8f8677b0b345825f61fb733c73d14d sentinel deny-scripts-reconfig yes -sentinel monitor mymaster 127.0.0.1 6380 2 +sentinel monitor mymaster 127.0.0.1 6380 1 port 26380 -sentinel down-after-milliseconds mymaster 10000 +sentinel down-after-milliseconds mymaster 2000 # Generated by CONFIG REWRITE protected-mode no user default on nopass sanitize-payload ~* &* +@all dir "/Users/kimmking/kimmking/JavaCourseCodes/08cache/ha/conf" -sentinel config-epoch mymaster 1 -sentinel leader-epoch mymaster 1 -sentinel known-replica mymaster 127.0.0.1 6379 -sentinel current-epoch 1 +sentinel config-epoch mymaster 4 +sentinel leader-epoch mymaster 5 sentinel known-sentinel mymaster 127.0.0.1 26379 8d992c54df8f8677b0b345825f61fb733c73d14c +sentinel current-epoch 5 +sentinel known-replica mymaster 127.0.0.1 6379 From c6a07a3fec978059118a5651ec1872009950ea26 Mon Sep 17 00:00:00 2001 From: kimmking Date: Mon, 1 Jan 2024 23:03:53 +0800 Subject: [PATCH 04/13] remove some sout --- .../main/java/io/kimmking/rpcfx/client/Rpcfx.java | 12 ++++++------ .../rpcfx/demo/consumer/RpcfxClientApplication.java | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/Rpcfx.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/Rpcfx.java index 375e6813..6cd995be 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/Rpcfx.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/Rpcfx.java @@ -129,11 +129,11 @@ public RpcfxInvocationHandler(Class serviceClass, List invokers, public Object invoke(Object proxy, Method method, Object[] params) throws Throwable { List urls = router.route(invokers); - System.out.println("router.route => "); - urls.forEach(System.out::println); +// System.out.println("router.route => "); +// urls.forEach(System.out::println); String url = loadBalance.select(urls); // router, loadbalance - System.out.println("loadBalance.select => "); - System.out.println("final => " + url); +// System.out.println("loadBalance.select => "); +// System.out.println("final => " + url); if (url == null) { throw new RuntimeException("No available providers from registry center."); @@ -170,7 +170,7 @@ public Object invoke(Object proxy, Method method, Object[] params) throws Throwa private RpcfxResponse post(RpcfxRequest req, String url) throws IOException { String reqJson = JSON.toJSONString(req); - System.out.println("req json: "+reqJson); +// System.out.println("req json: "+reqJson); // 1.可以复用client // 2.尝试使用httpclient或者netty client @@ -180,7 +180,7 @@ private RpcfxResponse post(RpcfxRequest req, String url) throws IOException { .post(RequestBody.create(JSONTYPE, reqJson)) .build(); String respJson = client.newCall(request).execute().body().string(); - System.out.println("resp json: "+respJson); +// System.out.println("resp json: "+respJson); return JSON.parseObject(respJson, RpcfxResponse.class); } } diff --git a/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/RpcfxClientApplication.java b/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/RpcfxClientApplication.java index d72425f4..e0245360 100644 --- a/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/RpcfxClientApplication.java +++ b/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/RpcfxClientApplication.java @@ -84,7 +84,7 @@ private static class CuicuiFilter implements Filter { @Override public boolean filter(RpcfxRequest request) { //log.info("filter {} -> {}", this.getClass().getName(), request.toString()); - System.out.printf("filter %s -> %s%n", this.getClass().getName(), request.toString()); + //System.out.printf("filter %s -> %s%n", this.getClass().getName(), request.toString()); return true; } } From 8d5ea10bcbb65beb549713e7f9cb12c76eed9c8e Mon Sep 17 00:00:00 2001 From: kimmking Date: Mon, 1 Jan 2024 23:07:29 +0800 Subject: [PATCH 05/13] remove some sout --- .../java/io/kimmking/rpcfx/client/Rpcfx.java | 81 ---------------- .../rpcfx/client/RpcfxInvocationHandler.java | 95 +++++++++++++++++++ 2 files changed, 95 insertions(+), 81 deletions(-) create mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/RpcfxInvocationHandler.java diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/Rpcfx.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/Rpcfx.java index 6cd995be..c3c2a5de 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/Rpcfx.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/Rpcfx.java @@ -103,85 +103,4 @@ private static Object create(Class serviceClass, List invokers, R new RpcfxInvocationHandler(serviceClass, invokers, router,loadBalance, filters)); } - public static class RpcfxInvocationHandler implements InvocationHandler { - - public static final MediaType JSONTYPE = MediaType.get("application/json; charset=utf-8"); - - private final Class serviceClass; - private final List invokers; - private final Router router; - private final LoadBalancer loadBalance; - private final Filter[] filters; - - public RpcfxInvocationHandler(Class serviceClass, List invokers, Router router, LoadBalancer loadBalance, Filter... filters) { - this.serviceClass = serviceClass; - this.invokers = invokers; - this.router = router; - this.loadBalance = loadBalance; - this.filters = filters; - } - - // 可以尝试,自己去写对象序列化,二进制还是文本的,,,rpcfx是xml自定义序列化、反序列化,json: code.google.com/p/rpcfx - // int byte char float double long bool - // [], data class - - @Override - public Object invoke(Object proxy, Method method, Object[] params) throws Throwable { - - List urls = router.route(invokers); -// System.out.println("router.route => "); -// urls.forEach(System.out::println); - String url = loadBalance.select(urls); // router, loadbalance -// System.out.println("loadBalance.select => "); -// System.out.println("final => " + url); - - if (url == null) { - throw new RuntimeException("No available providers from registry center."); - } - - // 加filter地方之二 - // mock == true, new Student("hubao"); - - RpcfxRequest request = new RpcfxRequest(); - request.setServiceClass(this.serviceClass.getName()); - request.setMethod(method.getName()); - request.setParams(params); - - if (null!=filters) { - for (Filter filter : filters) { - if (!filter.filter(request)) { - return null; - } - } - } - - RpcfxResponse response = post(request, url); - - // 加filter地方之三 - // Student.setTeacher("cuijing"); - - // 这里判断response.status,处理异常 - // 考虑封装一个全局的RpcfxException - - return JSON.parse(response.getResult().toString()); - } - - OkHttpClient client = new OkHttpClient(); - - private RpcfxResponse post(RpcfxRequest req, String url) throws IOException { - String reqJson = JSON.toJSONString(req); -// System.out.println("req json: "+reqJson); - - // 1.可以复用client - // 2.尝试使用httpclient或者netty client - - final Request request = new Request.Builder() - .url(url) - .post(RequestBody.create(JSONTYPE, reqJson)) - .build(); - String respJson = client.newCall(request).execute().body().string(); -// System.out.println("resp json: "+respJson); - return JSON.parseObject(respJson, RpcfxResponse.class); - } - } } diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/RpcfxInvocationHandler.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/RpcfxInvocationHandler.java new file mode 100644 index 00000000..eed510c1 --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/RpcfxInvocationHandler.java @@ -0,0 +1,95 @@ +package io.kimmking.rpcfx.client; + +import com.alibaba.fastjson.JSON; +import io.kimmking.rpcfx.api.*; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; + +import java.io.IOException; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.util.List; + +public class RpcfxInvocationHandler implements InvocationHandler { + + public static final MediaType JSONTYPE = MediaType.get("application/json; charset=utf-8"); + + private final Class serviceClass; + private final List invokers; + private final Router router; + private final LoadBalancer loadBalance; + private final Filter[] filters; + + public RpcfxInvocationHandler(Class serviceClass, List invokers, Router router, LoadBalancer loadBalance, Filter... filters) { + this.serviceClass = serviceClass; + this.invokers = invokers; + this.router = router; + this.loadBalance = loadBalance; + this.filters = filters; + } + + // 可以尝试,自己去写对象序列化,二进制还是文本的,,,rpcfx是xml自定义序列化、反序列化,json: code.google.com/p/rpcfx + // int byte char float double long bool + // [], data class + + @Override + public Object invoke(Object proxy, Method method, Object[] params) throws Throwable { + + List urls = router.route(invokers); +// System.out.println("router.route => "); +// urls.forEach(System.out::println); + String url = loadBalance.select(urls); // router, loadbalance +// System.out.println("loadBalance.select => "); +// System.out.println("final => " + url); + + if (url == null) { + throw new RuntimeException("No available providers from registry center."); + } + + // 加filter地方之二 + // mock == true, new Student("hubao"); + + RpcfxRequest request = new RpcfxRequest(); + request.setServiceClass(this.serviceClass.getName()); + request.setMethod(method.getName()); + request.setParams(params); + + if (null!=filters) { + for (Filter filter : filters) { + if (!filter.filter(request)) { + return null; + } + } + } + + RpcfxResponse response = post(request, url); + + // 加filter地方之三 + // Student.setTeacher("cuijing"); + + // 这里判断response.status,处理异常 + // 考虑封装一个全局的RpcfxException + + return JSON.parse(response.getResult().toString()); + } + + OkHttpClient client = new OkHttpClient(); + + private RpcfxResponse post(RpcfxRequest req, String url) throws IOException { + String reqJson = JSON.toJSONString(req); +// System.out.println("req json: "+reqJson); + + // 1.可以复用client + // 2.尝试使用httpclient或者netty client + + final Request request = new Request.Builder() + .url(url) + .post(RequestBody.create(JSONTYPE, reqJson)) + .build(); + String respJson = client.newCall(request).execute().body().string(); +// System.out.println("resp json: "+respJson); + return JSON.parseObject(respJson, RpcfxResponse.class); + } +} \ No newline at end of file From 8afa0eb3e929fb4afb7447fe32d8982bfed64226 Mon Sep 17 00:00:00 2001 From: kimmking Date: Tue, 2 Jan 2024 00:11:35 +0800 Subject: [PATCH 06/13] remove some sout --- .../main/java/io/kimmking/rpcfx/client/Rpcfx.java | 2 +- .../src/main/resources/application.yml | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 07rpc/rpc01/rpcfx-demo-consumer/src/main/resources/application.yml diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/Rpcfx.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/Rpcfx.java index c3c2a5de..e887b587 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/Rpcfx.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/Rpcfx.java @@ -100,7 +100,7 @@ private static void fetchInvokers(CuratorFramework client, String service, List< private static Object create(Class serviceClass, List invokers, Router router, LoadBalancer loadBalance, Filter... filters) { // 0. 替换动态代理 -> 字节码生成 return (T) Proxy.newProxyInstance(Rpcfx.class.getClassLoader(), new Class[]{serviceClass}, - new RpcfxInvocationHandler(serviceClass, invokers, router,loadBalance, filters)); + new RpcfxInvocationHandler(serviceClass, invokers, router, loadBalance, filters)); } } diff --git a/07rpc/rpc01/rpcfx-demo-consumer/src/main/resources/application.yml b/07rpc/rpc01/rpcfx-demo-consumer/src/main/resources/application.yml new file mode 100644 index 00000000..8728f144 --- /dev/null +++ b/07rpc/rpc01/rpcfx-demo-consumer/src/main/resources/application.yml @@ -0,0 +1,14 @@ +server: + port: 8080 + shutdown: graceful + +spring: + lifecycle: + timeout-per-shutdown-phase: 20s + main: + allow-circular-references: true + task: + execution: + pool: + core-size: 32 + max-size: 128 \ No newline at end of file From 42fe0c8f8a119b157d26303d977232bf759b5c86 Mon Sep 17 00:00:00 2001 From: kimmking Date: Sat, 13 Jan 2024 02:15:40 +0800 Subject: [PATCH 07/13] add connectionPool --- .../rpcfx/client/RpcfxInvocationHandler.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/RpcfxInvocationHandler.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/RpcfxInvocationHandler.java index eed510c1..f77acf97 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/RpcfxInvocationHandler.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/RpcfxInvocationHandler.java @@ -2,15 +2,13 @@ import com.alibaba.fastjson.JSON; import io.kimmking.rpcfx.api.*; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; +import okhttp3.*; import java.io.IOException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.List; +import java.util.concurrent.TimeUnit; public class RpcfxInvocationHandler implements InvocationHandler { @@ -75,7 +73,13 @@ public Object invoke(Object proxy, Method method, Object[] params) throws Throwa return JSON.parse(response.getResult().toString()); } - OkHttpClient client = new OkHttpClient(); + OkHttpClient client = new OkHttpClient.Builder() + .connectionPool(new ConnectionPool(128, 60, TimeUnit.SECONDS)) +// .dispatcher(dispatcher) +// .readTimeout(httpClientConfig.getReadTimeout(), TimeUnit.SECONDS) +// .writeTimeout(httpClientConfig.getWriteTimeout(), TimeUnit.SECONDS) +// .connectTimeout(httpClientConfig.getConnectTimeout(), TimeUnit.SECONDS) + .build(); private RpcfxResponse post(RpcfxRequest req, String url) throws IOException { String reqJson = JSON.toJSONString(req); From 04090c923fa5c67b8596e621042580cb0b736e6b Mon Sep 17 00:00:00 2001 From: kimmking Date: Sun, 14 Jan 2024 00:41:54 +0800 Subject: [PATCH 08/13] refactor RPC and Registry --- 07rpc/rpc01/client-rest.http | 1 + .../rpcfx/annotation/RpcfxReference.java | 2 +- .../io/kimmking/rpcfx/api/RpcContext.java | 25 ++++ .../io/kimmking/rpcfx/api/RpcfxResolver.java | 7 - .../rpcfx/consumer/ConsumerBootstrap.java | 60 ++++++++ .../RpcfxInvocationHandler.java | 10 +- .../Rpcfx.java => consumer/RpcfxInvoker.java} | 65 ++++----- .../rpcfx/discovery/DiscoveryClient.java | 10 ++ .../io/kimmking/rpcfx/meta/ProviderMeta.java | 19 +++ .../rpcfx/provider/ProviderBootstrap.java | 132 ++++++++++++++++++ .../{server => provider}/RpcfxInvoker.java | 35 ++--- .../rpcfx/registry/RegistryCenter.java | 57 ++++++++ .../rpcfx/stub/StubSkeletonHelper.java | 96 +++++++++++++ .../io/kimmking/rpcfx/utils/MethodUtils.java | 29 ++++ .../rpcfx/demo/consumer/HelloController.java | 26 ++++ .../demo/consumer/RpcfxClientApplication.java | 67 ++------- .../rpcfx/demo/provider/DemoResolver.java | 22 --- .../rpcfx/demo/provider/OrderServiceImpl.java | 12 +- .../demo/provider/RpcfxServerApplication.java | 94 +------------ .../rpcfx/demo/provider/UserServiceImpl.java | 2 + 20 files changed, 540 insertions(+), 231 deletions(-) create mode 100644 07rpc/rpc01/client-rest.http create mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/RpcContext.java delete mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/RpcfxResolver.java create mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/consumer/ConsumerBootstrap.java rename 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/{client => consumer}/RpcfxInvocationHandler.java (95%) rename 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/{client/Rpcfx.java => consumer/RpcfxInvoker.java} (53%) create mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/discovery/DiscoveryClient.java create mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/meta/ProviderMeta.java create mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/provider/ProviderBootstrap.java rename 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/{server => provider}/RpcfxInvoker.java (51%) create mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/RegistryCenter.java create mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/stub/StubSkeletonHelper.java create mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/utils/MethodUtils.java create mode 100644 07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/HelloController.java delete mode 100644 07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/DemoResolver.java diff --git a/07rpc/rpc01/client-rest.http b/07rpc/rpc01/client-rest.http new file mode 100644 index 00000000..5808372d --- /dev/null +++ b/07rpc/rpc01/client-rest.http @@ -0,0 +1 @@ +http://127.0.0.1:8080/api/hello \ No newline at end of file diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/annotation/RpcfxReference.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/annotation/RpcfxReference.java index 8fd1f4af..570b8ab8 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/annotation/RpcfxReference.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/annotation/RpcfxReference.java @@ -10,7 +10,7 @@ */ @Documented @Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) +@Target(ElementType.FIELD) @Inherited public @interface RpcfxReference { diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/RpcContext.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/RpcContext.java new file mode 100644 index 00000000..e5365ab7 --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/RpcContext.java @@ -0,0 +1,25 @@ +package io.kimmking.rpcfx.api; + +import io.kimmking.rpcfx.meta.ProviderMeta; +import lombok.Getter; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +import java.util.HashMap; +import java.util.Map; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2024/1/13 20:34 + */ +public class RpcContext { + + @Getter + private MultiValueMap providerHolder = new LinkedMultiValueMap<>(); + + @Getter + private Map consumerHolder = new HashMap<>(); + +} diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/RpcfxResolver.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/RpcfxResolver.java deleted file mode 100644 index f7c48068..00000000 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/RpcfxResolver.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.kimmking.rpcfx.api; - -public interface RpcfxResolver { - - Object resolve(String serviceClass); - -} diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/consumer/ConsumerBootstrap.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/consumer/ConsumerBootstrap.java new file mode 100644 index 00000000..16527507 --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/consumer/ConsumerBootstrap.java @@ -0,0 +1,60 @@ +package io.kimmking.rpcfx.consumer; + +import io.kimmking.rpcfx.annotation.RpcfxReference; +import io.kimmking.rpcfx.api.RpcContext; +import io.kimmking.rpcfx.stub.StubSkeletonHelper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeansException; +import org.springframework.beans.PropertyValues; +import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; +import org.springframework.stereotype.Component; + +import java.io.Closeable; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2024/1/13 23:26 + */ +@Slf4j +@Component +public class ConsumerBootstrap implements Closeable, InstantiationAwareBeanPostProcessor { + + private RpcContext rpcContext = new RpcContext(); + + private String scanPackage = "io.kimmking"; + + @Override + public void close() throws IOException { + + } + + @Override + public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException { + if (bean.getClass().getPackage().getName().startsWith(scanPackage)) { + Field[] declaredFields = bean.getClass().getDeclaredFields(); + List consumers = Arrays.stream(declaredFields).filter(field -> field.isAnnotationPresent(RpcfxReference.class)).collect(Collectors.toList()); + + consumers.stream().forEach(consumer -> { + Object consumer1 = createConsumer(consumer.getType()); + try { + consumer.setAccessible(true); + consumer.set(bean, consumer1); + } catch (IllegalAccessException e) { + log.error(e.getMessage(), e); + } + }); + } + return null; + } + + private T createConsumer(Class clazz) { + return StubSkeletonHelper.createConsumer(clazz, rpcContext); + } +} diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/RpcfxInvocationHandler.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/consumer/RpcfxInvocationHandler.java similarity index 95% rename from 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/RpcfxInvocationHandler.java rename to 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/consumer/RpcfxInvocationHandler.java index f77acf97..5c755596 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/RpcfxInvocationHandler.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/consumer/RpcfxInvocationHandler.java @@ -1,7 +1,8 @@ -package io.kimmking.rpcfx.client; +package io.kimmking.rpcfx.consumer; import com.alibaba.fastjson.JSON; import io.kimmking.rpcfx.api.*; +import io.kimmking.rpcfx.stub.StubSkeletonHelper; import okhttp3.*; import java.io.IOException; @@ -35,6 +36,10 @@ public RpcfxInvocationHandler(Class serviceClass, List invokers, @Override public Object invoke(Object proxy, Method method, Object[] params) throws Throwable { + if (!StubSkeletonHelper.checkRpcMethod(method)){ + return null ; + } + List urls = router.route(invokers); // System.out.println("router.route => "); // urls.forEach(System.out::println); @@ -85,9 +90,6 @@ private RpcfxResponse post(RpcfxRequest req, String url) throws IOException { String reqJson = JSON.toJSONString(req); // System.out.println("req json: "+reqJson); - // 1.可以复用client - // 2.尝试使用httpclient或者netty client - final Request request = new Request.Builder() .url(url) .post(RequestBody.create(JSONTYPE, reqJson)) diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/Rpcfx.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/consumer/RpcfxInvoker.java similarity index 53% rename from 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/Rpcfx.java rename to 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/consumer/RpcfxInvoker.java index e887b587..356c7fe3 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/Rpcfx.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/consumer/RpcfxInvoker.java @@ -1,13 +1,8 @@ -package io.kimmking.rpcfx.client; +package io.kimmking.rpcfx.consumer; -import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.parser.ParserConfig; import io.kimmking.rpcfx.api.*; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; import org.apache.curator.RetryPolicy; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; @@ -15,40 +10,41 @@ import org.apache.curator.framework.recipes.cache.TreeCacheEvent; import org.apache.curator.framework.recipes.cache.TreeCacheListener; import org.apache.curator.retry.ExponentialBackoffRetry; -import org.apache.zookeeper.CreateMode; -import java.io.IOException; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; import java.lang.reflect.Proxy; -import java.net.InetAddress; import java.util.ArrayList; import java.util.List; -public final class Rpcfx { +public final class RpcfxInvoker { static { ParserConfig.getGlobalInstance().addAccept("io.kimmking"); } + CuratorFramework client; + String zkUrl = null; - public static T createFromRegistry(final Class serviceClass, final String zkUrl, Router router, LoadBalancer loadBalance, Filter filter) { + public RpcfxInvoker(String zkUrl) { + this.zkUrl = zkUrl; //"localhost:2181" + this.start(); + } + + public void start() { + RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); + client = CuratorFrameworkFactory.builder().connectString(this.zkUrl).namespace("rpcfx").retryPolicy(retryPolicy).build(); + client.start(); + } + + public void stop() { + client.close(); + } - // 加filte之一 + public T createFromRegistry(final Class serviceClass, Router router, LoadBalancer loadBalance, Filter filter) { String service = serviceClass.getCanonicalName();//"io.kimking.rpcfx.demo.api.UserService"; System.out.println("====> "+service); List invokers = new ArrayList<>(); - RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); - CuratorFramework client = CuratorFrameworkFactory.builder().connectString("localhost:2181").namespace("rpcfx").retryPolicy(retryPolicy).build(); - client.start(); - try { -// ServiceProviderDesc userServiceSesc = ServiceProviderDesc.builder() -// .host(InetAddress.getLocalHost().getHostAddress()) -// .port(8082).serviceClass(service).build(); - // String userServiceSescJson = JSON.toJSONString(userServiceSesc); - if ( null == client.checkExists().forPath("/" + service)) { return null; @@ -69,25 +65,13 @@ public void childEvent(CuratorFramework curatorFramework, TreeCacheEvent treeCac ex.printStackTrace(); } -// -// -// // register service -// // xxx "io.kimmking.rpcfx.demo.api.UserService" -// - - - // curator Provider list from zk - - // 1. 简单:从zk拿到服务提供的列表 - // 2. 挑战:监听zk的临时节点,根据事件更新这个list(注意,需要做个全局map保持每个服务的提供者List) - return (T) create(serviceClass, invokers, router, loadBalance, filter); } - private static void fetchInvokers(CuratorFramework client, String service, List invokers) throws Exception { + private void fetchInvokers(CuratorFramework client, String service, List invokers) throws Exception { List services = client.getChildren().forPath("/" + service); invokers.clear(); for (String svc : services) { @@ -97,10 +81,11 @@ private static void fetchInvokers(CuratorFramework client, String service, List< } } - private static Object create(Class serviceClass, List invokers, Router router, LoadBalancer loadBalance, Filter... filters) { - // 0. 替换动态代理 -> 字节码生成 - return (T) Proxy.newProxyInstance(Rpcfx.class.getClassLoader(), new Class[]{serviceClass}, - new RpcfxInvocationHandler(serviceClass, invokers, router, loadBalance, filters)); + private T create(Class serviceClass, List invokers, Router router, LoadBalancer loadBalance, Filter... filters) { + RpcfxInvocationHandler invocationHandler + = new RpcfxInvocationHandler(serviceClass, invokers, router, loadBalance, filters); + return (T) Proxy.newProxyInstance(RpcfxInvoker.class.getClassLoader(), + new Class[]{serviceClass}, invocationHandler); } } diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/discovery/DiscoveryClient.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/discovery/DiscoveryClient.java new file mode 100644 index 00000000..46f89433 --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/discovery/DiscoveryClient.java @@ -0,0 +1,10 @@ +package io.kimmking.rpcfx.discovery; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2024/1/13 20:17 + */ +public class DiscoveryClient { +} diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/meta/ProviderMeta.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/meta/ProviderMeta.java new file mode 100644 index 00000000..f0b9e0ae --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/meta/ProviderMeta.java @@ -0,0 +1,19 @@ +package io.kimmking.rpcfx.meta; + +import lombok.Data; + +import java.lang.reflect.Method; + +/** + * @author lirui + */ +@Data +public class ProviderMeta { + + private Object serviceImpl; + + private Method method; + + private String methodSign; + +} diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/provider/ProviderBootstrap.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/provider/ProviderBootstrap.java new file mode 100644 index 00000000..5b541a34 --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/provider/ProviderBootstrap.java @@ -0,0 +1,132 @@ +package io.kimmking.rpcfx.provider; + +import io.kimmking.rpcfx.annotation.RpcfxService; +import io.kimmking.rpcfx.api.RpcContext; +import io.kimmking.rpcfx.registry.RegistryCenter; +import io.kimmking.rpcfx.stub.StubSkeletonHelper; +import lombok.Getter; +import lombok.SneakyThrows; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationRunner; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.Order; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.net.InetAddress; +import java.util.Arrays; +import java.util.Objects; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2024/1/13 20:27 + */ + +@Component +public class ProviderBootstrap { + + @Autowired + private ApplicationContext applicationContext; + + @Autowired + Environment environment; + + private RpcContext rpcContext = new RpcContext(); + + @Getter + private RpcfxInvoker invoker; + + private static String PROTO = "http"; + private static String ip; + private static int port; + + RegistryCenter registry = new RegistryCenter(); + + @SneakyThrows + @PostConstruct + public void start(){ + System.out.println("build all services from annotation..."); + buildProvider(); + + System.out.println("get IP and PORT..."); + ip = InetAddress.getLocalHost().getHostAddress(); + port = Integer.parseInt(Objects.requireNonNull(environment.getProperty("server.port"))); + } + + private void buildProvider() { + String[] beansName = applicationContext.getBeanDefinitionNames(); + for (int i = 0; i < beansName.length; i++) { + String beanName = beansName[i]; + Object bean = applicationContext.getBean(beanName); + RpcfxService provider = AnnotationUtils.findAnnotation(bean.getClass(), RpcfxService.class); + if (provider == null) { + continue; + } + Class[] classes = bean.getClass().getInterfaces(); + if (classes == null || classes.length == 0) { + continue; + } + Arrays.stream(classes).forEach(c -> this.createProvider(c, bean)); + } + } + + private void createProvider(Class clazz, Object bean) { + StubSkeletonHelper.createProvider(clazz, bean, rpcContext); // 初始化了holder + this.invoker = new RpcfxInvoker(rpcContext); + } + + @Order(Integer.MIN_VALUE) + @Bean + public ApplicationRunner run() throws Exception { + return x -> registerServices(); + } + + private void registerServices() { + + registry.start(); + + System.out.println("registry all services from zk..."); + rpcContext.getProviderHolder().forEach( (x,y) -> + { + System.out.println(" register " + x); + + try { + registry.registerService(x, ip, port); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + ); + } + + @PreDestroy + public void stop() { + unregisterServices(); + } + + private void unregisterServices() { + System.out.println("unregistry all services from zk..."); + rpcContext.getProviderHolder().forEach( (x,y) -> + { + System.out.println(" unregister " + x); + try { + registry.unregisterService(x, ip, port); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + ); + + registry.stop(); + + } + + + +} diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/server/RpcfxInvoker.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/provider/RpcfxInvoker.java similarity index 51% rename from 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/server/RpcfxInvoker.java rename to 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/provider/RpcfxInvoker.java index faeb1c85..4767e046 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/server/RpcfxInvoker.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/provider/RpcfxInvoker.java @@ -1,37 +1,35 @@ -package io.kimmking.rpcfx.server; +package io.kimmking.rpcfx.provider; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; +import io.kimmking.rpcfx.api.RpcContext; import io.kimmking.rpcfx.api.RpcfxRequest; -import io.kimmking.rpcfx.api.RpcfxResolver; import io.kimmking.rpcfx.api.RpcfxResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; +import io.kimmking.rpcfx.meta.ProviderMeta; +import org.springframework.util.CollectionUtils; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.Arrays; +import java.util.List; +import java.util.Optional; -@Component public class RpcfxInvoker { - @Autowired - private RpcfxResolver resolver; + RpcContext context; - public RpcfxInvoker(RpcfxResolver resolver){ - this.resolver = resolver; + public RpcfxInvoker(RpcContext context) { + this.context = context; } public RpcfxResponse invoke(RpcfxRequest request) { RpcfxResponse response = new RpcfxResponse(); String serviceClass = request.getServiceClass(); - // 作业1:改成泛型和反射 - Object service = resolver.resolve(serviceClass);//this.applicationContext.getBean(serviceClass); + ProviderMeta meta = findProvider(serviceClass, request.getMethod()); try { - Method method = resolveMethodFromClass(service.getClass(), request.getMethod()); - Object result = method.invoke(service, request.getParams()); // dubbo, fastjson, + Method method = meta.getMethod(); + Object result = method.invoke(meta.getServiceImpl(), request.getParams()); // dubbo, fastjson, // 两次json序列化能否合并成一个 response.setResult(JSON.toJSONString(result, SerializerFeature.WriteClassName)); response.setStatus(true); @@ -49,8 +47,13 @@ public RpcfxResponse invoke(RpcfxRequest request) { } } - private Method resolveMethodFromClass(Class klass, String methodName) { - return Arrays.stream(klass.getMethods()).filter(m -> methodName.equals(m.getName())).findFirst().get(); + protected ProviderMeta findProvider(String interfaceName, String methodSign) { + List providerMetas = context.getProviderHolder().get(interfaceName); + if (!CollectionUtils.isEmpty(providerMetas)) { + Optional providerMeta = providerMetas.stream().filter(provider -> methodSign.equals(provider.getMethodSign())).findFirst(); + return providerMeta.orElse(null); + } + return null; } } diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/RegistryCenter.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/RegistryCenter.java new file mode 100644 index 00000000..f7ec83bf --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/RegistryCenter.java @@ -0,0 +1,57 @@ +package io.kimmking.rpcfx.registry; + +import io.kimmking.rpcfx.api.ServiceProviderDesc; +import org.apache.curator.RetryPolicy; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.retry.ExponentialBackoffRetry; +import org.apache.zookeeper.CreateMode; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2024/1/13 20:16 + */ +public class RegistryCenter { + + CuratorFramework client = null; + public void start() { + RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); + client = CuratorFrameworkFactory.builder().connectString("localhost:2181").namespace("rpcfx").retryPolicy(retryPolicy).build(); + client.start(); + } + + public void stop(){ + client.close(); + } + + public void registerService(String service, String host, int port) throws Exception { + ServiceProviderDesc userServiceSesc = ServiceProviderDesc.builder() + .host(host) + .port(port).serviceClass(service).build(); + // String userServiceSescJson = JSON.toJSONString(userServiceSesc); + + try { + if ( null == client.checkExists().forPath("/" + service)) { + client.create().withMode(CreateMode.PERSISTENT).forPath("/" + service, "service".getBytes()); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + + client.create().withMode(CreateMode.EPHEMERAL). + forPath( "/" + service + "/" + userServiceSesc.getHost() + "_" + userServiceSesc.getPort(), "provider".getBytes()); + } + + public void unregisterService(String service, String host, int port) throws Exception { + + if (null == client.checkExists().forPath("/" + service)) { + return; + } + System.out.println("delete " + "/" + service + "/" + host + "_" + port); + client.delete().quietly(). + forPath( "/" + service + "/" + host + "_" + port); + } + +} diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/stub/StubSkeletonHelper.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/stub/StubSkeletonHelper.java new file mode 100644 index 00000000..ebe5f7ab --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/stub/StubSkeletonHelper.java @@ -0,0 +1,96 @@ +package io.kimmking.rpcfx.stub; + +import io.kimmking.rpcfx.api.*; +import io.kimmking.rpcfx.consumer.RpcfxInvoker; +import io.kimmking.rpcfx.meta.ProviderMeta; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.MultiValueMap; + +import java.lang.reflect.Method; +import java.util.List; +import java.util.Random; + +/** + * @author lirui + */ +public class StubSkeletonHelper { + + public static void createProvider(Class clazz, Object serviceImpl, RpcContext rpcContext) { + String clazzName = clazz.getName(); + Class callClass = serviceImpl.getClass(); + + Method[] methodList = callClass.getMethods(); + for (Method method : methodList) { + if (!checkRpcMethod(method)) { + continue; + } + ProviderMeta providerMeta = buildProviderMeta(method, serviceImpl); + + MultiValueMap providerHolder = rpcContext.getProviderHolder(); + providerHolder.add(clazzName, providerMeta); + } + } + + private static ProviderMeta buildProviderMeta(Method method, Object serviceImpl) { + String methodSign = method.getName();//MethodUtils.methodSign(method); + ProviderMeta providerMeta = new ProviderMeta(); + providerMeta.setMethod(method); + providerMeta.setServiceImpl(serviceImpl); + providerMeta.setMethodSign(methodSign); + return providerMeta; + } + + public static boolean checkRpcMethod(final Method method) { + //本地方法不代理 + if ("toString".equals(method.getName()) || + "hashCode".equals(method.getName()) || + "notifyAll".equals(method.getName()) || + "equals".equals(method.getName()) || + "wait".equals(method.getName()) || + "getClass".equals(method.getName()) || + "notify".equals(method.getName())) { + return false; + } + return true; + } + + public static T createConsumer(Class clazz, RpcContext rpcContext) { + String clazzName = clazz.getName(); + T proxyHandler = (T) rpcContext.getConsumerHolder().get(clazzName); + if (proxyHandler == null) { // TODO configuration + proxyHandler = new RpcfxInvoker("localhost:2181") + .createFromRegistry(clazz, new TagRouter(), + new RandomLoadBalancer(), new CuicuiFilter()); + rpcContext.getConsumerHolder().put(clazzName, proxyHandler); + } + return (T) proxyHandler; + } + + + private static class TagRouter implements Router { + @Override + public List route(List urls) { + return urls; + } + } + + private static class RandomLoadBalancer implements LoadBalancer { + private final Random random = new Random(); + @Override + public String select(List urls) { + if(urls.isEmpty()) return null; + return urls.get(random.nextInt(urls.size())); + } + } + + @Slf4j + private static class CuicuiFilter implements Filter { + @Override + public boolean filter(RpcfxRequest request) { + //log.info("filter {} -> {}", this.getClass().getName(), request.toString()); + //System.out.printf("filter %s -> %s%n", this.getClass().getName(), request.toString()); + return true; + } + } + +} diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/utils/MethodUtils.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/utils/MethodUtils.java new file mode 100644 index 00000000..8158ccff --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/utils/MethodUtils.java @@ -0,0 +1,29 @@ +package io.kimmking.rpcfx.utils; + +import org.springframework.util.DigestUtils; + +import java.lang.reflect.Method; +import java.util.Arrays; + +public class MethodUtils { + + public static String methodSign(Method method) { + if (method != null) { + StringBuilder builder = new StringBuilder("method:"); + String name = method.getName(); + builder.append(name); + builder.append("_"); + int count = method.getParameterCount(); + builder.append(count); + builder.append("_"); + if (count > 0) { + Class[] classes = method.getParameterTypes(); + Arrays.stream(classes).forEach(c -> builder.append(c.getName() + ",")); + } + String string = builder.toString(); + return DigestUtils.md5DigestAsHex(string.getBytes()); + } + return ""; + } + +} \ No newline at end of file diff --git a/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/HelloController.java b/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/HelloController.java new file mode 100644 index 00000000..14f75607 --- /dev/null +++ b/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/HelloController.java @@ -0,0 +1,26 @@ +package io.kimmking.rpcfx.demo.consumer; + +import io.kimmking.rpcfx.annotation.RpcfxReference; +import io.kimmking.rpcfx.demo.api.User; +import io.kimmking.rpcfx.demo.api.UserService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2024/1/14 00:35 + */ + +@RestController +public class HelloController { + @RpcfxReference + UserService userService2;// = Rpcfx.createFromRegistry(UserService.class, "localhost:2181", new TagRouter(), new RandomLoadBalancer(), new CuicuiFilter()); + + @GetMapping("/api/hello") + public User invoke() { + return userService2.findById(100); + } + +} diff --git a/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/RpcfxClientApplication.java b/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/RpcfxClientApplication.java index e0245360..dc058edd 100644 --- a/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/RpcfxClientApplication.java +++ b/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/RpcfxClientApplication.java @@ -1,9 +1,12 @@ package io.kimmking.rpcfx.demo.consumer; -import io.kimmking.rpcfx.api.*; -import io.kimmking.rpcfx.client.Rpcfx; -import io.kimmking.rpcfx.demo.api.Order; -import io.kimmking.rpcfx.demo.api.OrderService; +import com.alibaba.fastjson.JSON; +import io.kimmking.rpcfx.annotation.RpcfxReference; +import io.kimmking.rpcfx.api.Filter; +import io.kimmking.rpcfx.api.LoadBalancer; +import io.kimmking.rpcfx.api.Router; +import io.kimmking.rpcfx.api.RpcfxRequest; +import io.kimmking.rpcfx.consumer.RpcfxInvoker; import io.kimmking.rpcfx.demo.api.User; import io.kimmking.rpcfx.demo.api.UserService; import lombok.extern.slf4j.Slf4j; @@ -11,23 +14,15 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.ComponentScans; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; import java.util.Random; -import java.util.concurrent.atomic.AtomicInteger; @SpringBootApplication -@RestController -//@ComponentScan("io.kimmking.rpcfx") -public class RpcfxClientApplication implements CommandLineRunner { - - // 二方库 - // 三方库 lib - // nexus, userserivce -> userdao -> user - // +@ComponentScan("io.kimmking.rpcfx") +public class RpcfxClientApplication { public static void main(String[] args) { @@ -49,45 +44,13 @@ public static void main(String[] args) { SpringApplication.run(RpcfxClientApplication.class, args); } - UserService userService2;// = Rpcfx.createFromRegistry(UserService.class, "localhost:2181", new TagRouter(), new RandomLoadBalancer(), new CuicuiFilter()); - - @GetMapping("/api/hello") - public User invoke() { - return userService2.findById(100); - } - - @Override - public void run(String... args) throws Exception { - userService2 = Rpcfx.createFromRegistry(UserService.class, "localhost:2181", new TagRouter(), new RandomLoadBalancer(), new CuicuiFilter()); - User user = userService2.findById(1); - System.out.println(user.getName()); - } - - private static class TagRouter implements Router { - @Override - public List route(List urls) { - return urls; - } - } - - private static class RandomLoadBalancer implements LoadBalancer { - private final Random random = new Random(); - @Override - public String select(List urls) { - if(urls.isEmpty()) return null; - return urls.get(random.nextInt(urls.size())); - } - } +// @Override +// public void run(String... args) throws Exception { +// // userService2 = RpcfxInvoker.createFromRegistry(UserService.class, new TagRouter(), new RandomLoadBalancer(), new CuicuiFilter()); +// User user = userService2.findById(1); +// System.out.println(JSON.toJSONString(user)); +// } - @Slf4j - private static class CuicuiFilter implements Filter { - @Override - public boolean filter(RpcfxRequest request) { - //log.info("filter {} -> {}", this.getClass().getName(), request.toString()); - //System.out.printf("filter %s -> %s%n", this.getClass().getName(), request.toString()); - return true; - } - } } diff --git a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/DemoResolver.java b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/DemoResolver.java deleted file mode 100644 index 83d9b545..00000000 --- a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/DemoResolver.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.kimmking.rpcfx.demo.provider; - -import io.kimmking.rpcfx.api.RpcfxResolver; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.stereotype.Component; - -@Component -public class DemoResolver implements RpcfxResolver, ApplicationContextAware { - - private ApplicationContext applicationContext; - - @Override - public void setApplicationContext(ApplicationContext applicationContext) { - this.applicationContext = applicationContext; - } - - @Override - public Object resolve(String serviceClass) { - return this.applicationContext.getBean(serviceClass); - } -} diff --git a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/OrderServiceImpl.java b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/OrderServiceImpl.java index 8e56c10e..96a4768a 100644 --- a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/OrderServiceImpl.java +++ b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/OrderServiceImpl.java @@ -1,12 +1,22 @@ package io.kimmking.rpcfx.demo.provider; +import io.kimmking.rpcfx.annotation.RpcfxService; import io.kimmking.rpcfx.demo.api.Order; import io.kimmking.rpcfx.demo.api.OrderService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; +@RpcfxService +@Component public class OrderServiceImpl implements OrderService { + @Autowired + Environment environment; + @Override public Order findOrderById(int id) { - return new Order(id, "Order" + System.currentTimeMillis(), 9.9f); + return new Order(id, "KK-" + + environment.getProperty("server.port") + "_Order" + System.currentTimeMillis(), 9.9f); } } diff --git a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/RpcfxServerApplication.java b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/RpcfxServerApplication.java index 2b349d89..aacab597 100644 --- a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/RpcfxServerApplication.java +++ b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/RpcfxServerApplication.java @@ -2,34 +2,18 @@ import com.alibaba.fastjson.JSON; import io.kimmking.rpcfx.api.RpcfxRequest; -import io.kimmking.rpcfx.api.RpcfxResolver; import io.kimmking.rpcfx.api.RpcfxResponse; -import io.kimmking.rpcfx.api.ServiceProviderDesc; -import io.kimmking.rpcfx.demo.api.OrderService; -import io.kimmking.rpcfx.demo.api.UserService; -import io.kimmking.rpcfx.server.RpcfxInvoker; -import org.apache.curator.RetryPolicy; -import org.apache.curator.framework.CuratorFramework; -import org.apache.curator.framework.CuratorFrameworkFactory; -import org.apache.curator.retry.ExponentialBackoffRetry; -import org.apache.zookeeper.CreateMode; +import io.kimmking.rpcfx.provider.ProviderBootstrap; 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.annotation.Bean; import org.springframework.context.annotation.ComponentScan; -import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import javax.annotation.PreDestroy; -import java.net.InetAddress; -import java.net.InterfaceAddress; -import java.net.UnknownHostException; -import java.util.Objects; @SpringBootApplication @RestController @@ -37,36 +21,16 @@ public class RpcfxServerApplication implements CommandLineRunner { @Autowired - Environment environment; + ProviderBootstrap bootstrap; public static void main(String[] args) throws Exception { SpringApplication.run(RpcfxServerApplication.class, args); } - private static void registerService(CuratorFramework client, String service, String host, int port) throws Exception { - ServiceProviderDesc userServiceSesc = ServiceProviderDesc.builder() - .host(host) - .port(port).serviceClass(service).build(); - // String userServiceSescJson = JSON.toJSONString(userServiceSesc); - - try { - if ( null == client.checkExists().forPath("/" + service)) { - client.create().withMode(CreateMode.PERSISTENT).forPath("/" + service, "service".getBytes()); - } - } catch (Exception ex) { - ex.printStackTrace(); - } - - client.create().withMode(CreateMode.EPHEMERAL). - forPath( "/" + service + "/" + userServiceSesc.getHost() + "_" + userServiceSesc.getPort(), "provider".getBytes()); - } - - @Autowired - RpcfxInvoker invoker; @PostMapping("/") public RpcfxResponse invoke(@RequestBody RpcfxRequest request) { - return invoker.invoke(request); + return bootstrap.getInvoker().invoke(request); } @GetMapping("/api/hello") @@ -75,59 +39,13 @@ public RpcfxResponse invoke() { request.setServiceClass("io.kimmking.rpcfx.demo.api.UserService"); request.setParams(new Object[]{1}); request.setMethod("findById"); - return invoker.invoke(request); + return bootstrap.getInvoker().invoke(request); } -// @Bean -// public RpcfxInvoker createInvoker(@Autowired RpcfxResolver resolver){ -// return new RpcfxInvoker(resolver); -// } - -// @Bean -// public RpcfxResolver createResolver(){ -// return new DemoResolver(); -// } - @Override public void run(String... args) throws Exception { - // // start zk client - RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); - CuratorFramework client = CuratorFrameworkFactory.builder().connectString("localhost:2181").namespace("rpcfx").retryPolicy(retryPolicy).build(); - client.start(); -// -// -// // register service -// // xxx "io.kimmking.rpcfx.demo.api.UserService" -// - String userService = "io.kimmking.rpcfx.demo.api.UserService"; - registerService(client, userService, InetAddress.getLocalHost().getHostAddress(), Integer.parseInt(Objects.requireNonNull(environment.getProperty("server.port")))); -// String orderService = "io.kimmking.rpcfx.demo.api.OrderService"; -// registerService(client, orderService); - - - // 进一步的优化,是在spring加载完成后,从里面拿到特定注解的bean,自动注册到zk - } - - @PreDestroy - public void onExit() { - System.out.println("unregistry all services from zk..."); - + RpcfxResponse response = invoke(); + System.out.println(JSON.toJSONString(response)); } - // 能否去掉name - // - - // annotation - - -// @Bean(name = "io.kimmking.rpcfx.demo.api.UserService") -// public UserService createUserService(){ -// return new UserServiceImpl(); -// } -// -// @Bean(name = "io.kimmking.rpcfx.demo.api.OrderService") -// public OrderService createOrderService(){ -// return new OrderServiceImpl(); -// } - } diff --git a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/UserServiceImpl.java b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/UserServiceImpl.java index a7cc9c5f..ed410cc7 100644 --- a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/UserServiceImpl.java +++ b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/UserServiceImpl.java @@ -1,5 +1,6 @@ package io.kimmking.rpcfx.demo.provider; +import io.kimmking.rpcfx.annotation.RpcfxService; import io.kimmking.rpcfx.demo.api.User; import io.kimmking.rpcfx.demo.api.UserService; import org.springframework.beans.factory.annotation.Autowired; @@ -7,6 +8,7 @@ import org.springframework.stereotype.Component; @Component("io.kimmking.rpcfx.demo.api.UserService") +@RpcfxService public class UserServiceImpl implements UserService { @Autowired From c7c24d0e486272fa45735319e25b5e15d803ff42 Mon Sep 17 00:00:00 2001 From: kimmking Date: Sun, 14 Apr 2024 15:13:17 +0800 Subject: [PATCH 09/13] compelet kk registry integration --- 04fx/spring01/pom.xml | 6 +- .../io/kimmking/spring02/SpringDemo11.java | 49 ++++ 07rpc/rpc01/rpcfx-core/pom.xml | 10 +- .../java/io/kimmking/rpcfx/api/Filter.java | 4 +- .../io/kimmking/rpcfx/api/LoadBalancer.java | 4 +- .../java/io/kimmking/rpcfx/api/Router.java | 4 +- .../io/kimmking/rpcfx/api/RpcContext.java | 20 +- .../io/kimmking/rpcfx/api/RpcfxRequest.java | 2 +- .../rpcfx/consumer/ConsumerBootstrap.java | 66 +++++- .../rpcfx/consumer/RpcfxConsumerInvoker.java | 70 ++++++ .../consumer/RpcfxInvocationHandler.java | 121 ++++++---- .../kimmking/rpcfx/consumer/RpcfxInvoker.java | 91 -------- .../rpcfx/discovery/DiscoveryClient.java | 10 - .../io/kimmking/rpcfx/meta/InstanceMeta.java | 62 +++++ .../io/kimmking/rpcfx/meta/ServiceMeta.java | 25 ++ .../rpcfx/provider/ProviderBootstrap.java | 46 +++- ...Invoker.java => RpcfxProviderInvoker.java} | 10 +- .../rpcfx/registry/ChangedListener.java | 13 ++ .../io/kimmking/rpcfx/registry/Event.java | 29 +++ .../rpcfx/registry/RegistryCenter.java | 62 ++--- .../rpcfx/registry/RegistryConfiguration.java | 23 ++ .../registry/kkregistry/KKHeathChecker.java | 42 ++++ .../registry/kkregistry/KKRegistryCenter.java | 158 +++++++++++++ .../zookeeper/ZookeeperRegistryCenter.java | 101 ++++++++ .../io/kimmking/rpcfx/stub/MockHandler.java | 30 +++ .../rpcfx/stub/StubSkeletonHelper.java | 128 ++++++++-- .../io/kimmking/rpcfx/utils/MethodUtils.java | 9 +- .../io/kimmking/rpcfx/utils/MockUtils.java | 143 ++++++++++++ .../utils/RoundRobinByWeightLoadBalance.java | 219 ++++++++++++++++++ .../src/test/java/FourSumCount.java | 47 ++++ .../kimmking/rpcfx/demo/api/UserService.java | 2 + .../demo/consumer/RpcfxClientApplication.java | 36 +-- .../src/main/resources/application.yml | 10 +- .../demo/provider/RpcfxServerApplication.java | 6 +- .../rpcfx/demo/provider/UserServiceImpl.java | 10 + .../src/main/resources/application.yml | 5 + 36 files changed, 1404 insertions(+), 269 deletions(-) create mode 100644 04fx/spring01/src/main/java/io/kimmking/spring02/SpringDemo11.java create mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/consumer/RpcfxConsumerInvoker.java delete mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/consumer/RpcfxInvoker.java delete mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/discovery/DiscoveryClient.java create mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/meta/InstanceMeta.java create mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/meta/ServiceMeta.java rename 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/provider/{RpcfxInvoker.java => RpcfxProviderInvoker.java} (87%) create mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/ChangedListener.java create mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/Event.java create mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/RegistryConfiguration.java create mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/kkregistry/KKHeathChecker.java create mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/kkregistry/KKRegistryCenter.java create mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/zookeeper/ZookeeperRegistryCenter.java create mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/stub/MockHandler.java create mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/utils/MockUtils.java create mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/utils/RoundRobinByWeightLoadBalance.java create mode 100644 07rpc/rpc01/rpcfx-core/src/test/java/FourSumCount.java diff --git a/04fx/spring01/pom.xml b/04fx/spring01/pom.xml index 505009a7..3075aaf2 100644 --- a/04fx/spring01/pom.xml +++ b/04fx/spring01/pom.xml @@ -10,7 +10,7 @@ - 4.3.29.RELEASE + 4.3.30.RELEASE @@ -19,8 +19,8 @@ org.apache.maven.plugins maven-compiler-plugin - 8 - 8 + 11 + 11 diff --git a/04fx/spring01/src/main/java/io/kimmking/spring02/SpringDemo11.java b/04fx/spring01/src/main/java/io/kimmking/spring02/SpringDemo11.java new file mode 100644 index 00000000..bcbc7e30 --- /dev/null +++ b/04fx/spring01/src/main/java/io/kimmking/spring02/SpringDemo11.java @@ -0,0 +1,49 @@ +package io.kimmking.spring02; + +import org.springframework.cglib.proxy.Enhancer; +import org.springframework.cglib.proxy.MethodInterceptor; +import org.springframework.cglib.proxy.MethodProxy; + +import java.lang.reflect.Method; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2024/1/22 18:01 + */ +public class SpringDemo11 { + + public static void main(String[] args) { + long s = System.currentTimeMillis(); + Enhancer enhancer = new Enhancer(); + enhancer.setInterfaces(new Class[]{IAction.class}); + enhancer.setCallback(new MI()); + enhancer.setUseCache(true); + IAction demo = (IAction) enhancer.create(); + for (int i = 0; i < 5; i++) { + long ss = System.currentTimeMillis(); + System.out.println(demo.action()); + System.out.println( i + " *****====> invoke proxy " + (System.currentTimeMillis() - ss) + " ms"); + } + System.out.println(" *****====> enhancer proxy " + (System.currentTimeMillis() - s) + " ms"); + + } + + public interface IAction { + Object action(); + } + + + static class MI implements MethodInterceptor { + @Override + public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { + long s = System.currentTimeMillis(); + System.out.println(" *****==MI==> " + s + " " +"Before:"+method.getName()); + Object result = "S-" + s;//methodProxy.invokeSuper(obj, objects); + System.out.println(" *****==MI==> " + (System.currentTimeMillis() - s) + " ms After:"+method.getName()); + return result; + } + } + +} diff --git a/07rpc/rpc01/rpcfx-core/pom.xml b/07rpc/rpc01/rpcfx-core/pom.xml index d8b62724..5a1eeac3 100644 --- a/07rpc/rpc01/rpcfx-core/pom.xml +++ b/07rpc/rpc01/rpcfx-core/pom.xml @@ -21,7 +21,7 @@ com.alibaba fastjson - 1.2.70 + 1.2.83 @@ -45,7 +45,7 @@ org.apache.curator - curator-framework + curator-recipes 5.1.0 @@ -70,11 +70,7 @@ - - org.apache.curator - curator-recipes - 5.1.0 - + diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/Filter.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/Filter.java index 64f3b99d..29060ace 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/Filter.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/Filter.java @@ -2,7 +2,9 @@ public interface Filter { - boolean filter(RpcfxRequest request); + RpcfxResponse prefilter(RpcfxRequest request); + + RpcfxResponse postfilter(RpcfxRequest request, RpcfxResponse response); // Filter next(); diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/LoadBalancer.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/LoadBalancer.java index eccb66f5..5ac4fab2 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/LoadBalancer.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/LoadBalancer.java @@ -1,9 +1,11 @@ package io.kimmking.rpcfx.api; +import io.kimmking.rpcfx.meta.InstanceMeta; + import java.util.List; public interface LoadBalancer { - String select(List urls); + InstanceMeta select(List instances); } diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/Router.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/Router.java index 594aeff5..a4ed3225 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/Router.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/Router.java @@ -1,8 +1,10 @@ package io.kimmking.rpcfx.api; +import io.kimmking.rpcfx.meta.InstanceMeta; + import java.util.List; public interface Router { - List route(List urls); + List route(List instances); } diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/RpcContext.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/RpcContext.java index e5365ab7..79a65bde 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/RpcContext.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/RpcContext.java @@ -2,6 +2,7 @@ import io.kimmking.rpcfx.meta.ProviderMeta; import lombok.Getter; +import lombok.Setter; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -17,9 +18,24 @@ public class RpcContext { @Getter - private MultiValueMap providerHolder = new LinkedMultiValueMap<>(); + private final MultiValueMap providerHolder = new LinkedMultiValueMap<>(); @Getter - private Map consumerHolder = new HashMap<>(); + private final Map consumerHolder = new HashMap<>(); + + @Getter + private final Map parameters = new HashMap<>(); + + @Getter + @Setter + private Router router; + + @Getter + @Setter + private LoadBalancer loadBalancer; + + @Getter + @Setter + private Filter[] filters; } diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/RpcfxRequest.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/RpcfxRequest.java index 5ee7b9e1..1e9edfb4 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/RpcfxRequest.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/api/RpcfxRequest.java @@ -5,6 +5,6 @@ @Data public class RpcfxRequest { private String serviceClass; - private String method; + private String methodSign; private Object[] params; } diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/consumer/ConsumerBootstrap.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/consumer/ConsumerBootstrap.java index 16527507..a69fe083 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/consumer/ConsumerBootstrap.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/consumer/ConsumerBootstrap.java @@ -2,16 +2,24 @@ import io.kimmking.rpcfx.annotation.RpcfxReference; import io.kimmking.rpcfx.api.RpcContext; +import io.kimmking.rpcfx.meta.ServiceMeta; +import io.kimmking.rpcfx.registry.RegistryCenter; +import io.kimmking.rpcfx.registry.RegistryConfiguration; import io.kimmking.rpcfx.stub.StubSkeletonHelper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeansException; import org.springframework.beans.PropertyValues; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; +import org.springframework.context.annotation.Import; import org.springframework.stereotype.Component; +import javax.annotation.PostConstruct; import java.io.Closeable; import java.io.IOException; import java.lang.reflect.Field; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @@ -24,12 +32,39 @@ */ @Slf4j @Component +@Import({RegistryConfiguration.class}) public class ConsumerBootstrap implements Closeable, InstantiationAwareBeanPostProcessor { - private RpcContext rpcContext = new RpcContext(); + private RpcContext context = new RpcContext(); private String scanPackage = "io.kimmking"; + @Value("${app.id:app1}") + public String app; + @Value("${app.namespace:public}") + public String ns; + @Value("${app.env:dev}") + public String env; + @Value("${app.mock:false}") + public boolean mock; + @Value("${app.cache:false}") + public boolean cache; + @Value("${app.retry:1}") + public int retry; + + @Autowired + RegistryCenter rc; + + @PostConstruct + public void init() { + this.context.getParameters().put("app.id", app); + this.context.getParameters().put("app.namespace", ns); + this.context.getParameters().put("app.env", env); + this.context.getParameters().put("app.mock", String.valueOf(mock)); + this.context.getParameters().put("app.cache", String.valueOf(cache)); + this.context.getParameters().put("app.retry", String.valueOf(retry)); + } + @Override public void close() throws IOException { @@ -38,14 +73,17 @@ public void close() throws IOException { @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException { if (bean.getClass().getPackage().getName().startsWith(scanPackage)) { - Field[] declaredFields = bean.getClass().getDeclaredFields(); - List consumers = Arrays.stream(declaredFields).filter(field -> field.isAnnotationPresent(RpcfxReference.class)).collect(Collectors.toList()); + Field[] declaredFields = resolveAllField(bean.getClass()); // 解决父类里的注解扫描不到的问题 - consumers.stream().forEach(consumer -> { - Object consumer1 = createConsumer(consumer.getType()); + List consumers = Arrays.stream(declaredFields) + .filter(field -> field.isAnnotationPresent(RpcfxReference.class)) + .collect(Collectors.toList()); + + consumers.stream().forEach(field -> { + Object consumer = createConsumer(field.getType()); try { - consumer.setAccessible(true); - consumer.set(bean, consumer1); + field.setAccessible(true); + field.set(bean, consumer); } catch (IllegalAccessException e) { log.error(e.getMessage(), e); } @@ -54,7 +92,19 @@ public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, Str return null; } + private Field[] resolveAllField(Class aClass) { + List res = new ArrayList<>(20); + while ( !Object.class.equals(aClass) ) { + Field[] fields = aClass.getDeclaredFields(); + res.addAll(Arrays.asList(fields)); + aClass = aClass.getSuperclass(); + } + return res.toArray(new Field[0]); + } + private T createConsumer(Class clazz) { - return StubSkeletonHelper.createConsumer(clazz, rpcContext); + ServiceMeta sm = ServiceMeta.builder().name(clazz.getCanonicalName()) + .app(app).namespace(ns).env(env).build(); + return (T) StubSkeletonHelper.createConsumer(sm, context, rc); } } diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/consumer/RpcfxConsumerInvoker.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/consumer/RpcfxConsumerInvoker.java new file mode 100644 index 00000000..66d9ea89 --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/consumer/RpcfxConsumerInvoker.java @@ -0,0 +1,70 @@ +package io.kimmking.rpcfx.consumer; + + +import com.alibaba.fastjson.parser.ParserConfig; +import io.kimmking.rpcfx.api.*; +import io.kimmking.rpcfx.meta.InstanceMeta; +import io.kimmking.rpcfx.meta.ServiceMeta; +import io.kimmking.rpcfx.registry.RegistryCenter; + +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.List; + +public final class RpcfxConsumerInvoker { + + static { + ParserConfig.getGlobalInstance().addAccept("io.kimmking"); + } + + RpcContext ctx; + + RegistryCenter rc; + + public RpcfxConsumerInvoker(RpcContext ctx, RegistryCenter rc) { + this.ctx = ctx; + this.rc = rc; //"localhost:2181" + } + + public void start() { + this.rc.start(); + } + + public void stop() { + this.rc.stop(); + } + + public T createFromRegistry(final ServiceMeta sm, RpcContext ctx) { + + String service = sm.getName();//"io.kimking.rpcfx.demo.api.UserService"; + System.out.println("====> "+service); + List invokers = new ArrayList<>(); + Class serviceClass = null; + try { + + serviceClass = Class.forName(service); + + List insts = rc.fetchInstances(sm); + if(insts != null && insts.size()>0) invokers.addAll(insts); + rc.subscribe(sm, e -> { + invokers.clear(); + invokers.addAll((List)e.getData()); + }); + + } catch (Exception ex) { + ex.printStackTrace(); + throw new RuntimeException(ex); + } + + return (T) create(serviceClass, invokers, ctx); + + } + + private T create(Class serviceClass, List invokers, RpcContext ctx) { + RpcfxInvocationHandler invocationHandler + = new RpcfxInvocationHandler(serviceClass, invokers, ctx); + return (T) Proxy.newProxyInstance(RpcfxConsumerInvoker.class.getClassLoader(), + new Class[]{serviceClass}, invocationHandler); + } + +} diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/consumer/RpcfxInvocationHandler.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/consumer/RpcfxInvocationHandler.java index 5c755596..0325e0c7 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/consumer/RpcfxInvocationHandler.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/consumer/RpcfxInvocationHandler.java @@ -2,31 +2,32 @@ import com.alibaba.fastjson.JSON; import io.kimmking.rpcfx.api.*; +import io.kimmking.rpcfx.meta.InstanceMeta; import io.kimmking.rpcfx.stub.StubSkeletonHelper; +import io.kimmking.rpcfx.utils.MethodUtils; import okhttp3.*; -import java.io.IOException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; +import java.net.SocketTimeoutException; import java.util.List; import java.util.concurrent.TimeUnit; public class RpcfxInvocationHandler implements InvocationHandler { + public final Object target = new Object(); + public static final MediaType JSONTYPE = MediaType.get("application/json; charset=utf-8"); private final Class serviceClass; - private final List invokers; - private final Router router; - private final LoadBalancer loadBalance; - private final Filter[] filters; + private final List invokers; + + private final RpcContext context; - public RpcfxInvocationHandler(Class serviceClass, List invokers, Router router, LoadBalancer loadBalance, Filter... filters) { + public RpcfxInvocationHandler(Class serviceClass, List invokers, RpcContext ctx) { this.serviceClass = serviceClass; this.invokers = invokers; - this.router = router; - this.loadBalance = loadBalance; - this.filters = filters; + this.context = ctx; } // 可以尝试,自己去写对象序列化,二进制还是文本的,,,rpcfx是xml自定义序列化、反序列化,json: code.google.com/p/rpcfx @@ -36,66 +37,104 @@ public RpcfxInvocationHandler(Class serviceClass, List invokers, @Override public Object invoke(Object proxy, Method method, Object[] params) throws Throwable { + long start = System.currentTimeMillis(); + if (!StubSkeletonHelper.checkRpcMethod(method)){ - return null ; + return method.invoke(target, params); } - List urls = router.route(invokers); + + int retry = 2; + while (retry-- > 0) { + System.out.println("retry:" + retry); + try { + + // check mock, 挡板功能 TODO 3 + + List insts = context.getRouter().route(invokers); // System.out.println("router.route => "); // urls.forEach(System.out::println); - String url = loadBalance.select(urls); // router, loadbalance + InstanceMeta instance = context.getLoadBalancer().select(insts); // router, loadbalance // System.out.println("loadBalance.select => "); // System.out.println("final => " + url); - if (url == null) { - throw new RuntimeException("No available providers from registry center."); - } + if (instance == null) { + throw new RuntimeException("No available providers from registry center."); + } - // 加filter地方之二 - // mock == true, new Student("hubao"); - RpcfxRequest request = new RpcfxRequest(); - request.setServiceClass(this.serviceClass.getName()); - request.setMethod(method.getName()); - request.setParams(params); + RpcfxRequest request = new RpcfxRequest(); + request.setServiceClass(this.serviceClass.getName()); + request.setMethodSign(MethodUtils.methodSign(method)); + request.setParams(params); - if (null!=filters) { - for (Filter filter : filters) { - if (!filter.filter(request)) { - return null; + Filter[] filters = context.getFilters(); + + if (null != filters) { + for (Filter filter : filters) { + RpcfxResponse response = filter.prefilter(request); + if (response != null) { + return JSON.parse(response.getResult().toString()); + } + } } - } - } - RpcfxResponse response = post(request, url); + // 没有控制超时,可能会很久 TODO 2 + RpcfxResponse response = post(request, instance); + + if (null != filters) { + for (Filter filter : filters) { + RpcfxResponse postResponse = filter.postfilter(request, response); + if (postResponse!=null) { + response = postResponse; + } + } + } + + System.out.println("Invoke spend " + (System.currentTimeMillis()-start) + " ms"); + + // 加filter地方之三 + // Student.setTeacher("cuijing"); - // 加filter地方之三 - // Student.setTeacher("cuijing"); + // 这里判断response.status,处理异常 + // 考虑封装一个全局的RpcfxException - // 这里判断response.status,处理异常 - // 考虑封装一个全局的RpcfxException + return JSON.parse(response.getResult().toString()); + + } catch (RuntimeException ex) { + ex.printStackTrace(); + if(! (ex.getCause() instanceof SocketTimeoutException)) { + break; + } + } + } + return null; - return JSON.parse(response.getResult().toString()); } OkHttpClient client = new OkHttpClient.Builder() .connectionPool(new ConnectionPool(128, 60, TimeUnit.SECONDS)) // .dispatcher(dispatcher) -// .readTimeout(httpClientConfig.getReadTimeout(), TimeUnit.SECONDS) -// .writeTimeout(httpClientConfig.getWriteTimeout(), TimeUnit.SECONDS) -// .connectTimeout(httpClientConfig.getConnectTimeout(), TimeUnit.SECONDS) + .readTimeout(1, TimeUnit.SECONDS) + .writeTimeout(1, TimeUnit.SECONDS) + .connectTimeout(1, TimeUnit.SECONDS) .build(); - private RpcfxResponse post(RpcfxRequest req, String url) throws IOException { + private RpcfxResponse post(RpcfxRequest req, InstanceMeta instance) throws Exception { String reqJson = JSON.toJSONString(req); -// System.out.println("req json: "+reqJson); + System.out.println("req json: "+reqJson); final Request request = new Request.Builder() - .url(url) + .url(instance.toString()) .post(RequestBody.create(JSONTYPE, reqJson)) .build(); - String respJson = client.newCall(request).execute().body().string(); -// System.out.println("resp json: "+respJson); + String respJson; + try { + respJson = client.newCall(request).execute().body().string(); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + System.out.println("resp json: "+respJson); return JSON.parseObject(respJson, RpcfxResponse.class); } } \ No newline at end of file diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/consumer/RpcfxInvoker.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/consumer/RpcfxInvoker.java deleted file mode 100644 index 356c7fe3..00000000 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/consumer/RpcfxInvoker.java +++ /dev/null @@ -1,91 +0,0 @@ -package io.kimmking.rpcfx.consumer; - - -import com.alibaba.fastjson.parser.ParserConfig; -import io.kimmking.rpcfx.api.*; -import org.apache.curator.RetryPolicy; -import org.apache.curator.framework.CuratorFramework; -import org.apache.curator.framework.CuratorFrameworkFactory; -import org.apache.curator.framework.recipes.cache.TreeCache; -import org.apache.curator.framework.recipes.cache.TreeCacheEvent; -import org.apache.curator.framework.recipes.cache.TreeCacheListener; -import org.apache.curator.retry.ExponentialBackoffRetry; - -import java.lang.reflect.Proxy; -import java.util.ArrayList; -import java.util.List; - -public final class RpcfxInvoker { - - static { - ParserConfig.getGlobalInstance().addAccept("io.kimmking"); - } - CuratorFramework client; - String zkUrl = null; - - public RpcfxInvoker(String zkUrl) { - this.zkUrl = zkUrl; //"localhost:2181" - this.start(); - } - - public void start() { - RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); - client = CuratorFrameworkFactory.builder().connectString(this.zkUrl).namespace("rpcfx").retryPolicy(retryPolicy).build(); - client.start(); - } - - public void stop() { - client.close(); - } - - public T createFromRegistry(final Class serviceClass, Router router, LoadBalancer loadBalance, Filter filter) { - - String service = serviceClass.getCanonicalName();//"io.kimking.rpcfx.demo.api.UserService"; - System.out.println("====> "+service); - List invokers = new ArrayList<>(); - - try { - - if ( null == client.checkExists().forPath("/" + service)) { - return null; - } - - fetchInvokers(client, service, invokers); - - final TreeCache treeCache = TreeCache.newBuilder(client, "/" + service).setCacheData(true).setMaxDepth(2).build(); - treeCache.getListenable().addListener(new TreeCacheListener() { - public void childEvent(CuratorFramework curatorFramework, TreeCacheEvent treeCacheEvent) throws Exception { - System.out.println("treeCacheEvent: "+treeCacheEvent); - fetchInvokers(client, service, invokers); - } - }); - treeCache.start(); - - } catch (Exception ex) { - ex.printStackTrace(); - } - - return (T) create(serviceClass, invokers, router, loadBalance, filter); - - } - - - - private void fetchInvokers(CuratorFramework client, String service, List invokers) throws Exception { - List services = client.getChildren().forPath("/" + service); - invokers.clear(); - for (String svc : services) { - System.out.println(svc); - String url = svc.replace("_", ":"); - invokers.add("http://" + url); - } - } - - private T create(Class serviceClass, List invokers, Router router, LoadBalancer loadBalance, Filter... filters) { - RpcfxInvocationHandler invocationHandler - = new RpcfxInvocationHandler(serviceClass, invokers, router, loadBalance, filters); - return (T) Proxy.newProxyInstance(RpcfxInvoker.class.getClassLoader(), - new Class[]{serviceClass}, invocationHandler); - } - -} diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/discovery/DiscoveryClient.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/discovery/DiscoveryClient.java deleted file mode 100644 index 46f89433..00000000 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/discovery/DiscoveryClient.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.kimmking.rpcfx.discovery; - -/** - * Description for this class. - * - * @Author : kimmking(kimmking@apache.org) - * @create 2024/1/13 20:17 - */ -public class DiscoveryClient { -} diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/meta/InstanceMeta.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/meta/InstanceMeta.java new file mode 100644 index 00000000..0fae8699 --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/meta/InstanceMeta.java @@ -0,0 +1,62 @@ +package io.kimmking.rpcfx.meta; + +import com.google.common.base.Strings; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.net.URI; +import java.util.Map; +import java.util.Objects; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2024/2/8 19:46 + */ + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InstanceMeta { + + private String scheme; + private String ip; + private Integer port; + private String context; + private String status; + private Map metadata; + + public static InstanceMeta from(String instance) { + URI uri = URI.create(instance); + String path = uri.getPath(); + path = Strings.isNullOrEmpty(path) ? "" : path.substring(1); + return InstanceMeta.builder() + .scheme(uri.getScheme()) + .ip(uri.getHost()) + .port(uri.getPort()) + .context(path) + .build(); + } + + @Override + public String toString() { + return scheme + "://" + ip + ":" + port + "/" + context; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + InstanceMeta that = (InstanceMeta) o; + return Objects.equals(scheme, that.scheme) && Objects.equals(ip, that.ip) && Objects.equals(port, that.port) && Objects.equals(context, that.context); + } + + @Override + public int hashCode() { + return Objects.hash(scheme, ip, port, context); + } +} diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/meta/ServiceMeta.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/meta/ServiceMeta.java new file mode 100644 index 00000000..c443a272 --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/meta/ServiceMeta.java @@ -0,0 +1,25 @@ +package io.kimmking.rpcfx.meta; + +import lombok.Builder; +import lombok.Data; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2024/2/8 19:46 + */ +@Data +@Builder +public class ServiceMeta { + + private String app; + private String namespace; + private String env; + private String name; + + @Override + public String toString() { + return String.format("%s_%s_%s_%s", app, namespace, env, name); + } +} \ No newline at end of file diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/provider/ProviderBootstrap.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/provider/ProviderBootstrap.java index 5b541a34..6fa28882 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/provider/ProviderBootstrap.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/provider/ProviderBootstrap.java @@ -2,14 +2,19 @@ import io.kimmking.rpcfx.annotation.RpcfxService; import io.kimmking.rpcfx.api.RpcContext; +import io.kimmking.rpcfx.meta.InstanceMeta; +import io.kimmking.rpcfx.meta.ServiceMeta; import io.kimmking.rpcfx.registry.RegistryCenter; +import io.kimmking.rpcfx.registry.RegistryConfiguration; import io.kimmking.rpcfx.stub.StubSkeletonHelper; import lombok.Getter; import lombok.SneakyThrows; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.ApplicationRunner; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.Order; import org.springframework.core.env.Environment; @@ -29,6 +34,7 @@ */ @Component +@Import({RegistryConfiguration.class}) public class ProviderBootstrap { @Autowired @@ -37,16 +43,24 @@ public class ProviderBootstrap { @Autowired Environment environment; - private RpcContext rpcContext = new RpcContext(); + @Value("${app.id:app1}") + public String app; + @Value("${app.namespace:public}") + public String ns; + @Value("${app.env:dev}") + public String env; + + private final RpcContext context = new RpcContext(); @Getter - private RpcfxInvoker invoker; + private final RpcfxProviderInvoker invoker = new RpcfxProviderInvoker(context);; - private static String PROTO = "http"; + private static String SCHEME = "http"; private static String ip; private static int port; - RegistryCenter registry = new RegistryCenter(); + @Autowired + RegistryCenter registry;// = new KKRegistryCenter(); @SneakyThrows @PostConstruct @@ -77,8 +91,7 @@ private void buildProvider() { } private void createProvider(Class clazz, Object bean) { - StubSkeletonHelper.createProvider(clazz, bean, rpcContext); // 初始化了holder - this.invoker = new RpcfxInvoker(rpcContext); + StubSkeletonHelper.createProvider(clazz, bean, context); // 初始化了holder } @Order(Integer.MIN_VALUE) @@ -91,13 +104,18 @@ private void registerServices() { registry.start(); - System.out.println("registry all services from zk..."); - rpcContext.getProviderHolder().forEach( (x,y) -> + System.out.println("registry all services from RegistryCenter..."); + context.getProviderHolder().forEach( (x, y) -> { System.out.println(" register " + x); + ServiceMeta sm = ServiceMeta.builder().name(x) + .app(app).namespace(ns).env(env).build(); + InstanceMeta im = InstanceMeta.builder() + .scheme(SCHEME).ip(ip).port(port).context("").build(); try { - registry.registerService(x, ip, port); + registry.registerService(sm, im); + registry.heartbeat(sm, im); } catch (Exception e) { throw new RuntimeException(e); } @@ -111,12 +129,16 @@ public void stop() { } private void unregisterServices() { - System.out.println("unregistry all services from zk..."); - rpcContext.getProviderHolder().forEach( (x,y) -> + System.out.println("unregistry all services from RegistryCenter..."); + context.getProviderHolder().forEach( (x, y) -> { System.out.println(" unregister " + x); + ServiceMeta sm = ServiceMeta.builder().name(x) + .app(app).namespace(ns).env(env).build(); + InstanceMeta im = InstanceMeta.builder() + .scheme(SCHEME).ip(ip).port(port).context("").build(); try { - registry.unregisterService(x, ip, port); + registry.unregisterService(sm, im); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/provider/RpcfxInvoker.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/provider/RpcfxProviderInvoker.java similarity index 87% rename from 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/provider/RpcfxInvoker.java rename to 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/provider/RpcfxProviderInvoker.java index 4767e046..6842dfe6 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/provider/RpcfxInvoker.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/provider/RpcfxProviderInvoker.java @@ -13,11 +13,11 @@ import java.util.List; import java.util.Optional; -public class RpcfxInvoker { +public class RpcfxProviderInvoker { RpcContext context; - public RpcfxInvoker(RpcContext context) { + public RpcfxProviderInvoker(RpcContext context) { this.context = context; } @@ -25,10 +25,11 @@ public RpcfxResponse invoke(RpcfxRequest request) { RpcfxResponse response = new RpcfxResponse(); String serviceClass = request.getServiceClass(); - ProviderMeta meta = findProvider(serviceClass, request.getMethod()); + ProviderMeta meta = findProvider(serviceClass, request.getMethodSign()); try { Method method = meta.getMethod(); + // 没有控制超时,所以可能会很久 TODO 1 Object result = method.invoke(meta.getServiceImpl(), request.getParams()); // dubbo, fastjson, // 两次json序列化能否合并成一个 response.setResult(JSON.toJSONString(result, SerializerFeature.WriteClassName)); @@ -50,7 +51,8 @@ public RpcfxResponse invoke(RpcfxRequest request) { protected ProviderMeta findProvider(String interfaceName, String methodSign) { List providerMetas = context.getProviderHolder().get(interfaceName); if (!CollectionUtils.isEmpty(providerMetas)) { - Optional providerMeta = providerMetas.stream().filter(provider -> methodSign.equals(provider.getMethodSign())).findFirst(); + Optional providerMeta = providerMetas.stream() + .filter(provider -> methodSign.equals(provider.getMethodSign())).findFirst(); return providerMeta.orElse(null); } return null; diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/ChangedListener.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/ChangedListener.java new file mode 100644 index 00000000..971cbcda --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/ChangedListener.java @@ -0,0 +1,13 @@ +package io.kimmking.rpcfx.registry; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2024/2/8 20:19 + */ +public interface ChangedListener { + + void fireEvent(Event e); + +} diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/Event.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/Event.java new file mode 100644 index 00000000..54082e15 --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/Event.java @@ -0,0 +1,29 @@ +package io.kimmking.rpcfx.registry; + +import io.kimmking.rpcfx.meta.InstanceMeta; +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.util.List; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2024/2/8 20:20 + */ +public interface Event { + + T getData(); + + static Event withData(List list) { + return new ChangedEvent(list); + } + + @Data + @AllArgsConstructor + class ChangedEvent implements Event> { + List data; + } + +} diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/RegistryCenter.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/RegistryCenter.java index f7ec83bf..93d86519 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/RegistryCenter.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/RegistryCenter.java @@ -1,57 +1,35 @@ package io.kimmking.rpcfx.registry; import io.kimmking.rpcfx.api.ServiceProviderDesc; +import io.kimmking.rpcfx.meta.InstanceMeta; +import io.kimmking.rpcfx.meta.ServiceMeta; import org.apache.curator.RetryPolicy; -import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.zookeeper.CreateMode; +import java.util.List; + /** * Description for this class. * * @Author : kimmking(kimmking@apache.org) - * @create 2024/1/13 20:16 + * @create 2024/2/8 15:23 */ -public class RegistryCenter { - - CuratorFramework client = null; - public void start() { - RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); - client = CuratorFrameworkFactory.builder().connectString("localhost:2181").namespace("rpcfx").retryPolicy(retryPolicy).build(); - client.start(); - } - - public void stop(){ - client.close(); - } - - public void registerService(String service, String host, int port) throws Exception { - ServiceProviderDesc userServiceSesc = ServiceProviderDesc.builder() - .host(host) - .port(port).serviceClass(service).build(); - // String userServiceSescJson = JSON.toJSONString(userServiceSesc); - - try { - if ( null == client.checkExists().forPath("/" + service)) { - client.create().withMode(CreateMode.PERSISTENT).forPath("/" + service, "service".getBytes()); - } - } catch (Exception ex) { - ex.printStackTrace(); - } - - client.create().withMode(CreateMode.EPHEMERAL). - forPath( "/" + service + "/" + userServiceSesc.getHost() + "_" + userServiceSesc.getPort(), "provider".getBytes()); - } - - public void unregisterService(String service, String host, int port) throws Exception { - - if (null == client.checkExists().forPath("/" + service)) { - return; - } - System.out.println("delete " + "/" + service + "/" + host + "_" + port); - client.delete().quietly(). - forPath( "/" + service + "/" + host + "_" + port); - } +public interface RegistryCenter { + + void start(); + + void stop(); + + void registerService(ServiceMeta service, InstanceMeta instance) throws Exception; + + void unregisterService(ServiceMeta service, InstanceMeta instance) throws Exception; + + List fetchInstances(ServiceMeta service) throws Exception; + + void subscribe(ServiceMeta service, ChangedListener listener); + + void heartbeat(ServiceMeta service, InstanceMeta instance); } diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/RegistryConfiguration.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/RegistryConfiguration.java new file mode 100644 index 00000000..5aa67ec1 --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/RegistryConfiguration.java @@ -0,0 +1,23 @@ +package io.kimmking.rpcfx.registry; + +import io.kimmking.rpcfx.registry.kkregistry.KKRegistryCenter; +import io.kimmking.rpcfx.registry.zookeeper.ZookeeperRegistryCenter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2024/2/9 01:05 + */ + +@Configuration +public class RegistryConfiguration { + + @Bean + RegistryCenter createRC() { + return new ZookeeperRegistryCenter(); //KKRegistryCenter(); + } + +} diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/kkregistry/KKHeathChecker.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/kkregistry/KKHeathChecker.java new file mode 100644 index 00000000..bbf0e17e --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/kkregistry/KKHeathChecker.java @@ -0,0 +1,42 @@ +package io.kimmking.rpcfx.registry.kkregistry; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2024/2/1 06:18 + */ +public class KKHeathChecker { + + final int interval = 5_000; + + final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); + static final DateTimeFormatter DTF = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss"); + + public void check(Callback callback) { + executor.scheduleWithFixedDelay(() -> { + System.out.println("start to check kk health ...[" + DTF.format(LocalDateTime.now()) + "]"); + try { + callback.call(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }, interval, interval, TimeUnit.MILLISECONDS); + } + + public void stop() { + this.executor.shutdown(); + } + + public interface Callback { + void call() throws Exception; + } + + +} diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/kkregistry/KKRegistryCenter.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/kkregistry/KKRegistryCenter.java new file mode 100644 index 00000000..93171162 --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/kkregistry/KKRegistryCenter.java @@ -0,0 +1,158 @@ +package io.kimmking.rpcfx.registry.kkregistry; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +import io.kimmking.rpcfx.meta.InstanceMeta; +import io.kimmking.rpcfx.meta.ServiceMeta; +import io.kimmking.rpcfx.registry.ChangedListener; +import io.kimmking.rpcfx.registry.Event; +import io.kimmking.rpcfx.registry.RegistryCenter; +import okhttp3.ConnectionPool; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static io.kimmking.rpcfx.consumer.RpcfxInvocationHandler.JSONTYPE; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2024/2/8 15:25 + */ +public class KKRegistryCenter implements RegistryCenter { + + private Map TV = new HashMap<>(); + + OkHttpClient client; + @Override + public void start() { + client = new OkHttpClient.Builder() + .connectionPool(new ConnectionPool(128, 60, TimeUnit.SECONDS)) +// .dispatcher(dispatcher) + .readTimeout(65, TimeUnit.SECONDS) + .writeTimeout(65, TimeUnit.SECONDS) + .connectTimeout(3, TimeUnit.SECONDS) + .build(); + } + + @Override + public void stop() { + this.checker.stop(); + } + + @Override + public void registerService(ServiceMeta service, InstanceMeta instance) throws Exception { + String reqJson = "{\n" + + " \"scheme\": \"http\",\n" + + " \"ip\": \"" + instance.getIp() + "\",\n" + + " \"port\": \"" + instance.getPort() + "\",\n" + + " \"context\": \"\",\n" + + " \"status\": \"online\",\n" + + " \"metadata\": {\n" + + " \"env\": \"dev\",\n" + + " \"tag\": \"RED\"\n" + + " }\n" + + "}"; + final Request request = new Request.Builder() + .url("http://localhost:8484/reg?service=" + service) + .post(RequestBody.create(JSONTYPE, reqJson)) + .build(); + String respJson = client.newCall(request).execute().body().string(); + System.out.println(respJson); + } + + @Override + public void unregisterService(ServiceMeta service, InstanceMeta instance) throws Exception { + String reqJson = "{\n" + + " \"scheme\": \"http\",\n" + + " \"ip\": \"" + instance.getIp() + "\",\n" + + " \"port\": \"" + instance.getPort() + "\",\n" + + " \"context\": \"\"\n" + + "}"; + final Request request = new Request.Builder() + .url("http://localhost:8484/unreg?service=" + service) + .post(RequestBody.create(JSONTYPE, reqJson)) + .build(); + String respJson = client.newCall(request).execute().body().string(); + System.out.println(respJson); + } + + public List fetchInstances(ServiceMeta service) throws Exception { + final Request request = new Request.Builder() + .url("http://localhost:8484/list?service=" + service) + .get() + .build(); + String respJson = client.newCall(request).execute().body().string(); + System.out.println(respJson); + List instances = JSON.parseObject(respJson, new TypeReference>() { + }); + return instances; + } + + KKHeathChecker checker = new KKHeathChecker(); + + // for Consumer + public void subscribe(ServiceMeta service, final ChangedListener listener) { + + checker.check( () -> { + if(hb(service, listener)) { + List instances = fetchInstances(service); + Event e = Event.withData(instances); + listener.fireEvent(e); + } + }); + + // 定时器轮询 + // 保存上一次的TV + // 如果有差异就fire + } + + private boolean hb(ServiceMeta service, ChangedListener listener) throws Exception { + String svc = service.toString(); + final Request request = new Request.Builder() + .url("http://localhost:8484/hb?service=" + svc) + .get() + .build(); + String respJson = client.newCall(request).execute().body().string(); + System.out.println("hb:"+respJson); + Long v = Long.valueOf(respJson); + Long o = TV.getOrDefault(svc, -1L); + if ( v > o) { + TV.put(svc, v); + return o > -1L; + } + return false; + } + + + // for Provider + public void heartbeat(ServiceMeta service, InstanceMeta instance) { + checker.check( () -> { + heart(service, instance); + }); + } + + Long heart(ServiceMeta service, InstanceMeta instance) throws Exception { + String reqJson = "{\n" + + " \"scheme\": \"http\",\n" + + " \"ip\": \"" + instance.getIp() + "\",\n" + + " \"port\": \"" + instance.getPort() + "\",\n" + + " \"context\": \"\",\n" + + " \"status\": \"online\"\n" + + "}"; + final Request request = new Request.Builder() + .url("http://localhost:8484/heart?service=" + service) + .post(RequestBody.create(JSONTYPE, reqJson)) + .build(); + String respJson = client.newCall(request).execute().body().string(); + System.out.println("heart:"+respJson); + return Long.valueOf(respJson); + } + +} diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/zookeeper/ZookeeperRegistryCenter.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/zookeeper/ZookeeperRegistryCenter.java new file mode 100644 index 00000000..784a9b80 --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/zookeeper/ZookeeperRegistryCenter.java @@ -0,0 +1,101 @@ +package io.kimmking.rpcfx.registry.zookeeper; + +import io.kimmking.rpcfx.api.ServiceProviderDesc; +import io.kimmking.rpcfx.meta.InstanceMeta; +import io.kimmking.rpcfx.meta.ServiceMeta; +import io.kimmking.rpcfx.registry.ChangedListener; +import io.kimmking.rpcfx.registry.Event; +import io.kimmking.rpcfx.registry.RegistryCenter; +import org.apache.curator.RetryPolicy; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.framework.recipes.cache.TreeCache; +import org.apache.curator.retry.ExponentialBackoffRetry; +import org.apache.zookeeper.CreateMode; + +import java.util.ArrayList; +import java.util.List; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2024/1/13 20:16 + */ +public class ZookeeperRegistryCenter implements RegistryCenter { + +// private final List listeners = new ArrayList<>(); +// public void addListener(ChangedListener listener) { +// this.listeners.add(listener); +// } + + CuratorFramework client = null; + public void start() { + RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); + client = CuratorFrameworkFactory.builder().connectString("localhost:2181").namespace("rpcfx").retryPolicy(retryPolicy).build(); + client.start(); + } + + public void stop(){ + client.close(); + } + + public void registerService(ServiceMeta service, InstanceMeta instance) throws Exception { + ServiceProviderDesc userServiceSesc = ServiceProviderDesc.builder() + .host(instance.getIp()) + .port(instance.getPort()).serviceClass(service.getName()).build(); + // String userServiceSescJson = JSON.toJSONString(userServiceSesc); + + try { + if ( null == client.checkExists().forPath("/" + service)) { + client.create().withMode(CreateMode.PERSISTENT).forPath("/" + service, "service".getBytes()); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + + client.create().withMode(CreateMode.EPHEMERAL). + forPath( "/" + service + "/" + userServiceSesc.getHost() + "_" + userServiceSesc.getPort(), "provider".getBytes()); + } + + public void unregisterService(ServiceMeta service, InstanceMeta instance) throws Exception { + + if (null == client.checkExists().forPath("/" + service)) { + return; + } + System.out.println("delete " + "/" + service + "/" + instance.getIp() + "_" + instance.getPort()); + client.delete().quietly(). + forPath( "/" + service + "/" + instance.getIp() + "_" + instance.getPort()); + } + + public List fetchInstances(ServiceMeta service) throws Exception { + List services = client.getChildren().forPath("/" + service); + List instances = new ArrayList<>(); + for (String svc : services) { + System.out.println(svc); + String url = svc.replace("_", ":"); + instances.add(InstanceMeta.from("http://" + url)); + } + return instances; + } + + public void subscribe(ServiceMeta service, ChangedListener listener) { + final TreeCache treeCache = TreeCache.newBuilder(client, "/" + service).setCacheData(true).setMaxDepth(2).build(); + treeCache.getListenable().addListener((curatorFramework, treeCacheEvent) -> { + System.out.println("treeCacheEvent: "+treeCacheEvent); + List instances = fetchInstances(service); + listener.fireEvent(Event.withData(instances)); + }); + try { + treeCache.start(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public void heartbeat(ServiceMeta service, InstanceMeta instance) { + // do nothing + } + +} diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/stub/MockHandler.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/stub/MockHandler.java new file mode 100644 index 00000000..9fe5269c --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/stub/MockHandler.java @@ -0,0 +1,30 @@ +package io.kimmking.rpcfx.stub; + +import io.kimmking.rpcfx.utils.MockUtils; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2024/2/11 02:57 + */ +public class MockHandler implements InvocationHandler { + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + Class type = method.getReturnType(); + System.out.println("invoke by mock handler..."); + return MockUtils.mock(type, null); + } + + public static T createMock(Class serviceClass) { + //final ServiceMeta sm, Router router, LoadBalancer loadBalance, Filter filter) { + return (T) Proxy.newProxyInstance(MockHandler.class.getClassLoader(), + new Class[]{serviceClass}, new MockHandler()); + + } +} diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/stub/StubSkeletonHelper.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/stub/StubSkeletonHelper.java index ebe5f7ab..e77215a8 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/stub/StubSkeletonHelper.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/stub/StubSkeletonHelper.java @@ -1,14 +1,18 @@ package io.kimmking.rpcfx.stub; import io.kimmking.rpcfx.api.*; -import io.kimmking.rpcfx.consumer.RpcfxInvoker; +import io.kimmking.rpcfx.consumer.RpcfxConsumerInvoker; +import io.kimmking.rpcfx.meta.InstanceMeta; import io.kimmking.rpcfx.meta.ProviderMeta; +import io.kimmking.rpcfx.meta.ServiceMeta; +import io.kimmking.rpcfx.registry.RegistryCenter; +import io.kimmking.rpcfx.utils.MethodUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.util.MultiValueMap; import java.lang.reflect.Method; -import java.util.List; -import java.util.Random; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; /** * @author lirui @@ -32,7 +36,7 @@ public static void createProvider(Class clazz, Object serviceImpl, RpcContext } private static ProviderMeta buildProviderMeta(Method method, Object serviceImpl) { - String methodSign = method.getName();//MethodUtils.methodSign(method); + String methodSign = MethodUtils.methodSign(method); ProviderMeta providerMeta = new ProviderMeta(); providerMeta.setMethod(method); providerMeta.setServiceImpl(serviceImpl); @@ -54,43 +58,129 @@ public static boolean checkRpcMethod(final Method method) { return true; } - public static T createConsumer(Class clazz, RpcContext rpcContext) { - String clazzName = clazz.getName(); - T proxyHandler = (T) rpcContext.getConsumerHolder().get(clazzName); + public static T createConsumer(ServiceMeta sm, RpcContext ctx, RegistryCenter rc) { + String clazzName = sm.getName(); + Class serviceClass = null; + try { + serviceClass = Class.forName(clazzName); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + T proxyHandler = (T) ctx.getConsumerHolder().get(clazzName); if (proxyHandler == null) { // TODO configuration - proxyHandler = new RpcfxInvoker("localhost:2181") - .createFromRegistry(clazz, new TagRouter(), - new RandomLoadBalancer(), new CuicuiFilter()); - rpcContext.getConsumerHolder().put(clazzName, proxyHandler); + + ctx.setRouter(new TagRouter()); + ctx.setLoadBalancer(new RoundRibbonLoadBalancer()); + ctx.setFilters(createFilters(ctx)); + + T mockHandler = createMockHandler(ctx, serviceClass); + if(mockHandler != null) { + return mockHandler; + } + + RpcfxConsumerInvoker consumerInvoker = new RpcfxConsumerInvoker(ctx, rc); + consumerInvoker.start(); + proxyHandler = consumerInvoker.createFromRegistry(sm, ctx); + ctx.getConsumerHolder().put(clazzName, proxyHandler); + } + return proxyHandler; + } + + private static Filter[] createFilters(RpcContext ctx) { + String cache = ctx.getParameters().getOrDefault("app.cache", "false"); + Filter[] filters = null; + if("true".equalsIgnoreCase(cache)) { + filters = new Filter[]{new CuicuiFilter(), new CacheFilter()}; + } else { + filters = new Filter[]{new CuicuiFilter()}; } - return (T) proxyHandler; + return filters; + } + + private static T createMockHandler(RpcContext ctx, Class serviceClass) { + String mock = ctx.getParameters().getOrDefault("app.mock", "false"); + if("true".equalsIgnoreCase(mock)) { + return (T) MockHandler.createMock(serviceClass); + } + return null; } private static class TagRouter implements Router { @Override - public List route(List urls) { - return urls; + public List route(List instances) { + return instances; + } + } + + private static class RoundRibbonLoadBalancer implements LoadBalancer { + private final AtomicInteger count = new AtomicInteger(0); + @Override + public InstanceMeta select(List instances) { + if(instances.isEmpty()) return null; + return instances.get((count.getAndIncrement() & Integer.MAX_VALUE) % instances.size()); } } private static class RandomLoadBalancer implements LoadBalancer { private final Random random = new Random(); @Override - public String select(List urls) { - if(urls.isEmpty()) return null; - return urls.get(random.nextInt(urls.size())); + public InstanceMeta select(List instances) { + if(instances.isEmpty()) return null; + return instances.get(random.nextInt(instances.size())); } } @Slf4j private static class CuicuiFilter implements Filter { @Override - public boolean filter(RpcfxRequest request) { + public RpcfxResponse prefilter(RpcfxRequest request) { + //log.info("filter {} -> {}", this.getClass().getName(), request.toString()); + //System.out.printf("filter %s -> %s%n", this.getClass().getName(), request.toString()); + return null; + } + + @Override + public RpcfxResponse postfilter(RpcfxRequest request, RpcfxResponse response) { + return response; + } + + } + + private static class CacheFilter implements Filter { + + static Map CACHE = new HashMap<>(); + + @Override + public RpcfxResponse prefilter(RpcfxRequest request) { + RpcfxResponse response = CACHE.get(genKey(request)); + if(response != null) { + System.out.println("CacheFilter.prefilter hit! => request: \n" + request + "\n =>response: \n" + response); + } + return response; //log.info("filter {} -> {}", this.getClass().getName(), request.toString()); //System.out.printf("filter %s -> %s%n", this.getClass().getName(), request.toString()); - return true; } + + @Override + public RpcfxResponse postfilter(RpcfxRequest request, RpcfxResponse response) { + String key = genKey(request); + if(!CACHE.containsKey(key)) { + CACHE.put(key, response); + } + return response; + } + + } + + public static String genKey(RpcfxRequest request) { + StringBuilder sb = new StringBuilder(); + sb.append(request.getServiceClass()); + sb.append("@"); + sb.append(request.getMethodSign()); + //sb.append(""); + Arrays.stream(request.getParams()).forEach(x -> sb.append("_"+x.toString())); + return sb.toString(); } } diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/utils/MethodUtils.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/utils/MethodUtils.java index 8158ccff..f17a102f 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/utils/MethodUtils.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/utils/MethodUtils.java @@ -9,10 +9,10 @@ public class MethodUtils { public static String methodSign(Method method) { if (method != null) { - StringBuilder builder = new StringBuilder("method:"); + StringBuilder builder = new StringBuilder(); String name = method.getName(); builder.append(name); - builder.append("_"); + builder.append("@"); int count = method.getParameterCount(); builder.append(count); builder.append("_"); @@ -20,8 +20,9 @@ public static String methodSign(Method method) { Class[] classes = method.getParameterTypes(); Arrays.stream(classes).forEach(c -> builder.append(c.getName() + ",")); } - String string = builder.toString(); - return DigestUtils.md5DigestAsHex(string.getBytes()); + return builder.toString(); +// String string = builder.toString(); +// return DigestUtils.md5DigestAsHex(string.getBytes()); } return ""; } diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/utils/MockUtils.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/utils/MockUtils.java new file mode 100644 index 00000000..8d22536a --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/utils/MockUtils.java @@ -0,0 +1,143 @@ +package io.kimmking.rpcfx.utils; + +import lombok.Data; +import org.springframework.util.ClassUtils; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.*; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2024/2/11 03:15 + */ +public class MockUtils { + + public static Object mock(Class clazz, Type[] generics) { + boolean primitiveOrWrapper = ClassUtils.isPrimitiveOrWrapper(clazz); + if(primitiveOrWrapper) return mockPrimitive(clazz); + if(String.class.equals(clazz)) return mockString(); + if (Number.class.isAssignableFrom(clazz)) { + return 10; + } + if(clazz.isArray()) { + return mockArray(clazz.getComponentType()); + } + if(List.class.isAssignableFrom(clazz)) { + return mockList(clazz, generics[0]); + } + if(Map.class.isAssignableFrom(clazz)) { + return mockMap(clazz, generics[1]); + } + return mockPojo(clazz); + } + + private static Object mockMap(Class clazz, Type generic) { + HashMap map = new HashMap<>(); + map.put("a", mock((Class)generic, null)); + map.put("b", mock((Class)generic, null)); + return map; + } + + private static Object mockList(Class clazz, Type generic) { + List list = new ArrayList<>(); + list.add(mock((Class)generic, null)); + list.add(mock((Class)generic, null)); + return list; + } + + private static Object mockArray(Class clazz) { + Object array = Array.newInstance(clazz, 2); + Array.set(array, 0, mock(clazz, null)); + Array.set(array, 1, mock(clazz,null)); + return array; + } + + private static Object mockPojo(Class clazz) { + try { + Object object = clazz.getDeclaredConstructor().newInstance(); + Field[] fields = clazz.getDeclaredFields(); + for (Field f : fields) { + f.setAccessible(true); + Type genericType = f.getGenericType(); +// System.out.println(f.getGenericType()); +// System.out.println(f.getType()); + if (genericType instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) genericType; + Type[] typeArguments = parameterizedType.getActualTypeArguments(); + System.out.println("genericType="+Arrays.toString(typeArguments)); + f.set(object, mock(f.getType(), typeArguments)); + } else { + f.set(object, mock(f.getType(),null)); + } + } + return object; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + private static Object mockString() { + return "this_is_a_mock_string"; + } + + private static Object mockPrimitive(Class clazz) { + + if (Boolean.class.equals(clazz)) { + return true; + } + + return 1; + } + + public static void main(String[] args) { + + System.out.println(mock(ListPojo.class,null)); + + +// System.out.println(mock(Byte.class)); +// System.out.println(mock(Character.class)); +// System.out.println(mock(Boolean.class)); +// System.out.println(mock(Integer.class)); +// System.out.println(mock(Float.class)); +// System.out.println(mock(Short.class)); +// System.out.println(mock(Long.class)); +// System.out.println(mock(Double.class)); +// System.out.println(mock(BigInteger.class)); +// System.out.println(mock(BigDecimal.class)); +// System.out.println(mock(String.class)); + +// System.out.println(mock(Pojo.class)); + +// Arrays.stream(((Pojo[]) mock(new Pojo[]{}.getClass()))).forEach(System.out::println); + + } + + + @Data + public static class Pojo { + private int id; + private String name; + private float amount; + private InnerPojo inner; + } + + @Data + public static class InnerPojo { + private int value; + private String key; + } + + @Data + public static class ListPojo { + private List list; + private Integer inner; + private Map map; + } + +} diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/utils/RoundRobinByWeightLoadBalance.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/utils/RoundRobinByWeightLoadBalance.java new file mode 100644 index 00000000..6ada40ed --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/utils/RoundRobinByWeightLoadBalance.java @@ -0,0 +1,219 @@ +package io.kimmking.rpcfx.utils; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2024/2/13 23:44 + */ + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by caojun on 2018/2/20. + * + * 基本概念: + * weight: 配置文件中指定的该后端的权重,这个值是固定不变的。 + * effective_weight: 后端的有效权重,初始值为weight。 + * 在释放后端时,如果发现和后端的通信过程中发生了错误,就减小effective_weight。 + * 此后有新的请求过来时,在选取后端的过程中,再逐步增加effective_weight,最终又恢复到weight。 + * 之所以增加这个字段,是为了当后端发生错误时,降低其权重。 + * current_weight: + * 后端目前的权重,一开始为0,之后会动态调整。那么是怎么个动态调整呢? + * 每次选取后端时,会遍历集群中所有后端,对于每个后端,让它的current_weight增加它的effective_weight, + * 同时累加所有后端的effective_weight,保存为total。 + * 如果该后端的current_weight是最大的,就选定这个后端,然后把它的current_weight减去total。 + * 如果该后端没有被选定,那么current_weight不用减小。 + * + * 算法逻辑: + * 1. 对于每个请求,遍历集群中的所有可用后端,对于每个后端peer执行: + *     peer->current_weight += peer->effecitve_weight。 + *     同时累加所有peer的effective_weight,保存为total。 + * 2. 从集群中选出current_weight最大的peer,作为本次选定的后端。 + * 3. 对于本次选定的后端,执行:peer->current_weight -= total。 + * + */ +public class RoundRobinByWeightLoadBalance { + + //约定的invoker和权重的键值对 + final private List nodes; + + public RoundRobinByWeightLoadBalance(Map invokersWeight){ + if (invokersWeight != null && !invokersWeight.isEmpty()) { + nodes = new ArrayList<>(invokersWeight.size()); + invokersWeight.forEach((invoker, weight)->nodes.add(new Node(invoker, weight))); + }else + nodes = null; + } + + /** + * 算法逻辑: + * 1. 对于每个请求,遍历集群中的所有可用后端,对于每个后端peer执行: + *     peer->current_weight += peer->effecitve_weight。 + *     同时累加所有peer的effective_weight,保存为total。 + * 2. 从集群中选出current_weight最大的peer,作为本次选定的后端。 + * 3. 对于本次选定的后端,执行:peer->current_weight -= total。 + * + * @Return ivoker + */ + public Invoker select(){ + if (! checkNodes()) + return null; + else if (nodes.size() == 1) { + if (nodes.get(0).invoker.isAvalable()) + return nodes.get(0).invoker; + else + return null; + } + Integer total = 0; + Node nodeOfMaxWeight = null; + for (Node node : nodes) { + total += node.effectiveWeight; + node.currentWeight += node.effectiveWeight; + + if (nodeOfMaxWeight == null) { + nodeOfMaxWeight = node; + }else{ + nodeOfMaxWeight = nodeOfMaxWeight.compareTo(node) > 0 ? nodeOfMaxWeight : node; + } + } + + nodeOfMaxWeight.currentWeight -= total; + return nodeOfMaxWeight.invoker; + } + + public void onInvokeSuccess(Invoker invoker){ + if (checkNodes()){ + nodes.stream() + .filter((Node node)->invoker.id().equals(node.invoker.id())) + .findFirst() + .get() + .onInvokeSuccess(); + } + } + + public void onInvokeFail(Invoker invoker){ + if (checkNodes()){ + nodes.stream() + .filter((Node node)->invoker.id().equals(node.invoker.id())) + .findFirst() + .get() + .onInvokeFail(); + } + } + + private boolean checkNodes(){ + return (nodes != null && nodes.size() > 0); + } + + public void printCurrenctWeightBeforeSelect(){ + if (checkNodes()) { + final StringBuffer out = new StringBuffer("{"); + nodes.forEach(node->out.append(node.invoker.id()) + .append("=") + .append(node.currentWeight+node.effectiveWeight) + .append(",")); + out.append("}"); + System.out.print(out); + } + } + + public void printCurrenctWeight(){ + if (checkNodes()) { + final StringBuffer out = new StringBuffer("{"); + nodes.forEach(node->out.append(node.invoker.id()) + .append("=") + .append(node.currentWeight) + .append(",")); + out.append("}"); + System.out.print(out); + } + } + + public interface Invoker{ + Boolean isAvalable(); + String id(); + } + + private static class Node implements Comparable{ + final Invoker invoker; + final Integer weight; + Integer effectiveWeight; + Integer currentWeight; + + Node(Invoker invoker, Integer weight){ + this.invoker = invoker; + this.weight = weight; + this.effectiveWeight = weight; + this.currentWeight = 0; + } + + @Override + public int compareTo(Node o) { + return currentWeight > o.currentWeight ? 1 : (currentWeight.equals(o.currentWeight) ? 0 : -1); + } + + public void onInvokeSuccess(){ + if (effectiveWeight < this.weight) + effectiveWeight++; + } + + public void onInvokeFail(){ + effectiveWeight--; + } + } + + public static void main(String[] args){ + Map invokersWeight = new HashMap<>(3); + Integer aWeight = 4; + Integer bWeight = 2; + Integer cWeight = 1; + + invokersWeight.put(new Invoker() { + @Override + public Boolean isAvalable() { + return true; + } + @Override + public String id() { + return "a"; + } + }, aWeight); + + invokersWeight.put(new Invoker() { + @Override + public Boolean isAvalable() { + return true; + } + @Override + public String id() { + return "b"; + } + }, bWeight); + + invokersWeight.put(new Invoker() { + @Override + public Boolean isAvalable() { + return true; + } + @Override + public String id() { + return "c"; + } + }, cWeight); + + Integer times = 7; + RoundRobinByWeightLoadBalance roundRobin = new RoundRobinByWeightLoadBalance(invokersWeight); + for(int i=1; i<=times; i++){ + System.out.print(new StringBuffer(i+"").append(" ")); + roundRobin.printCurrenctWeightBeforeSelect(); + Invoker invoker = roundRobin.select(); + System.out.print(new StringBuffer(" ").append(invoker.id()).append(" ")); + roundRobin.printCurrenctWeight(); + System.out.println(); + } + } +} diff --git a/07rpc/rpc01/rpcfx-core/src/test/java/FourSumCount.java b/07rpc/rpc01/rpcfx-core/src/test/java/FourSumCount.java new file mode 100644 index 00000000..7375135e --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/test/java/FourSumCount.java @@ -0,0 +1,47 @@ +import java.util.HashMap; +import java.util.Map; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2024/3/14 17:52 + */ +public class FourSumCount { + public static void main(String[] args) { + int[] numsA = {1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2}; + int[] numsB = {-2, -1, -2, -1, -2, -1, -2, -1, -2, -1, -2, -1, -2, -1, -2, -1,-2, -1, -2, -1, -2, -1, -2, -1, -2, -1, -2, -1, -2, -1, -2, -1}; + int[] numsC = {-1, 2, -1, 2, -1, 2, -1, 2, -1, 2, -1, 2, -1, 2, -1, 2, -1, 2, -1, 2, -1, 2, -1, 2, -1, 2, -1, 2, -1, 2, -1, 2}; + int[] numsD = {0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2}; + + long start = System.nanoTime(); + int count = fourSumCount(numsA, numsB, numsC, numsD); + System.out.println(" take " + (System.nanoTime()-start)/1000000.0 + " ms"); + System.out.println(count); + } + + public static int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) { + Map map = new HashMap<>(); + int temp; + int res = 0; + for (int i : nums1) { + for (int j : nums2) { + temp = i + j; + if (map.containsKey(temp)) { + map.put(temp, map.get(temp) + 1); + } else { + map.put(temp, 1); + } + } + } + for (int i : nums3) { + for (int j : nums4) { + temp = i + j; + if (map.containsKey(0 - temp)) { + res += map.get(0 - temp); + } + } + } + return res; + } +} \ No newline at end of file diff --git a/07rpc/rpc01/rpcfx-demo-api/src/main/java/io/kimmking/rpcfx/demo/api/UserService.java b/07rpc/rpc01/rpcfx-demo-api/src/main/java/io/kimmking/rpcfx/demo/api/UserService.java index 8940d291..c7678f10 100644 --- a/07rpc/rpc01/rpcfx-demo-api/src/main/java/io/kimmking/rpcfx/demo/api/UserService.java +++ b/07rpc/rpc01/rpcfx-demo-api/src/main/java/io/kimmking/rpcfx/demo/api/UserService.java @@ -4,6 +4,8 @@ public interface UserService { User findById(int id); + User find(int timeout); + //User findById(int id, String name); } diff --git a/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/RpcfxClientApplication.java b/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/RpcfxClientApplication.java index dc058edd..366af282 100644 --- a/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/RpcfxClientApplication.java +++ b/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/RpcfxClientApplication.java @@ -2,23 +2,13 @@ import com.alibaba.fastjson.JSON; import io.kimmking.rpcfx.annotation.RpcfxReference; -import io.kimmking.rpcfx.api.Filter; -import io.kimmking.rpcfx.api.LoadBalancer; -import io.kimmking.rpcfx.api.Router; -import io.kimmking.rpcfx.api.RpcfxRequest; -import io.kimmking.rpcfx.consumer.RpcfxInvoker; -import io.kimmking.rpcfx.demo.api.User; +import io.kimmking.rpcfx.demo.api.OrderService; import io.kimmking.rpcfx.demo.api.UserService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.ApplicationRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.util.List; -import java.util.Random; @SpringBootApplication @ComponentScan("io.kimmking.rpcfx") @@ -44,11 +34,21 @@ public static void main(String[] args) { SpringApplication.run(RpcfxClientApplication.class, args); } -// @Override -// public void run(String... args) throws Exception { -// // userService2 = RpcfxInvoker.createFromRegistry(UserService.class, new TagRouter(), new RandomLoadBalancer(), new CuicuiFilter()); -// User user = userService2.findById(1); -// System.out.println(JSON.toJSONString(user)); + @RpcfxReference + UserService userService; + + @RpcfxReference + OrderService orderService; + + @Bean + public ApplicationRunner runUserService() { + System.out.println(JSON.toJSONString(userService.hashCode())); + return x -> System.out.println(JSON.toJSONString(userService.find(500))); + } + +// @Bean +// public ApplicationRunner runOrderService() { +// return x -> System.out.println(JSON.toJSONString(orderService.findOrderById(11))); // } } diff --git a/07rpc/rpc01/rpcfx-demo-consumer/src/main/resources/application.yml b/07rpc/rpc01/rpcfx-demo-consumer/src/main/resources/application.yml index 8728f144..8b216a03 100644 --- a/07rpc/rpc01/rpcfx-demo-consumer/src/main/resources/application.yml +++ b/07rpc/rpc01/rpcfx-demo-consumer/src/main/resources/application.yml @@ -11,4 +11,12 @@ spring: execution: pool: core-size: 32 - max-size: 128 \ No newline at end of file + max-size: 128 + +app: + id: app2 + namespace: ns1 + env: sit + mock: false + cache: false + retry: 2 diff --git a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/RpcfxServerApplication.java b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/RpcfxServerApplication.java index aacab597..a6b166dd 100644 --- a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/RpcfxServerApplication.java +++ b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/RpcfxServerApplication.java @@ -23,7 +23,7 @@ public class RpcfxServerApplication implements CommandLineRunner { @Autowired ProviderBootstrap bootstrap; - public static void main(String[] args) throws Exception { + public static void main(String[] args) { SpringApplication.run(RpcfxServerApplication.class, args); } @@ -38,12 +38,12 @@ public RpcfxResponse invoke() { RpcfxRequest request = new RpcfxRequest(); request.setServiceClass("io.kimmking.rpcfx.demo.api.UserService"); request.setParams(new Object[]{1}); - request.setMethod("findById"); + request.setMethodSign("findById@1_int,"); return bootstrap.getInvoker().invoke(request); } @Override - public void run(String... args) throws Exception { + public void run(String... args) { RpcfxResponse response = invoke(); System.out.println(JSON.toJSONString(response)); } diff --git a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/UserServiceImpl.java b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/UserServiceImpl.java index ed410cc7..425fe315 100644 --- a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/UserServiceImpl.java +++ b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/UserServiceImpl.java @@ -19,4 +19,14 @@ public User findById(int id) { return new User(id, "KK-" + environment.getProperty("server.port") + "_" + System.currentTimeMillis()); } + + public User find(int timeout) { + try { + String p = environment.getProperty("server.port"); + if("8081".equals(p)) Thread.sleep(timeout); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + return findById(100); + } } diff --git a/07rpc/rpc01/rpcfx-demo-provider/src/main/resources/application.yml b/07rpc/rpc01/rpcfx-demo-provider/src/main/resources/application.yml index bec5b5f8..9abe8100 100644 --- a/07rpc/rpc01/rpcfx-demo-provider/src/main/resources/application.yml +++ b/07rpc/rpc01/rpcfx-demo-provider/src/main/resources/application.yml @@ -7,3 +7,8 @@ spring: timeout-per-shutdown-phase: 20s main: allow-circular-references: true + +app: + id: app2 + namespace: ns1 + env: sit \ No newline at end of file From beddc69164ecb6642b83ac9cfa4d9fbc73fd6f37 Mon Sep 17 00:00:00 2001 From: kimmking Date: Mon, 15 Apr 2024 00:15:46 +0800 Subject: [PATCH 10/13] update some config --- 07rpc/rpc01/client-rest.http | 2 +- .../io/kimmking/rpcfx/meta/InstanceMeta.java | 26 ++------ .../rpcfx/provider/ProviderBootstrap.java | 4 +- .../rpcfx/registry/RegistryConfiguration.java | 3 +- .../registry/kkregistry/KKRegistryCenter.java | 66 ++++++++++++------- .../zookeeper/ZookeeperRegistryCenter.java | 6 +- .../demo/consumer/RpcfxClientApplication.java | 2 +- .../demo/provider/RpcfxServerApplication.java | 2 +- 8 files changed, 58 insertions(+), 53 deletions(-) diff --git a/07rpc/rpc01/client-rest.http b/07rpc/rpc01/client-rest.http index 5808372d..cfe742ec 100644 --- a/07rpc/rpc01/client-rest.http +++ b/07rpc/rpc01/client-rest.http @@ -1 +1 @@ -http://127.0.0.1:8080/api/hello \ No newline at end of file +http://127.0.0.1:8091/api/hello \ No newline at end of file diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/meta/InstanceMeta.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/meta/InstanceMeta.java index 0fae8699..e1388143 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/meta/InstanceMeta.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/meta/InstanceMeta.java @@ -1,10 +1,7 @@ package io.kimmking.rpcfx.meta; import com.google.common.base.Strings; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; +import lombok.*; import java.net.URI; import java.util.Map; @@ -21,13 +18,14 @@ @Builder @NoArgsConstructor @AllArgsConstructor +@EqualsAndHashCode(of = {"scheme", "host", "port", "context"}) public class InstanceMeta { private String scheme; - private String ip; + private String host; private Integer port; private String context; - private String status; + private boolean status; private Map metadata; public static InstanceMeta from(String instance) { @@ -36,7 +34,7 @@ public static InstanceMeta from(String instance) { path = Strings.isNullOrEmpty(path) ? "" : path.substring(1); return InstanceMeta.builder() .scheme(uri.getScheme()) - .ip(uri.getHost()) + .host(uri.getHost()) .port(uri.getPort()) .context(path) .build(); @@ -44,19 +42,7 @@ public static InstanceMeta from(String instance) { @Override public String toString() { - return scheme + "://" + ip + ":" + port + "/" + context; + return scheme + "://" + host + ":" + port + "/" + context; } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - InstanceMeta that = (InstanceMeta) o; - return Objects.equals(scheme, that.scheme) && Objects.equals(ip, that.ip) && Objects.equals(port, that.port) && Objects.equals(context, that.context); - } - - @Override - public int hashCode() { - return Objects.hash(scheme, ip, port, context); - } } diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/provider/ProviderBootstrap.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/provider/ProviderBootstrap.java index 6fa28882..ce714bb0 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/provider/ProviderBootstrap.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/provider/ProviderBootstrap.java @@ -112,7 +112,7 @@ private void registerServices() { .app(app).namespace(ns).env(env).build(); InstanceMeta im = InstanceMeta.builder() - .scheme(SCHEME).ip(ip).port(port).context("").build(); + .scheme(SCHEME).host(ip).port(port).context("").build(); try { registry.registerService(sm, im); registry.heartbeat(sm, im); @@ -136,7 +136,7 @@ private void unregisterServices() { ServiceMeta sm = ServiceMeta.builder().name(x) .app(app).namespace(ns).env(env).build(); InstanceMeta im = InstanceMeta.builder() - .scheme(SCHEME).ip(ip).port(port).context("").build(); + .scheme(SCHEME).host(ip).port(port).context("").build(); try { registry.unregisterService(sm, im); } catch (Exception e) { diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/RegistryConfiguration.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/RegistryConfiguration.java index 5aa67ec1..8391c4aa 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/RegistryConfiguration.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/RegistryConfiguration.java @@ -17,7 +17,8 @@ public class RegistryConfiguration { @Bean RegistryCenter createRC() { - return new ZookeeperRegistryCenter(); //KKRegistryCenter(); + return new KKRegistryCenter(); + //return new ZookeeperRegistryCenter(); //KKRegistryCenter(); } } diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/kkregistry/KKRegistryCenter.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/kkregistry/KKRegistryCenter.java index 93171162..175bd84e 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/kkregistry/KKRegistryCenter.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/kkregistry/KKRegistryCenter.java @@ -48,48 +48,62 @@ public void stop() { @Override public void registerService(ServiceMeta service, InstanceMeta instance) throws Exception { - String reqJson = "{\n" + - " \"scheme\": \"http\",\n" + - " \"ip\": \"" + instance.getIp() + "\",\n" + - " \"port\": \"" + instance.getPort() + "\",\n" + - " \"context\": \"\",\n" + - " \"status\": \"online\",\n" + - " \"metadata\": {\n" + - " \"env\": \"dev\",\n" + - " \"tag\": \"RED\"\n" + - " }\n" + - "}"; + instance.setStatus(true); + String reqJson = JSON.toJSONString(instance); + String url = "http://localhost:8484/reg?service=" + service; + System.out.println(" ====> reg service: " + url); final Request request = new Request.Builder() - .url("http://localhost:8484/reg?service=" + service) + .url(url) .post(RequestBody.create(JSONTYPE, reqJson)) .build(); String respJson = client.newCall(request).execute().body().string(); - System.out.println(respJson); + System.out.println(" ====> reg response: " + respJson); +// String reqJson = "{\n" + +// " \"scheme\": \"http\",\n" + +// " \"ip\": \"" + instance.getIp() + "\",\n" + +// " \"port\": \"" + instance.getPort() + "\",\n" + +// " \"context\": \"\",\n" + +// " \"status\": \"online\",\n" + +// " \"metadata\": {\n" + +// " \"env\": \"dev\",\n" + +// " \"tag\": \"RED\"\n" + +// " }\n" + +// "}"; +// final Request request = new Request.Builder() +// .url("http://localhost:8484/reg?service=" + service) +// .post(RequestBody.create(JSONTYPE, reqJson)) +// .build(); +// String respJson = client.newCall(request).execute().body().string(); +// System.out.println(respJson); } @Override public void unregisterService(ServiceMeta service, InstanceMeta instance) throws Exception { String reqJson = "{\n" + " \"scheme\": \"http\",\n" + - " \"ip\": \"" + instance.getIp() + "\",\n" + + " \"host\": \"" + instance.getHost() + "\",\n" + " \"port\": \"" + instance.getPort() + "\",\n" + " \"context\": \"\"\n" + "}"; + String url = "http://localhost:8484/unreg?service=" + service; + System.out.println(" ====> unreg service: " + url); final Request request = new Request.Builder() - .url("http://localhost:8484/unreg?service=" + service) + .url(url) .post(RequestBody.create(JSONTYPE, reqJson)) .build(); String respJson = client.newCall(request).execute().body().string(); - System.out.println(respJson); + System.out.println(" ====> unreg response: " + respJson); } public List fetchInstances(ServiceMeta service) throws Exception { + String url = "http://localhost:8484/findAll?service=" + service; + System.out.println(" ====> fetchInstances: " + url); final Request request = new Request.Builder() - .url("http://localhost:8484/list?service=" + service) + .url(url) .get() .build(); String respJson = client.newCall(request).execute().body().string(); - System.out.println(respJson); + System.out.println(" ====> fetchInstances response: " + respJson); List instances = JSON.parseObject(respJson, new TypeReference>() { }); return instances; @@ -115,12 +129,14 @@ public void subscribe(ServiceMeta service, final ChangedListener listener) { private boolean hb(ServiceMeta service, ChangedListener listener) throws Exception { String svc = service.toString(); + String url = "http://localhost:8484/version?service=" + svc; + System.out.println(" ====> consumer version: " + url); final Request request = new Request.Builder() - .url("http://localhost:8484/hb?service=" + svc) + .url(url) .get() .build(); String respJson = client.newCall(request).execute().body().string(); - System.out.println("hb:"+respJson); + System.out.println(" ====> consumer version: "+respJson); Long v = Long.valueOf(respJson); Long o = TV.getOrDefault(svc, -1L); if ( v > o) { @@ -141,17 +157,19 @@ public void heartbeat(ServiceMeta service, InstanceMeta instance) { Long heart(ServiceMeta service, InstanceMeta instance) throws Exception { String reqJson = "{\n" + " \"scheme\": \"http\",\n" + - " \"ip\": \"" + instance.getIp() + "\",\n" + + " \"host\": \"" + instance.getHost() + "\",\n" + " \"port\": \"" + instance.getPort() + "\",\n" + " \"context\": \"\",\n" + - " \"status\": \"online\"\n" + + " \"status\": true\n" + "}"; + String url = "http://localhost:8484/renew?service=" + service; + System.out.println(" ====> provider renew: " + url); final Request request = new Request.Builder() - .url("http://localhost:8484/heart?service=" + service) + .url(url) .post(RequestBody.create(JSONTYPE, reqJson)) .build(); String respJson = client.newCall(request).execute().body().string(); - System.out.println("heart:"+respJson); + System.out.println(" ====> provider renew: "+respJson); return Long.valueOf(respJson); } diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/zookeeper/ZookeeperRegistryCenter.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/zookeeper/ZookeeperRegistryCenter.java index 784a9b80..37f7b6ea 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/zookeeper/ZookeeperRegistryCenter.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/zookeeper/ZookeeperRegistryCenter.java @@ -42,7 +42,7 @@ public void stop(){ public void registerService(ServiceMeta service, InstanceMeta instance) throws Exception { ServiceProviderDesc userServiceSesc = ServiceProviderDesc.builder() - .host(instance.getIp()) + .host(instance.getHost()) .port(instance.getPort()).serviceClass(service.getName()).build(); // String userServiceSescJson = JSON.toJSONString(userServiceSesc); @@ -63,9 +63,9 @@ public void unregisterService(ServiceMeta service, InstanceMeta instance) throws if (null == client.checkExists().forPath("/" + service)) { return; } - System.out.println("delete " + "/" + service + "/" + instance.getIp() + "_" + instance.getPort()); + System.out.println("delete " + "/" + service + "/" + instance.getHost() + "_" + instance.getPort()); client.delete().quietly(). - forPath( "/" + service + "/" + instance.getIp() + "_" + instance.getPort()); + forPath( "/" + service + "/" + instance.getHost() + "_" + instance.getPort()); } public List fetchInstances(ServiceMeta service) throws Exception { diff --git a/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/RpcfxClientApplication.java b/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/RpcfxClientApplication.java index 366af282..88e23382 100644 --- a/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/RpcfxClientApplication.java +++ b/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/RpcfxClientApplication.java @@ -11,7 +11,7 @@ import org.springframework.context.annotation.ComponentScan; @SpringBootApplication -@ComponentScan("io.kimmking.rpcfx") +@ComponentScan({"io.kimmking.rpcfx.consumer","io.kimmking.rpcfx.demo.consumer"}) public class RpcfxClientApplication { public static void main(String[] args) { diff --git a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/RpcfxServerApplication.java b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/RpcfxServerApplication.java index a6b166dd..ca236d16 100644 --- a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/RpcfxServerApplication.java +++ b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/RpcfxServerApplication.java @@ -17,7 +17,7 @@ @SpringBootApplication @RestController -@ComponentScan("io.kimmking.rpcfx") +@ComponentScan({"io.kimmking.rpcfx.provider", "io.kimmking.rpcfx.demo.provider"}) public class RpcfxServerApplication implements CommandLineRunner { @Autowired From ef42a2b667535859c241199e7d7311e02b8684f4 Mon Sep 17 00:00:00 2001 From: kimmking Date: Mon, 15 Apr 2024 14:11:22 +0800 Subject: [PATCH 11/13] polish codes --- .../io/kimmking/rpcfx/meta/ServerMeta.java | 24 ++++ .../io/kimmking/rpcfx/registry/Event.java | 2 +- .../rpcfx/registry/RegistryCenter.java | 2 +- .../registry/kkregistry/KKRegistryCenter.java | 121 +++++++++++------- 4 files changed, 103 insertions(+), 46 deletions(-) create mode 100644 07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/meta/ServerMeta.java diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/meta/ServerMeta.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/meta/ServerMeta.java new file mode 100644 index 00000000..239042b8 --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/meta/ServerMeta.java @@ -0,0 +1,24 @@ +package io.kimmking.rpcfx.meta; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2024/4/13 21:43 + */ + +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(of = {"url"}) +public class ServerMeta { + private String url; + private boolean leader; + private boolean status; + private long version; +} diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/Event.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/Event.java index 54082e15..19a92cd4 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/Event.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/Event.java @@ -16,7 +16,7 @@ public interface Event { T getData(); - static Event withData(List list) { + static Event> withData(List list) { return new ChangedEvent(list); } diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/RegistryCenter.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/RegistryCenter.java index 93d86519..871c1a7e 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/RegistryCenter.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/RegistryCenter.java @@ -28,7 +28,7 @@ public interface RegistryCenter { List fetchInstances(ServiceMeta service) throws Exception; - void subscribe(ServiceMeta service, ChangedListener listener); + void subscribe(ServiceMeta service, ChangedListener> listener); void heartbeat(ServiceMeta service, InstanceMeta instance); diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/kkregistry/KKRegistryCenter.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/kkregistry/KKRegistryCenter.java index 175bd84e..001a3854 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/kkregistry/KKRegistryCenter.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/registry/kkregistry/KKRegistryCenter.java @@ -3,18 +3,22 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.TypeReference; import io.kimmking.rpcfx.meta.InstanceMeta; +import io.kimmking.rpcfx.meta.ServerMeta; import io.kimmking.rpcfx.meta.ServiceMeta; import io.kimmking.rpcfx.registry.ChangedListener; import io.kimmking.rpcfx.registry.Event; import io.kimmking.rpcfx.registry.RegistryCenter; +import lombok.SneakyThrows; import okhttp3.ConnectionPool; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; +import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Random; import java.util.concurrent.TimeUnit; import static io.kimmking.rpcfx.consumer.RpcfxInvocationHandler.JSONTYPE; @@ -27,9 +31,13 @@ */ public class KKRegistryCenter implements RegistryCenter { + public String RC_Server = "http://localhost:8485"; + private ServerMeta leader; + private List servers; private Map TV = new HashMap<>(); OkHttpClient client; + @SneakyThrows @Override public void start() { client = new OkHttpClient.Builder() @@ -39,6 +47,40 @@ public void start() { .writeTimeout(65, TimeUnit.SECONDS) .connectTimeout(3, TimeUnit.SECONDS) .build(); + + String url = RC_Server + "/cluster"; + boolean init = false; + while(!init) { + System.out.println("===============>> cluster info from :" + url); + List new_servers = null; + ServerMeta new_leader = null; + try { + String respJson = get(url); + new_servers = JSON.parseObject(respJson, new TypeReference>() { + }); + new_leader = new_servers.stream().filter(ServerMeta::isStatus) + .filter(ServerMeta::isLeader).findFirst().orElse(null); + } catch (Exception exception) { + exception.printStackTrace(); + } + + if(new_leader == null) { + System.out.println("===============>> no leader, 500ms later and retry."); + Thread.sleep(500); + Random random = new Random(); + if(new_servers !=null && new_servers.size() > 1) { + url = new_servers.get(random.nextInt(new_servers.size())).getUrl() + "/cluster"; + } else if((new_servers ==null || new_servers.isEmpty()) && !servers.isEmpty()) { + url = servers.get(random.nextInt(servers.size())).getUrl() + "/cluster"; + } + } else { + this.servers = new_servers; + this.leader = new_leader; + init = true; + System.out.println("===============>> init ok, new_leader = " + new_leader); + System.out.println("===============>> init ok, new_servers = " + new_servers); + } + } } @Override @@ -50,14 +92,8 @@ public void stop() { public void registerService(ServiceMeta service, InstanceMeta instance) throws Exception { instance.setStatus(true); String reqJson = JSON.toJSONString(instance); - String url = "http://localhost:8484/reg?service=" + service; - System.out.println(" ====> reg service: " + url); - final Request request = new Request.Builder() - .url(url) - .post(RequestBody.create(JSONTYPE, reqJson)) - .build(); - String respJson = client.newCall(request).execute().body().string(); - System.out.println(" ====> reg response: " + respJson); + String url = leader.getUrl() + "/reg?service=" + service; + post(url, reqJson); // String reqJson = "{\n" + // " \"scheme\": \"http\",\n" + // " \"ip\": \"" + instance.getIp() + "\",\n" + @@ -77,33 +113,43 @@ public void registerService(ServiceMeta service, InstanceMeta instance) throws E // System.out.println(respJson); } - @Override - public void unregisterService(ServiceMeta service, InstanceMeta instance) throws Exception { - String reqJson = "{\n" + - " \"scheme\": \"http\",\n" + - " \"host\": \"" + instance.getHost() + "\",\n" + - " \"port\": \"" + instance.getPort() + "\",\n" + - " \"context\": \"\"\n" + - "}"; - String url = "http://localhost:8484/unreg?service=" + service; - System.out.println(" ====> unreg service: " + url); + private String post(String url, String reqJson) throws IOException { + System.out.println(" ====> request: " + url); final Request request = new Request.Builder() .url(url) .post(RequestBody.create(JSONTYPE, reqJson)) .build(); String respJson = client.newCall(request).execute().body().string(); - System.out.println(" ====> unreg response: " + respJson); + System.out.println(" ====> response: " + respJson); + return respJson; } - public List fetchInstances(ServiceMeta service) throws Exception { - String url = "http://localhost:8484/findAll?service=" + service; - System.out.println(" ====> fetchInstances: " + url); + private String get(String url) throws IOException { + System.out.println(" ====> request: " + url); final Request request = new Request.Builder() .url(url) .get() .build(); String respJson = client.newCall(request).execute().body().string(); - System.out.println(" ====> fetchInstances response: " + respJson); + System.out.println(" ====> response: " + respJson); + return respJson; + } + + @Override + public void unregisterService(ServiceMeta service, InstanceMeta instance) throws Exception { + String reqJson = "{\n" + + " \"scheme\": \"http\",\n" + + " \"host\": \"" + instance.getHost() + "\",\n" + + " \"port\": \"" + instance.getPort() + "\",\n" + + " \"context\": \"\"\n" + + "}"; + String url = leader.getUrl() + "/unreg?service=" + service; + post(url, reqJson); + } + + public List fetchInstances(ServiceMeta service) throws Exception { + String url = RC_Server + "/findAll?service=" + service; + String respJson = get(url); List instances = JSON.parseObject(respJson, new TypeReference>() { }); return instances; @@ -112,12 +158,11 @@ public List fetchInstances(ServiceMeta service) throws Exception { KKHeathChecker checker = new KKHeathChecker(); // for Consumer - public void subscribe(ServiceMeta service, final ChangedListener listener) { - + public void subscribe(ServiceMeta service, final ChangedListener> listener) { checker.check( () -> { - if(hb(service, listener)) { + if(hb(service)) { List instances = fetchInstances(service); - Event e = Event.withData(instances); + Event> e = Event.withData(instances); listener.fireEvent(e); } }); @@ -127,16 +172,10 @@ public void subscribe(ServiceMeta service, final ChangedListener listener) { // 如果有差异就fire } - private boolean hb(ServiceMeta service, ChangedListener listener) throws Exception { + private boolean hb(ServiceMeta service) throws Exception { String svc = service.toString(); - String url = "http://localhost:8484/version?service=" + svc; - System.out.println(" ====> consumer version: " + url); - final Request request = new Request.Builder() - .url(url) - .get() - .build(); - String respJson = client.newCall(request).execute().body().string(); - System.out.println(" ====> consumer version: "+respJson); + String url = RC_Server + "/version?service=" + svc; + String respJson = get(url); Long v = Long.valueOf(respJson); Long o = TV.getOrDefault(svc, -1L); if ( v > o) { @@ -162,14 +201,8 @@ Long heart(ServiceMeta service, InstanceMeta instance) throws Exception { " \"context\": \"\",\n" + " \"status\": true\n" + "}"; - String url = "http://localhost:8484/renew?service=" + service; - System.out.println(" ====> provider renew: " + url); - final Request request = new Request.Builder() - .url(url) - .post(RequestBody.create(JSONTYPE, reqJson)) - .build(); - String respJson = client.newCall(request).execute().body().string(); - System.out.println(" ====> provider renew: "+respJson); + String url = leader.getUrl() + "/renew?service=" + service; + String respJson = post(url, reqJson); return Long.valueOf(respJson); } From 9f00f9e1fa3c92365120d266c6de010a1bee48a2 Mon Sep 17 00:00:00 2001 From: kimmking Date: Tue, 25 Jun 2024 19:44:07 +0800 Subject: [PATCH 12/13] add file store --- 02nio/nio02/pom.xml | 2 +- .../gateway/NettyServerApplication.java | 10 + .../filter/HeaderHttpResponseFilter.java | 14 + .../gateway/inbound/HttpInboundHandler.java | 11 +- .../gateway/inbound/NettyInfoHandler.java | 83 +++++ .../.mvn/wrapper/MavenWrapperDownloader.java | 118 ------- 09mq/kmq-core/.mvn/wrapper/maven-wrapper.jar | Bin 50710 -> 0 bytes .../.mvn/wrapper/maven-wrapper.properties | 2 - 09mq/kmq-core/mvnw | 310 ------------------ 09mq/kmq-core/mvnw.cmd | 182 ---------- .../main/java/io/kimmking/kmq/core/Kmq.java | 26 +- .../java/io/kimmking/kmq/core/KmqBroker.java | 4 +- .../io/kimmking/kmq/core/KmqConsumer.java | 14 +- .../java/io/kimmking/kmq/core/KmqMessage.java | 18 +- .../io/kimmking/kmq/core/MessageListener.java | 13 + .../java/io/kimmking/kmq/demo/KmqDemo.java | 9 +- .../java/io/kimmking/kmq/store/FileDemo.java | 97 ++++++ .../java/io/kimmking/kmq/store/Indexer.java | 53 +++ 18 files changed, 342 insertions(+), 624 deletions(-) create mode 100644 02nio/nio02/src/main/java/io/github/kimmking/gateway/inbound/NettyInfoHandler.java delete mode 100644 09mq/kmq-core/.mvn/wrapper/MavenWrapperDownloader.java delete mode 100644 09mq/kmq-core/.mvn/wrapper/maven-wrapper.jar delete mode 100644 09mq/kmq-core/.mvn/wrapper/maven-wrapper.properties delete mode 100755 09mq/kmq-core/mvnw delete mode 100644 09mq/kmq-core/mvnw.cmd create mode 100644 09mq/kmq-core/src/main/java/io/kimmking/kmq/core/MessageListener.java create mode 100644 09mq/kmq-core/src/main/java/io/kimmking/kmq/store/FileDemo.java create mode 100644 09mq/kmq-core/src/main/java/io/kimmking/kmq/store/Indexer.java diff --git a/02nio/nio02/pom.xml b/02nio/nio02/pom.xml index 005de90a..c10848f8 100644 --- a/02nio/nio02/pom.xml +++ b/02nio/nio02/pom.xml @@ -29,7 +29,7 @@ io.netty netty-all - 4.1.45.Final + 4.1.104.Final diff --git a/02nio/nio02/src/main/java/io/github/kimmking/gateway/NettyServerApplication.java b/02nio/nio02/src/main/java/io/github/kimmking/gateway/NettyServerApplication.java index e67b7961..1adda64a 100644 --- a/02nio/nio02/src/main/java/io/github/kimmking/gateway/NettyServerApplication.java +++ b/02nio/nio02/src/main/java/io/github/kimmking/gateway/NettyServerApplication.java @@ -2,7 +2,10 @@ import io.github.kimmking.gateway.inbound.HttpInboundServer; +import io.netty.util.internal.PlatformDependent; +import java.lang.reflect.Constructor; +import java.nio.ByteBuffer; import java.util.Arrays; public class NettyServerApplication { @@ -12,6 +15,13 @@ public class NettyServerApplication { public static void main(String[] args) { +// sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe(); +// System.out.println(unsafe.addressSize()); + + + System.out.println("PlatformDependent.hasUnsafe = " + PlatformDependent.javaVersion()); + System.out.println("PlatformDependent.hasUnsafe = " + PlatformDependent.hasUnsafe()); + String proxyPort = System.getProperty("proxyPort","8888"); // 这是之前的单个后端url的例子 diff --git a/02nio/nio02/src/main/java/io/github/kimmking/gateway/filter/HeaderHttpResponseFilter.java b/02nio/nio02/src/main/java/io/github/kimmking/gateway/filter/HeaderHttpResponseFilter.java index 53493fb4..12fe310a 100644 --- a/02nio/nio02/src/main/java/io/github/kimmking/gateway/filter/HeaderHttpResponseFilter.java +++ b/02nio/nio02/src/main/java/io/github/kimmking/gateway/filter/HeaderHttpResponseFilter.java @@ -1,10 +1,24 @@ package io.github.kimmking.gateway.filter; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpResponseStatus; public class HeaderHttpResponseFilter implements HttpResponseFilter { @Override public void filter(FullHttpResponse response) { response.headers().set("kk", "java-1-nio"); + response.setStatus(HttpResponseStatus.CREATED); +// byte[] array = response.content().array(); +// String content = new String(array); +// System.out.println(content); +// content = content + ",kimmking"; + byte[] bytes = "hello,kimm.".getBytes(); + //response.headers().setInt("Content-Length", bytes.length); + ByteBuf byteBuf = Unpooled.wrappedBuffer(bytes); + ByteBuf content = response.content(); + content.clear(); + content.writeBytes(byteBuf); } } diff --git a/02nio/nio02/src/main/java/io/github/kimmking/gateway/inbound/HttpInboundHandler.java b/02nio/nio02/src/main/java/io/github/kimmking/gateway/inbound/HttpInboundHandler.java index 69b40fde..1cd13ca2 100644 --- a/02nio/nio02/src/main/java/io/github/kimmking/gateway/inbound/HttpInboundHandler.java +++ b/02nio/nio02/src/main/java/io/github/kimmking/gateway/inbound/HttpInboundHandler.java @@ -39,9 +39,14 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { // if (uri.contains("/test")) { // handlerTest(fullRequest, ctx); // } - - handler.handle(fullRequest, ctx, filter); - + + String uri = fullRequest.getUri(); + System.out.println(" uri ==>> " + uri); + if(uri.contains("/netty/info")) { + NettyInfoHandler.INSTANCE.handle(fullRequest, ctx); + } else { + handler.handle(fullRequest, ctx, filter); + } } catch(Exception e) { e.printStackTrace(); } finally { diff --git a/02nio/nio02/src/main/java/io/github/kimmking/gateway/inbound/NettyInfoHandler.java b/02nio/nio02/src/main/java/io/github/kimmking/gateway/inbound/NettyInfoHandler.java new file mode 100644 index 00000000..f179cc82 --- /dev/null +++ b/02nio/nio02/src/main/java/io/github/kimmking/gateway/inbound/NettyInfoHandler.java @@ -0,0 +1,83 @@ +package io.github.kimmking.gateway.inbound; + +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpUtil; +import io.netty.util.internal.PlatformDependent; +import lombok.SneakyThrows; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + +import static io.netty.handler.codec.http.HttpResponseStatus.OK; +import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2024/5/31 下午7:13 + */ +public class NettyInfoHandler { + + public final static NettyInfoHandler INSTANCE = new NettyInfoHandler(); + + public void handle(final FullHttpRequest fullRequest, final ChannelHandlerContext ctx) { + System.out.println("NettyInfoHandler.handle..."); + Map infos = new HashMap<>(); + infos.put("netty.usedDirectMemory", ""+getNettyUsedDirectMemory()); + infos.put("netty.directMemoryLimit", ""+getNettyDirectMemoryLimit()); + StringBuilder sb = new StringBuilder(); + sb.append("{"); + infos.forEach((k, v) -> { + sb.append("\"").append(k).append("\"") + .append(":") + .append("\"").append(v).append("\"").append(","); + }); + if(sb.length()>1) { + sb.deleteCharAt(sb.length()-1); + } + sb.append("}"); + + byte[] body = ("{\"code\":200,\"msg\":\"success\",\"data\":" + sb +"}").getBytes(); + FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(body)); + + response.headers().set("Content-Type", "application/json"); + response.headers().setInt("Content-Length", body.length); + response.headers().set("kk.gw.hanlder", "netty.info"); + + if (fullRequest != null) { + if (!HttpUtil.isKeepAlive(fullRequest)) { + ctx.write(response).addListener(ChannelFutureListener.CLOSE); + } else { + //response.headers().set(CONNECTION, KEEP_ALIVE); + ctx.write(response); + } + } + ctx.flush(); + //ctx.close(); + + } + + @SneakyThrows + private static long getNettyUsedDirectMemory() { + Field field = PlatformDependent.class.getDeclaredField("DIRECT_MEMORY_COUNTER"); + field.setAccessible(true); + AtomicLong o = (AtomicLong)field.get(null); + return o.get(); + } + + @SneakyThrows + private static long getNettyDirectMemoryLimit() { + Field field = PlatformDependent.class.getDeclaredField("DIRECT_MEMORY_LIMIT"); + field.setAccessible(true); + return (Long)field.get(null); + } + +} diff --git a/09mq/kmq-core/.mvn/wrapper/MavenWrapperDownloader.java b/09mq/kmq-core/.mvn/wrapper/MavenWrapperDownloader.java deleted file mode 100644 index a45eb6ba..00000000 --- a/09mq/kmq-core/.mvn/wrapper/MavenWrapperDownloader.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2007-present the original author or authors. - * - * Licensed 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 - * - * https://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. - */ - -import java.net.*; -import java.io.*; -import java.nio.channels.*; -import java.util.Properties; - -public class MavenWrapperDownloader { - - private static final String WRAPPER_VERSION = "0.5.6"; - /** - * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. - */ - private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" - + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; - - /** - * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to - * use instead of the default one. - */ - private static final String MAVEN_WRAPPER_PROPERTIES_PATH = - ".mvn/wrapper/maven-wrapper.properties"; - - /** - * Path where the maven-wrapper.jar will be saved to. - */ - private static final String MAVEN_WRAPPER_JAR_PATH = - ".mvn/wrapper/maven-wrapper.jar"; - - /** - * Name of the property which should be used to override the default download url for the wrapper. - */ - private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; - - public static void main(String args[]) { - System.out.println("- Downloader started"); - File baseDirectory = new File(args[0]); - System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); - - // If the maven-wrapper.properties exists, read it and check if it contains a custom - // wrapperUrl parameter. - File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); - String url = DEFAULT_DOWNLOAD_URL; - if (mavenWrapperPropertyFile.exists()) { - FileInputStream mavenWrapperPropertyFileInputStream = null; - try { - mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); - Properties mavenWrapperProperties = new Properties(); - mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); - url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); - } catch (IOException e) { - System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); - } finally { - try { - if (mavenWrapperPropertyFileInputStream != null) { - mavenWrapperPropertyFileInputStream.close(); - } - } catch (IOException e) { - // Ignore ... - } - } - } - System.out.println("- Downloading from: " + url); - - File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); - if (!outputFile.getParentFile().exists()) { - if (!outputFile.getParentFile().mkdirs()) { - System.out.println( - "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); - } - } - System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); - try { - downloadFileFromURL(url, outputFile); - System.out.println("Done"); - System.exit(0); - } catch (Throwable e) { - System.out.println("- Error downloading"); - e.printStackTrace(); - System.exit(1); - } - } - - private static void downloadFileFromURL(String urlString, File destination) throws Exception { - if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { - String username = System.getenv("MVNW_USERNAME"); - char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); - Authenticator.setDefault(new Authenticator() { - @Override - protected PasswordAuthentication getPasswordAuthentication() { - return new PasswordAuthentication(username, password); - } - }); - } - URL website = new URL(urlString); - ReadableByteChannel rbc; - rbc = Channels.newChannel(website.openStream()); - FileOutputStream fos = new FileOutputStream(destination); - fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); - fos.close(); - rbc.close(); - } - -} diff --git a/09mq/kmq-core/.mvn/wrapper/maven-wrapper.jar b/09mq/kmq-core/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index 2cc7d4a55c0cd0092912bf49ae38b3a9e3fd0054..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50710 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 EfAhh=DF6Tf diff --git a/09mq/kmq-core/.mvn/wrapper/maven-wrapper.properties b/09mq/kmq-core/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index 642d572c..00000000 --- a/09mq/kmq-core/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1,2 +0,0 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/09mq/kmq-core/mvnw b/09mq/kmq-core/mvnw deleted file mode 100755 index a16b5431..00000000 --- a/09mq/kmq-core/mvnw +++ /dev/null @@ -1,310 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# 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 -# -# https://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. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Maven Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`which java`" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi -else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi - if [ -n "$MVNW_REPOURL" ]; then - jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - else - jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - fi - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; - esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" - if $cygwin; then - wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` - fi - - if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget "$jarUrl" -O "$wrapperJarPath" - else - wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" - fi - elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl -o "$wrapperJarPath" "$jarUrl" -f - else - curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f - fi - - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaClass=`cygpath --path --windows "$javaClass"` - fi - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") - fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") - fi - fi - fi -fi -########################################################################################## -# End of extension -########################################################################################## - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/09mq/kmq-core/mvnw.cmd b/09mq/kmq-core/mvnw.cmd deleted file mode 100644 index c8d43372..00000000 --- a/09mq/kmq-core/mvnw.cmd +++ /dev/null @@ -1,182 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM https://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - -FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% diff --git a/09mq/kmq-core/src/main/java/io/kimmking/kmq/core/Kmq.java b/09mq/kmq-core/src/main/java/io/kimmking/kmq/core/Kmq.java index ebf03192..8989faab 100644 --- a/09mq/kmq-core/src/main/java/io/kimmking/kmq/core/Kmq.java +++ b/09mq/kmq-core/src/main/java/io/kimmking/kmq/core/Kmq.java @@ -2,6 +2,8 @@ import lombok.SneakyThrows; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; @@ -13,14 +15,28 @@ public Kmq(String topic, int capacity) { this.queue = new LinkedBlockingQueue(capacity); } - private String topic; +// public List consumers = new ArrayList<>(); - private int capacity; + private List listeners = new ArrayList<>(); + + private final String topic; + + private final int capacity; private LinkedBlockingQueue queue; public boolean send(KmqMessage message) { - return queue.offer(message); + boolean offered = queue.offer(message); + if(offered) { + listeners.forEach(listener -> { + try { + listener.onMessage(message); + } catch (Exception e) { + e.printStackTrace(); + } + }); + } + return offered; } public KmqMessage poll() { @@ -32,4 +48,8 @@ public KmqMessage poll(long timeout) { return queue.poll(timeout, TimeUnit.MILLISECONDS); } + public void addListener(MessageListener listener) { + listeners.add(listener); + } + } diff --git a/09mq/kmq-core/src/main/java/io/kimmking/kmq/core/KmqBroker.java b/09mq/kmq-core/src/main/java/io/kimmking/kmq/core/KmqBroker.java index 672557db..c0aa344f 100644 --- a/09mq/kmq-core/src/main/java/io/kimmking/kmq/core/KmqBroker.java +++ b/09mq/kmq-core/src/main/java/io/kimmking/kmq/core/KmqBroker.java @@ -2,6 +2,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; public final class KmqBroker { // Broker+Connection @@ -21,8 +22,9 @@ public KmqProducer createProducer() { return new KmqProducer(this); } + final AtomicInteger consumerId = new AtomicInteger(0); public KmqConsumer createConsumer() { - return new KmqConsumer(this); + return new KmqConsumer("CID" + consumerId.getAndIncrement(), this); } } diff --git a/09mq/kmq-core/src/main/java/io/kimmking/kmq/core/KmqConsumer.java b/09mq/kmq-core/src/main/java/io/kimmking/kmq/core/KmqConsumer.java index a7dd83ae..83ae7245 100644 --- a/09mq/kmq-core/src/main/java/io/kimmking/kmq/core/KmqConsumer.java +++ b/09mq/kmq-core/src/main/java/io/kimmking/kmq/core/KmqConsumer.java @@ -1,12 +1,18 @@ package io.kimmking.kmq.core; +import lombok.Getter; + public class KmqConsumer { private final KmqBroker broker; + @Getter + private final String id; + private Kmq kmq; - public KmqConsumer(KmqBroker broker) { + public KmqConsumer(String id, KmqBroker broker) { + this.id = id; this.broker = broker; } @@ -15,6 +21,12 @@ public void subscribe(String topic) { if (null == kmq) throw new RuntimeException("Topic[" + topic + "] doesn't exist."); } + public void subscribe(String topic, MessageListener listener) { + this.kmq = this.broker.findKmq(topic); + if (null == kmq) throw new RuntimeException("Topic[" + topic + "] doesn't exist."); + this.kmq.addListener(listener); + } + public KmqMessage poll(long timeout) { return kmq.poll(timeout); } diff --git a/09mq/kmq-core/src/main/java/io/kimmking/kmq/core/KmqMessage.java b/09mq/kmq-core/src/main/java/io/kimmking/kmq/core/KmqMessage.java index fb7d90de..73823c9d 100644 --- a/09mq/kmq-core/src/main/java/io/kimmking/kmq/core/KmqMessage.java +++ b/09mq/kmq-core/src/main/java/io/kimmking/kmq/core/KmqMessage.java @@ -2,15 +2,31 @@ import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; import java.util.HashMap; +import java.util.concurrent.atomic.AtomicLong; @AllArgsConstructor +@NoArgsConstructor @Data public class KmqMessage { - private HashMap headers; + static AtomicLong MID = new AtomicLong(0); + private HashMap headers = new HashMap<>(); + private String topic; + private Long id; private T body; + public KmqMessage(String topic, T body) { + this.topic = topic; + this.body = body; + this.id = MID.getAndIncrement(); + } + + public static KmqMessage from(String topic, T body) { + return new KmqMessage<>(topic, body); + } + } diff --git a/09mq/kmq-core/src/main/java/io/kimmking/kmq/core/MessageListener.java b/09mq/kmq-core/src/main/java/io/kimmking/kmq/core/MessageListener.java new file mode 100644 index 00000000..4cfdbe08 --- /dev/null +++ b/09mq/kmq-core/src/main/java/io/kimmking/kmq/core/MessageListener.java @@ -0,0 +1,13 @@ +package io.kimmking.kmq.core; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2024/6/13 下午3:29 + */ +public interface MessageListener { + + void onMessage(KmqMessage message) throws Exception; + +} diff --git a/09mq/kmq-core/src/main/java/io/kimmking/kmq/demo/KmqDemo.java b/09mq/kmq-core/src/main/java/io/kimmking/kmq/demo/KmqDemo.java index ad6e2d56..9921af3d 100644 --- a/09mq/kmq-core/src/main/java/io/kimmking/kmq/demo/KmqDemo.java +++ b/09mq/kmq-core/src/main/java/io/kimmking/kmq/demo/KmqDemo.java @@ -16,6 +16,11 @@ public static void main(String[] args) { KmqBroker broker = new KmqBroker(); broker.createTopic(topic); + KmqConsumer subscriber = broker.createConsumer(); + subscriber.subscribe(topic, (message) -> { + System.out.println(subscriber.getId() + " : " + message.getBody()); + }); + KmqConsumer consumer = broker.createConsumer(); consumer.subscribe(topic); final boolean[] flag = new boolean[1]; @@ -24,14 +29,14 @@ public static void main(String[] args) { while (flag[0]) { KmqMessage message = consumer.poll(100); if(null != message) { - System.out.println(message.getBody()); + System.out.println(consumer.getId() + " : " + message.getBody()); } } System.out.println("程序退出。"); }).start(); KmqProducer producer = broker.createProducer(); - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < 10; i++) { Order order = new Order(1000L + i, System.currentTimeMillis(), "USD2CNY", 6.51d); producer.send(topic, new KmqMessage(null, order)); } diff --git a/09mq/kmq-core/src/main/java/io/kimmking/kmq/store/FileDemo.java b/09mq/kmq-core/src/main/java/io/kimmking/kmq/store/FileDemo.java new file mode 100644 index 00000000..baccdaff --- /dev/null +++ b/09mq/kmq-core/src/main/java/io/kimmking/kmq/store/FileDemo.java @@ -0,0 +1,97 @@ +package io.kimmking.kmq.store; + +import com.alibaba.fastjson.JSON; +import io.kimmking.kmq.core.KmqMessage; + +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.Scanner; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2024/6/13 下午9:59 + */ +public class FileDemo { + + public static void main(String[] args) throws IOException { + + String content = "this is a good file.\r\n" + + "that is a new line.\r\n"; + String topic = "topicA"; + System.out.println(content.length()); + + File file = new File("store001.dat"); + if (!file.exists()) { + file.createNewFile(); + } + Path path = Paths.get(file.toURI()); + try(FileChannel channel = (FileChannel) Files.newByteChannel(path, + StandardOpenOption.READ, + StandardOpenOption.WRITE)) { + + MappedByteBuffer mappedByteBuffer = channel + .map(FileChannel.MapMode.READ_WRITE, 0, 10240); + + if (mappedByteBuffer != null) { + + System.out.println(Charset.forName("utf-8") + .decode(mappedByteBuffer.asReadOnlyBuffer())); + + for (int i = 0; i < 100; i++) { + KmqMessage km = KmqMessage.from(topic, content); + String message = encodeMessage(km); + Indexer.addEntry(topic, km.getId(), mappedByteBuffer.position(), message.length()); + int pos = write(mappedByteBuffer, message); + System.out.println("POS = " + pos); + } + + System.out.println(" ======== indexer ========= "); + System.out.println(Indexer.getEntries(topic)); + } + + ByteBuffer readOnlyBuffer = mappedByteBuffer.asReadOnlyBuffer(); + Scanner sc = new Scanner(System.in); + while (sc.hasNextLine()) { + String line = sc.nextLine(); + if (line.equals("exit")) { + break; + } + System.out.println("IN = "+line); + Long id = Long.valueOf(line); + Indexer.Entry entry = Indexer.getEntry(id); + System.out.println("EN = " + entry); + if(entry == null) { + System.out.println("!!!No entry for id=" + id); + } else { + readOnlyBuffer.position(entry.offset); + byte[] bytes = new byte[entry.length]; + readOnlyBuffer.get(bytes, 0, entry.length); + System.out.println("MSG = " + new String(bytes)); + } + } + + } + } + + private static String encodeMessage(KmqMessage message) { + return JSON.toJSONString(message); + } + + public static int write(MappedByteBuffer buffer, String content) throws IOException { + buffer.put( + Charset.forName("utf-8") + .encode(content)); + return buffer.position(); + } + +} diff --git a/09mq/kmq-core/src/main/java/io/kimmking/kmq/store/Indexer.java b/09mq/kmq-core/src/main/java/io/kimmking/kmq/store/Indexer.java new file mode 100644 index 00000000..792ac400 --- /dev/null +++ b/09mq/kmq-core/src/main/java/io/kimmking/kmq/store/Indexer.java @@ -0,0 +1,53 @@ +package io.kimmking.kmq.store; + +import lombok.Data; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Description for this class. + * + * @Author : kimmking(kimmking@apache.org) + * @create 2024/6/14 下午6:19 + */ + +@Data +public class Indexer { + + static Map> index = new HashMap<>(); + static Map mappings = new HashMap<>(); + + @Data + public static class Entry { + long id; + int offset; + int length; + + public Entry(long id, int offset, int length) { + this.offset = offset; + this.length = length; + } + } + + public static void addEntry(String topic, long id, int offset, int length) { + List entries = index.get(topic); + if(entries == null) { + entries = new java.util.ArrayList<>(); + index.put(topic, entries); + } + Entry e = new Entry(id, offset, length); + entries.add(e); + mappings.put(id, e); + } + + public static List getEntries(String topic) { + return index.get(topic); + } + + public static Entry getEntry(long id) { + return mappings.get(id); + } + +} From 58bb7d3a90e97a37963f1b016d1c44db3dffe0d4 Mon Sep 17 00:00:00 2001 From: kimmking Date: Wed, 26 Jun 2024 00:10:04 +0800 Subject: [PATCH 13/13] update ignore --- 09mq/kmq-core/.gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/09mq/kmq-core/.gitignore b/09mq/kmq-core/.gitignore index e4e0bd7f..14fb3721 100644 --- a/09mq/kmq-core/.gitignore +++ b/09mq/kmq-core/.gitignore @@ -31,3 +31,5 @@ build/ ### VS Code ### .vscode/ + +*.dat