作者 crossoverJie

:sparkles: Introducing new features.zk

@@ -16,6 +16,12 @@ @@ -16,6 +16,12 @@
16 <dependency> 16 <dependency>
17 <groupId>com.crossoverjie.netty</groupId> 17 <groupId>com.crossoverjie.netty</groupId>
18 <artifactId>netty-action-common</artifactId> 18 <artifactId>netty-action-common</artifactId>
  19 + <exclusions>
  20 + <exclusion>
  21 + <artifactId>log4j</artifactId>
  22 + <groupId>log4j</groupId>
  23 + </exclusion>
  24 + </exclusions>
19 </dependency> 25 </dependency>
20 26
21 27
@@ -60,6 +66,15 @@ @@ -60,6 +66,15 @@
60 <groupId>ch.qos.logback</groupId> 66 <groupId>ch.qos.logback</groupId>
61 <artifactId>logback-classic</artifactId> 67 <artifactId>logback-classic</artifactId>
62 </dependency> 68 </dependency>
  69 + <dependency>
  70 + <groupId>org.slf4j</groupId>
  71 + <artifactId>slf4j-api</artifactId>
  72 + </dependency>
  73 + <dependency>
  74 + <groupId>ch.qos.logback</groupId>
  75 + <artifactId>logback-core</artifactId>
  76 + </dependency>
  77 +
63 78
64 <dependency> 79 <dependency>
65 <groupId>junit</groupId> 80 <groupId>junit</groupId>
1 package com.crossoverjie.netty.action.zk; 1 package com.crossoverjie.netty.action.zk;
2 2
  3 +import com.crossoverjie.netty.action.zk.thread.RegistryZK;
  4 +import com.crossoverjie.netty.action.zk.util.AppConfiguration;
  5 +import com.crossoverjie.netty.action.zk.util.ZKUtil;
3 import org.slf4j.Logger; 6 import org.slf4j.Logger;
4 import org.slf4j.LoggerFactory; 7 import org.slf4j.LoggerFactory;
  8 +import org.springframework.beans.factory.annotation.Autowired;
5 import org.springframework.boot.CommandLineRunner; 9 import org.springframework.boot.CommandLineRunner;
6 import org.springframework.boot.SpringApplication; 10 import org.springframework.boot.SpringApplication;
7 import org.springframework.boot.autoconfigure.SpringBootApplication; 11 import org.springframework.boot.autoconfigure.SpringBootApplication;
8 12
  13 +import java.net.InetAddress;
  14 +
9 /** 15 /**
10 * @author crossoverJie 16 * @author crossoverJie
11 */ 17 */
@@ -14,14 +20,24 @@ public class Application implements CommandLineRunner{ @@ -14,14 +20,24 @@ public class Application implements CommandLineRunner{
14 20
15 private final static Logger LOGGER = LoggerFactory.getLogger(Application.class); 21 private final static Logger LOGGER = LoggerFactory.getLogger(Application.class);
16 22
  23 + @Autowired
  24 + private AppConfiguration appConfiguration ;
17 25
  26 + @Autowired
  27 + private static ZKUtil zkUtil ;
18 28
19 public static void main(String[] args) { 29 public static void main(String[] args) {
20 SpringApplication.run(Application.class, args); 30 SpringApplication.run(Application.class, args);
21 LOGGER.info("启动应用成功"); 31 LOGGER.info("启动应用成功");
  32 +
22 } 33 }
23 34
24 @Override 35 @Override
25 public void run(String... args) throws Exception { 36 public void run(String... args) throws Exception {
  37 + //获得本机IP
  38 + String addr = InetAddress.getLocalHost().getHostAddress();
  39 + Thread thread = new Thread(new RegistryZK(addr, appConfiguration.getPort()));
  40 + thread.setName("registry-zk");
  41 + thread.start() ;
26 } 42 }
27 } 43 }
  1 +package com.crossoverjie.netty.action.zk.cache;
  2 +
  3 +import com.crossoverjie.netty.action.zk.util.ZKUtil;
  4 +import com.google.common.cache.LoadingCache;
  5 +import org.springframework.beans.factory.annotation.Autowired;
  6 +import org.springframework.stereotype.Component;
  7 +
  8 +import java.util.ArrayList;
  9 +import java.util.List;
  10 +import java.util.Map;
  11 +import java.util.concurrent.atomic.AtomicLong;
  12 +
  13 +/**
  14 + * Function: 服务器节点缓存
  15 + *
  16 + * @author crossoverJie
  17 + * Date: 2018/8/19 01:31
  18 + * @since JDK 1.8
  19 + */
  20 +@Component
  21 +public class ServerCache {
  22 +
  23 +
  24 + @Autowired
  25 + private LoadingCache<String,String> cache ;
  26 +
  27 + @Autowired
  28 + private ZKUtil zkUtil ;
  29 +
  30 + private AtomicLong index = new AtomicLong() ;
  31 +
  32 +
  33 + public void addCache(String key){
  34 + cache.put(key, key);
  35 + }
  36 +
  37 +
  38 + /**
  39 + * 更新所有缓存/先删除 再新增
  40 + * @param currentChilds
  41 + */
  42 + public void updateCache(List<String> currentChilds){
  43 + cache.invalidateAll() ;
  44 + for (String currentChild : currentChilds) {
  45 + String key = currentChild.split("-")[1] ;
  46 + addCache(key) ;
  47 + }
  48 + }
  49 +
  50 +
  51 + /**
  52 + * 获取所有的服务列表
  53 + * @return
  54 + */
  55 + public List<String> getAll(){
  56 +
  57 + List<String> list = new ArrayList<>() ;
  58 +
  59 + if (cache.size() == 0){
  60 + List<String> allNode = zkUtil.getAllNode();
  61 + for (String node : allNode) {
  62 + String key = node.split("-")[1] ;
  63 + addCache(key) ;
  64 + }
  65 + }
  66 + for (Map.Entry<String, String> entry : cache.asMap().entrySet()) {
  67 + list.add(entry.getKey());
  68 + }
  69 + return list ;
  70 +
  71 + }
  72 +
  73 + /**
  74 + * 选取服务器
  75 + * @return
  76 + */
  77 + public String selectServer(){
  78 + List<String> all = getAll();
  79 + if (all.size() == 0){
  80 + throw new RuntimeException("路由列表为空") ;
  81 + }
  82 + Long position = index.incrementAndGet() % all.size();
  83 + if (position < 0){
  84 + position = 0L ;
  85 + }
  86 +
  87 + return all.get(position.intValue()) ;
  88 + }
  89 +}
  1 +package com.crossoverjie.netty.action.zk.config;
  2 +
  3 +import com.crossoverjie.netty.action.zk.util.AppConfiguration;
  4 +import com.google.common.cache.CacheBuilder;
  5 +import com.google.common.cache.CacheLoader;
  6 +import com.google.common.cache.LoadingCache;
  7 +import org.I0Itec.zkclient.ZkClient;
  8 +import org.springframework.beans.factory.annotation.Autowired;
  9 +import org.springframework.beans.factory.annotation.Value;
  10 +import org.springframework.context.annotation.Bean;
  11 +import org.springframework.context.annotation.Configuration;
  12 +
  13 +/**
  14 + * Function:
  15 + *
  16 + * @author crossoverJie
  17 + * Date: 2018/8/24 01:28
  18 + * @since JDK 1.8
  19 + */
  20 +@Configuration
  21 +public class AppConfig {
  22 +
  23 + @Autowired
  24 + private AppConfiguration appConfiguration ;
  25 +
  26 + @Bean
  27 + public ZkClient buildZKClient(){
  28 + return new ZkClient(appConfiguration.getZkAddr(), 5000);
  29 + }
  30 +
  31 +
  32 + @Bean
  33 + public LoadingCache<String,String> buildCache(){
  34 + return CacheBuilder.newBuilder()
  35 + .build(new CacheLoader<String, String>() {
  36 + @Override
  37 + public String load(String s) throws Exception {
  38 + return null;
  39 + }
  40 + });
  41 + }
  42 +}
  1 +package com.crossoverjie.netty.action.zk.thread;
  2 +
  3 +import com.crossoverjie.netty.action.zk.util.AppConfiguration;
  4 +import com.crossoverjie.netty.action.zk.util.SpringBeanFactory;
  5 +import com.crossoverjie.netty.action.zk.util.ZKUtil;
  6 +import org.slf4j.Logger;
  7 +import org.slf4j.LoggerFactory;
  8 +
  9 +/**
  10 + * Function:
  11 + *
  12 + * @author crossoverJie
  13 + * Date: 2018/8/24 01:37
  14 + * @since JDK 1.8
  15 + */
  16 +public class RegistryZK implements Runnable {
  17 +
  18 + private static Logger logger = LoggerFactory.getLogger(RegistryZK.class);
  19 +
  20 + private ZKUtil zkUtil;
  21 +
  22 + private AppConfiguration appConfiguration ;
  23 +
  24 + private String ip;
  25 + private int port;
  26 +
  27 + public RegistryZK(String ip, int port) {
  28 + this.ip = ip;
  29 + this.port = port;
  30 + zkUtil = SpringBeanFactory.getBean(ZKUtil.class) ;
  31 + appConfiguration = SpringBeanFactory.getBean(AppConfiguration.class) ;
  32 + }
  33 +
  34 + @Override
  35 + public void run() {
  36 +
  37 + //创建父节点
  38 + zkUtil.createRootNode();
  39 +
  40 + //是否要将自己注册到 ZK
  41 + if (appConfiguration.isZkSwitch()){
  42 + String path = appConfiguration.getZkRoot() + "/ip-" + ip + ":" + port;
  43 + zkUtil.createNode(path, path);
  44 + logger.info("注册 zookeeper 成功,msg=[{}]", path);
  45 + }
  46 +
  47 + //注册监听服务
  48 + zkUtil.subscribeEvent(appConfiguration.getZkRoot());
  49 +
  50 + }
  51 +}
  1 +package com.crossoverjie.netty.action.zk.util;
  2 +
  3 +import org.springframework.beans.factory.annotation.Value;
  4 +import org.springframework.stereotype.Component;
  5 +
  6 +/**
  7 + * Function:
  8 + *
  9 + * @author crossoverJie
  10 + * Date: 2018/8/24 01:43
  11 + * @since JDK 1.8
  12 + */
  13 +@Component
  14 +public class AppConfiguration {
  15 +
  16 + @Value("${app.zk.root}")
  17 + private String zkRoot;
  18 +
  19 + @Value("${app.zk.addr}")
  20 + private String zkAddr;
  21 +
  22 + @Value("${app.zk.switch}")
  23 + private boolean zkSwitch;
  24 +
  25 + @Value("${server.port}")
  26 + private int port;
  27 +
  28 + public int getPort() {
  29 + return port;
  30 + }
  31 +
  32 + public void setPort(int port) {
  33 + this.port = port;
  34 + }
  35 +
  36 + public String getZkRoot() {
  37 + return zkRoot;
  38 + }
  39 +
  40 + public void setZkRoot(String zkRoot) {
  41 + this.zkRoot = zkRoot;
  42 + }
  43 +
  44 + public String getZkAddr() {
  45 + return zkAddr;
  46 + }
  47 +
  48 + public void setZkAddr(String zkAddr) {
  49 + this.zkAddr = zkAddr;
  50 + }
  51 +
  52 + public boolean isZkSwitch() {
  53 + return zkSwitch;
  54 + }
  55 +
  56 + public void setZkSwitch(boolean zkSwitch) {
  57 + this.zkSwitch = zkSwitch;
  58 + }
  59 +}
  1 +package com.crossoverjie.netty.action.zk.util;
  2 +
  3 +import org.springframework.beans.BeansException;
  4 +import org.springframework.context.ApplicationContext;
  5 +import org.springframework.context.ApplicationContextAware;
  6 +import org.springframework.stereotype.Component;
  7 +
  8 +@Component
  9 +public final class SpringBeanFactory implements ApplicationContextAware{
  10 + private static ApplicationContext context;
  11 +
  12 + public static <T> T getBean(Class<T> c){
  13 + return context.getBean(c);
  14 + }
  15 +
  16 +
  17 + public static <T> T getBean(String name,Class<T> clazz){
  18 + return context.getBean(name,clazz);
  19 + }
  20 +
  21 + @Override
  22 + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  23 + context = applicationContext;
  24 + }
  25 +
  26 +
  27 +}
  1 +package com.crossoverjie.netty.action.zk.util;
  2 +
  3 +import com.alibaba.fastjson.JSON;
  4 +import com.crossoverjie.netty.action.zk.cache.ServerCache;
  5 +import org.I0Itec.zkclient.IZkChildListener;
  6 +import org.I0Itec.zkclient.ZkClient;
  7 +import org.slf4j.Logger;
  8 +import org.slf4j.LoggerFactory;
  9 +import org.springframework.beans.factory.annotation.Autowired;
  10 +import org.springframework.stereotype.Component;
  11 +
  12 +import java.util.List;
  13 +
  14 +/**
  15 + * Function: Zookeeper 工具
  16 + *
  17 + * @author crossoverJie
  18 + * Date: 2018/8/19 00:33
  19 + * @since JDK 1.8
  20 + */
  21 +@Component
  22 +public class ZKUtil {
  23 +
  24 + private static Logger logger = LoggerFactory.getLogger(ZKUtil.class);
  25 +
  26 +
  27 + @Autowired
  28 + private ZkClient zkClient;
  29 +
  30 + @Autowired
  31 + private AppConfiguration appConfiguration ;
  32 +
  33 + @Autowired
  34 + private ServerCache serverCache ;
  35 +
  36 +
  37 + /**
  38 + * 创建父级节点
  39 + */
  40 + public void createRootNode(){
  41 + boolean exists = zkClient.exists(appConfiguration.getZkRoot());
  42 + if (exists){
  43 + return;
  44 + }
  45 +
  46 + //创建 root
  47 + zkClient.createPersistent(appConfiguration.getZkRoot()) ;
  48 + }
  49 +
  50 + /**
  51 + * 写入指定节点 临时目录
  52 + *
  53 + * @param path
  54 + * @param value
  55 + */
  56 + public void createNode(String path, String value) {
  57 + zkClient.createEphemeral(path, value);
  58 + }
  59 +
  60 +
  61 + /**
  62 + * 监听事件
  63 + *
  64 + * @param path
  65 + */
  66 + public void subscribeEvent(String path) {
  67 + zkClient.subscribeChildChanges(path, new IZkChildListener() {
  68 + @Override
  69 + public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
  70 + logger.info("清除/更新本地缓存 parentPath=【{}】,currentChilds=【{}】", parentPath,currentChilds.toString());
  71 +
  72 + //更新所有缓存/先删除 再新增
  73 + serverCache.updateCache(currentChilds) ;
  74 + }
  75 + });
  76 +
  77 +
  78 + }
  79 +
  80 +
  81 + /**
  82 + * 获取所有注册节点
  83 + * @return
  84 + */
  85 + public List<String> getAllNode(){
  86 + List<String> children = zkClient.getChildren("/route");
  87 + logger.info("查询所有节点成功=【{}】", JSON.toJSONString(children));
  88 + return children;
  89 + }
  90 +
  91 + /**
  92 + * 关闭 ZK
  93 + */
  94 + public void closeZK() {
  95 + logger.info("正在关闭 ZK");
  96 + zkClient.close();
  97 + logger.info("关闭 ZK 成功");
  98 +
  99 + }
  100 +}
@@ -7,3 +7,14 @@ server.port=8083 @@ -7,3 +7,14 @@ server.port=8083
7 swagger.enable = true 7 swagger.enable = true
8 8
9 logging.level.root=info 9 logging.level.root=info
  10 +
  11 +
  12 +
  13 +# 是否注册 zk
  14 +app.zk.switch=true
  15 +
  16 +# zk 地址
  17 +app.zk.addr=10.1.241.103:2181
  18 +
  19 +# zk 注册根节点
  20 +app.zk.root=/route