This commit is contained in:
骑着蜗牛追导弹 2024-12-03 23:43:10 +08:00
parent 3aae139b28
commit 9581a6d39c
14 changed files with 277 additions and 62 deletions

View File

@ -1,18 +1,4 @@
/*
* Copyright 2019-2020 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.odboy.infra.context; package cn.odboy.infra.context;

View File

@ -0,0 +1,27 @@
package cn.odboy.config.context;
/**
* @date: 2020/6/9 17:02
* @since: 1.0
* @see {@link SpringContextHolder}
* 针对某些初始化方法在SpringContextHolder 初始化前时<br>
* 可提交一个 提交回调任务<br>
* 在SpringContextHolder 初始化后进行回调使用
*/
public interface CallBack {
/**
* 回调执行方法
*/
void executor();
/**
* 本回调任务名称
*
* @return /
*/
default String getCallBackName() {
return Thread.currentThread().getId() + ":" + this.getClass().getName();
}
}

View File

@ -0,0 +1,140 @@
package cn.odboy.config.context;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Slf4j
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
private static ApplicationContext applicationContext = null;
private static final List<CallBack> CALL_BACKS = new ArrayList<>();
private static boolean addCallback = true;
/**
* 针对 某些初始化方法在SpringContextHolder 未初始化时 提交回调方法
* 在SpringContextHolder 初始化后进行回调使用
*
* @param callBack 回调函数
*/
public synchronized static void addCallBacks(CallBack callBack) {
if (addCallback) {
SpringContextHolder.CALL_BACKS.add(callBack);
} else {
log.warn("CallBack{} 已无法添加!立即执行", callBack.getCallBackName());
callBack.executor();
}
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
assertContextInjected();
return (T) applicationContext.getBean(name);
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
public static <T> T getBean(Class<T> requiredType) {
assertContextInjected();
return applicationContext.getBean(requiredType);
}
/**
* 获取SpringBoot 配置信息
*
* @param property 属性key
* @param defaultValue 默认值
* @param requiredType 返回类型
* @return /
*/
public static <T> T getProperties(String property, T defaultValue, Class<T> requiredType) {
T result = defaultValue;
try {
result = getBean(Environment.class).getProperty(property, requiredType);
} catch (Exception ignored) {
}
return result;
}
/**
* 获取SpringBoot 配置信息
*
* @param property 属性key
* @return /
*/
public static String getProperties(String property) {
return getProperties(property, null, String.class);
}
/**
* 获取SpringBoot 配置信息
*
* @param property 属性key
* @param requiredType 返回类型
* @return /
*/
public static <T> T getProperties(String property, Class<T> requiredType) {
return getProperties(property, null, requiredType);
}
/**
* 检查ApplicationContext不为空.
*/
private static void assertContextInjected() {
if (applicationContext == null) {
throw new IllegalStateException("applicaitonContext属性未注入, 请在applicationContext" +
".xml中定义SpringContextHolder或在SpringBoot启动类中注册SpringContextHolder.");
}
}
/**
* 清除SpringContextHolder中的ApplicationContext为Null.
*/
private static void clearHolder() {
log.debug("清除SpringContextHolder中的ApplicationContext:"
+ applicationContext);
applicationContext = null;
}
@Override
public void destroy() {
SpringContextHolder.clearHolder();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (SpringContextHolder.applicationContext != null) {
log.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringContextHolder.applicationContext);
}
SpringContextHolder.applicationContext = applicationContext;
if (addCallback) {
for (CallBack callBack : SpringContextHolder.CALL_BACKS) {
callBack.executor();
}
CALL_BACKS.clear();
}
SpringContextHolder.addCallback = false;
}
/**
* 获取 @Service 的所有 bean 名称
*
* @return /
*/
public static List<String> getAllServiceBeanName() {
return new ArrayList<>(Arrays.asList(applicationContext
.getBeanNamesForAnnotation(Service.class)));
}
}

View File

@ -0,0 +1,21 @@
package cn.odboy.config.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Message implements Serializable {
/**
* 消息类型<br/>
* register 注册<br/>
* pullConfig 拉取配置<br/>
* updateConfig 更新配置<br/>
*/
private String type;
}

View File

@ -9,7 +9,7 @@ import org.springframework.stereotype.Component;
@Setter @Setter
@Component @Component
@ConfigurationProperties(prefix = "kenaito.config-center") @ConfigurationProperties(prefix = "kenaito.config-center")
public class ConfigCenterProperties { public class ConfigClientProperties {
private String server; private String server;
private Integer port; private Integer port;
private String dataId; private String dataId;

View File

@ -1,11 +1,9 @@
package cn.odboy.config.context; package cn.odboy.config.context;
import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.FileUtil;
import cn.odboy.config.ConfigCenterProperties; import cn.odboy.config.netty.ConfigClient;
import cn.odboy.config.netty.ConfigCenterClient;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -25,7 +23,7 @@ import java.nio.file.Paths;
*/ */
@Slf4j @Slf4j
@Configuration @Configuration
public class ConfigCenterConfigLoader { public class ClientConfigLoader {
private static final String OS_TYPE_WIN = "win"; private static final String OS_TYPE_WIN = "win";
private static final String OS_TYPE_MAC = "mac"; private static final String OS_TYPE_MAC = "mac";
private static final String DEFAULT_PATH_WIN = "c:\\data"; private static final String DEFAULT_PATH_WIN = "c:\\data";
@ -55,21 +53,22 @@ public class ConfigCenterConfigLoader {
@Override @Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String defaultCacheDir = getDefaultCacheDir(); String defaultCacheDir = getDefaultCacheDir();
String server = environment.getProperty(DEFAULT_CONFIG_NAME_SERVER, String.class, DEFAULT_CONFIG_SERVER); ClientProp.server = environment.getProperty(DEFAULT_CONFIG_NAME_SERVER, String.class, DEFAULT_CONFIG_SERVER);
Integer port = environment.getProperty(DEFAULT_CONFIG_NAME_PORT, Integer.class, DEFAULT_CONFIG_PORT); ClientProp.port = environment.getProperty(DEFAULT_CONFIG_NAME_PORT, Integer.class, DEFAULT_CONFIG_PORT);
String env = environment.getProperty(DEFAULT_CONFIG_NAME_ENV, String.class, DEFAULT_CONFIG_ENV); ClientProp.env = environment.getProperty(DEFAULT_CONFIG_NAME_ENV, String.class, DEFAULT_CONFIG_ENV);
String dataId = environment.getProperty(DEFAULT_CONFIG_NAME_DATA_ID, String.class, DEFAULT_CONFIG_DATA_ID); ClientProp.dataId = environment.getProperty(DEFAULT_CONFIG_NAME_DATA_ID, String.class, DEFAULT_CONFIG_DATA_ID);
String cacheDir = environment.getProperty(DEFAULT_CONFIG_NAME_CACHE_DIR, String.class, defaultCacheDir); ClientProp.cacheDir = environment.getProperty(DEFAULT_CONFIG_NAME_CACHE_DIR, String.class, defaultCacheDir);
validateCacheDirPath(defaultCacheDir, cacheDir); log.info("客户端属性: {}", ClientProp.toPrint());
createCacheDir(cacheDir); validateCacheDirPath(defaultCacheDir, ClientProp.cacheDir);
ConfigCenterClient client = new ConfigCenterClient(); createCacheDir(ClientProp.cacheDir);
try { try {
client.start(server, port); ConfigClient client = new ConfigClient();
client.start(ClientProp.server, ClientProp.port);
} catch (InterruptedException e) { } catch (InterruptedException e) {
log.error("Netty Client Start Error", e); log.error("Netty Client Start Error", e);
throw new RuntimeException(e); throw new RuntimeException(e);
} }
// ConfigCenterClient client = new ConfigCenterClient("http://your-config-center-url/config"); // ConfigClient client = new ConfigClient("http://your-config-center-url/config");
// Map<String, Object> configKv = client.fetchConfig(); // Map<String, Object> configKv = client.fetchConfig();
// MapPropertySource propertySource = new MapPropertySource("configCenter", configKv); // MapPropertySource propertySource = new MapPropertySource("configCenter", configKv);
// environment.getPropertySources().addFirst(propertySource); // environment.getPropertySources().addFirst(propertySource);

View File

@ -0,0 +1,25 @@
package cn.odboy.config.context;
/**
* 客户端属性
*
* @author odboy
* @date 2024-12-03
*/
public class ClientProp {
public static String server;
public static Integer port;
public static String env;
public static String dataId;
public static String cacheDir;
public static String toPrint() {
return String.format("server: %s, port: %s, env: %s, dataId: %s, cacheDir: %s",
server,
port,
env,
dataId,
cacheDir
);
}
}

View File

@ -1,25 +0,0 @@
package cn.odboy.config.netty;
import cn.odboy.config.model.ConfigKv;
import cn.odboy.config.util.ProtostuffUtil;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
public class ConfigCenterClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("收到服务器消息:" + msg);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("MyClientHandler发送数据");
//ctx.writeAndFlush("测试String编解码");
//测试对象编解码
//ctx.writeAndFlush(new User(1,"zhuge"));
//测试用protostuff对对象编解码
ByteBuf buf = Unpooled.copiedBuffer(ProtostuffUtil.serializer(new ConfigKv("app.config", "张三")));
ctx.writeAndFlush(buf);
}
}

View File

@ -1,5 +1,4 @@
package cn.odboy.config.netty; package cn.odboy.config.netty;
import cn.odboy.config.ConfigCenterProperties;
import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelInitializer;
@ -11,7 +10,7 @@ import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
public class ConfigCenterClient { public class ConfigClient {
public void start(String server, Integer port) throws InterruptedException { public void start(String server, Integer port) throws InterruptedException {
EventLoopGroup group = new NioEventLoopGroup(); EventLoopGroup group = new NioEventLoopGroup();
try { try {
@ -21,7 +20,7 @@ public class ConfigCenterClient {
@Override @Override
protected void initChannel(SocketChannel ch) throws Exception { protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline(); ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new ConfigCenterClientHandler()); pipeline.addLast(new ConfigClientHandler());
} }
}); });
log.info("Netty Client Start..."); log.info("Netty Client Start...");

View File

@ -0,0 +1,41 @@
package cn.odboy.config.netty;
import cn.odboy.config.model.ConfigKv;
import cn.odboy.config.util.ProtostuffUtil;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
public class ConfigClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
System.err.println("当Channel已经注册到它的EventLoop并且能够处理I/O时被调用");
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
System.err.println("当Channel从它的EventLoop注销并且无法处理任何I/O时被调用");
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.err.println("当Channel处于活动状态已经连接到它的远程节点时被调用");
System.err.println("MyClientHandler发送数据");
//ctx.writeAndFlush("测试String编解码");
// 测试对象编解码
//ctx.writeAndFlush(new ConfigKv("app.config", "张三"));
// 测试用protostuff对对象编解码
ByteBuf buf = Unpooled.copiedBuffer(ProtostuffUtil.serializer(new ConfigKv("app.config", "张三")));
ctx.writeAndFlush(buf);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.err.println("当Channel离开活动状态并且不再连接到它的远程节点时被调用");
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.err.println("收到服务器消息:" + msg);
}
}

View File

@ -2,6 +2,6 @@ kenaito:
config-center: config-center:
server: 127.0.0.1 server: 127.0.0.1
port: 28002 port: 28002
data-id: kenaito-config-service data-id: kenaito-config-demo
cache-dir: c:\\data cache-dir: c:\\data
env: daily env: daily

View File

@ -2,6 +2,6 @@ kenaito:
config-center: config-center:
server: 127.0.0.1 server: 127.0.0.1
port: 28002 port: 28002
data-id: kenaito-config-service data-id: kenaito-config-demo
cache-dir: /home/admin/data cache-dir: /home/admin/data
env: production env: production

View File

@ -2,6 +2,6 @@ kenaito:
config-center: config-center:
server: 127.0.0.1 server: 127.0.0.1
port: 28002 port: 28002
data-id: kenaito-config-service data-id: kenaito-config-demo
cache-dir: /home/admin/data cache-dir: /home/admin/data
env: stage env: stage

View File

@ -7,15 +7,17 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInboundHandlerAdapter;
public class ConfigServerHandler extends ChannelInboundHandlerAdapter { public class ConfigServerHandler extends ChannelInboundHandlerAdapter {
@Override @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.err.println("当从Channel读取数据时被调用");
//System.out.println("从客户端读取到String" + msg.toString()); //System.out.println("从客户端读取到String" + msg.toString());
//System.out.println("从客户端读取到Object" + ((User)msg).toString()); //System.out.println("从客户端读取到Object" + ((User)msg).toString());
//测试用protostuff对对象编解码 //测试用protostuff对对象编解码
ByteBuf buf = (ByteBuf) msg; ByteBuf buf = (ByteBuf) msg;
byte[] bytes = new byte[buf.readableBytes()]; byte[] bytes = new byte[buf.readableBytes()];
buf.readBytes(bytes); buf.readBytes(bytes);
System.out.println("从客户端读取到Object" + ProtostuffUtil.deserializer(bytes, ConfigKv.class)); System.err.println("从客户端读取到Object" + ProtostuffUtil.deserializer(bytes, ConfigKv.class));
} }
@Override @Override