ZooKeeper服務發現客戶端

壹頁書發表於2016-05-27
單位想把那套ZooKeeper叢集用起來.
作為配置中心,一旦出現問題,所有服務都是中斷的.
尤其又涉及ACL
想來想去,我覺得這個客戶端還是自己封裝,安全係數大些.
好長時間也沒有寫過程式碼了,確實感覺很生疏.寫的邏輯稍微有點亂.
別到時候上線了,因為自己的客戶端引發問題.那就尷尬了.

要求:
1.在客戶端實現負載均衡
2.客戶端ACL密碼加密
3.在客戶端實現快取,如果ZK掛了,還能繼續提供服務
4.如果服務提供方在ZK正常註冊,但是服務呼叫方出現呼叫異常,需要將這個服務在快取做一個標識.在一段時間內,不提供這個服務地址.(預設5分鐘)
5.這段程式碼憋出事兒..心中默唸一百遍..

程式碼結構:


zoo.properties
server=192.168.1.105:2181,192.168.1.106:2181,192.168.1.107:2181,192.168.1.108:2181,192.168.1.109:2181,192.168.1.110:2181
invoker.vdfs=/service/vdfs/upload/dx
r=pqrMCxdhQUhKEgMFZoJG3vM2tDdIGqbA/rlZt9RkL8s=
rw=MqnMrPsX3c8RX7b+NES4mQ==
provider./service/vdfs/upload/dx=http://192.168.1.111
nodename=192.168.16.114:8080

Metadata,提供配置檔案加解密和配置檔案提取
  1. package com.vv.zkClient;  
  2.   
  3. import java.io.IOException;  
  4. import java.io.InputStream;  
  5. import java.security.SecureRandom;  
  6. import java.util.Enumeration;  
  7. import java.util.Map;  
  8. import java.util.Properties;  
  9.   
  10. import javax.crypto.Cipher;  
  11. import javax.crypto.SecretKey;  
  12. import javax.crypto.SecretKeyFactory;  
  13. import javax.crypto.spec.DESKeySpec;  
  14.   
  15. import org.apache.commons.codec.binary.Base64;  
  16. import org.jboss.netty.util.internal.ConcurrentHashMap;  
  17.   
  18. public class Metadata {  
  19.     private static volatile Metadata META = null;  
  20.     public static String decrypt(byte[] content, String key) {  
  21.         try {  
  22.             SecureRandom random = new SecureRandom();  
  23.             DESKeySpec desKey = new DESKeySpec(key.getBytes());  
  24.             SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");  
  25.             SecretKey securekey = keyFactory.generateSecret(desKey);  
  26.             Cipher cipher = Cipher.getInstance("DES");  
  27.             cipher.init(Cipher.DECRYPT_MODE, securekey, random);  
  28.             byte[] result = cipher.doFinal(content);  
  29.             return new String(result);  
  30.         } catch (Throwable e) {  
  31.             e.printStackTrace();  
  32.         }  
  33.         return null;  
  34.     }  
  35.     public static byte[] encrypt(String content, String key) {  
  36.         try {  
  37.             SecureRandom random = new SecureRandom();  
  38.             DESKeySpec desKey = new DESKeySpec(key.getBytes());  
  39.             SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");  
  40.             SecretKey securekey = keyFactory.generateSecret(desKey);  
  41.             Cipher cipher = Cipher.getInstance("DES");  
  42.             cipher.init(Cipher.ENCRYPT_MODE, securekey, random);  
  43.             byte[] result = cipher.doFinal(content.getBytes());  
  44.             return result;  
  45.         } catch (Throwable e) {  
  46.             e.printStackTrace();  
  47.         }  
  48.         return null;  
  49.     }  
  50.     public static Metadata getInstance() {  
  51.         if (META == null) {  
  52.             synchronized (Metadata.class) {  
  53.   
  54.                 if (META == null) {  
  55.                     META = new Metadata();  
  56.                 }  
  57.             }  
  58.         }  
  59.         return META;  
  60.     }  
  61.   
  62.     private String connectionString = null;  
  63.     private Map<String, String> invokerMap = new ConcurrentHashMap<String, String>();  
  64.     private String key = "12344321";  
  65.   
  66.     private String nodename = null;  
  67.   
  68.     private Properties p = new Properties();  
  69.   
  70.     private Map<String, String> providerMap = new ConcurrentHashMap<String, String>();  
  71.   
  72.     private String readOnlyPassword = null;  
  73.   
  74.     private String readwritePassword = null;  
  75.   
  76.     public Metadata() {  
  77.         InputStream in = Metadata.class.getClassLoader().getResourceAsStream("zoo.properties");  
  78.         init(in);  
  79.     }  
  80.   
  81.     public String getConnectionString() {  
  82.         return connectionString;  
  83.     }  
  84.   
  85.     public Map<String, String> getInvokerMap() {  
  86.         return invokerMap;  
  87.     }  
  88.   
  89.     public String getLocal() {  
  90.         return nodename;  
  91.     }  
  92.   
  93.     public Map<String, String> getProviderMap() {  
  94.         return providerMap;  
  95.     }  
  96.   
  97.     public String getReadOnlyPassword() {  
  98.         String password = new String(decrypt(Base64.decodeBase64(this.readOnlyPassword.getBytes()), this.key));  
  99.           
  100.         return password;  
  101.     }  
  102.   
  103.     public String getReadwritePassword() {  
  104.         String password = new String(decrypt(Base64.decodeBase64(this.readwritePassword.getBytes()), this.key));  
  105.           
  106.         return password;  
  107.     }  
  108.   
  109.     private void init(InputStream in) {  
  110.         try {  
  111.             p.load(in);  
  112.             connectionString = p.getProperty("server""");  
  113.             readOnlyPassword = p.getProperty("r""");  
  114.             readwritePassword = p.getProperty("rw""");  
  115.             nodename = p.getProperty("nodename""");  
  116.             Enumeration<Object> enums = p.keys();  
  117.             while (enums.hasMoreElements()) {  
  118.                 String key = (String) enums.nextElement();  
  119.                 if (key.startsWith("invoker.")) {  
  120.                     invokerMap.put(key.replace("invoker."""), p.getProperty(key));  
  121.                 } else if (key.startsWith("provider.")) {  
  122.                     providerMap.put(key.replace("provider."""), p.getProperty(key));  
  123.                 }  
  124.             }  
  125.   
  126.             in.close();  
  127.         } catch (IOException e) {  
  128.             e.printStackTrace();  
  129.         }  
  130.     }  
  131.       
  132.       
  133.     public static void main(String[] args) {  
  134.           
  135.     }  
  136. }  


ServiceProvider 服務提供方呼叫,將自己註冊到配置中心

  1. package com.vv.zkClient;  
  2.   
  3. import java.text.SimpleDateFormat;  
  4. import java.util.Date;  
  5. import java.util.concurrent.ScheduledThreadPoolExecutor;  
  6. import java.util.concurrent.TimeUnit;  
  7.   
  8. import org.apache.curator.framework.CuratorFramework;  
  9. import org.apache.curator.framework.CuratorFrameworkFactory;  
  10. import org.apache.curator.retry.RetryUntilElapsed;  
  11. import org.apache.zookeeper.CreateMode;  
  12. import org.slf4j.Logger;  
  13. import org.slf4j.LoggerFactory;  
  14.   
  15. public class ServiceProvider implements Runnable {  
  16.     private static Logger LOGGER = LoggerFactory.getLogger(ServiceProvider.class);  
  17.   
  18.     private static volatile ServiceProvider SERVICEPROVIDER = null;  
  19.     public static ServiceProvider getInstance() {  
  20.         if (SERVICEPROVIDER == null) {  
  21.             synchronized (ServiceProvider.class) {  
  22.   
  23.                 if (SERVICEPROVIDER == null) {  
  24.                     SERVICEPROVIDER = new ServiceProvider();  
  25.                 }  
  26.             }  
  27.         }  
  28.         return SERVICEPROVIDER;  
  29.     }  
  30.       
  31.     private CuratorFramework client;  
  32.   
  33.     private Metadata meta;  
  34.   
  35.     private ScheduledThreadPoolExecutor threadPool = new ScheduledThreadPoolExecutor(1);  
  36.   
  37.     private ServiceProvider() {  
  38.         this.meta = Metadata.getInstance();  
  39.         connection();  
  40.         threadPool.scheduleAtFixedRate(this15, TimeUnit.SECONDS);  
  41.     }  
  42.   
  43.     private void connection() {  
  44.         try {  
  45.             this.client = CuratorFrameworkFactory.newClient(meta.getConnectionString(),  
  46.                     new RetryUntilElapsed(20001000));  
  47.             client.start();  
  48.             client.getZookeeperClient().getZooKeeper().addAuthInfo("digest", meta.getReadwritePassword().getBytes());  
  49.         } catch (Exception e) {  
  50.             e.printStackTrace();  
  51.             LOGGER.error(e.getMessage());  
  52.         }  
  53.     }  
  54.   
  55.     public void run() {  
  56.         try {  
  57.             for (String serviceNode : meta.getProviderMap().keySet()) {  
  58.                 String serviceURL = meta.getProviderMap().get(serviceNode);  
  59.                 String serviceName = serviceNode + "/" + meta.getLocal();  
  60.                 if (client.checkExists().forPath(serviceName) == null) {  
  61.                     String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "||" + serviceURL;  
  62.                     client.create().withMode(CreateMode.EPHEMERAL).forPath(serviceName, date.getBytes());  
  63.                     LOGGER.info("Created Node->\"{}\",Node Data->\"{}\"", serviceName,date);  
  64.                 }  
  65.             }  
  66.         } catch (Exception e) {  
  67.             e.printStackTrace();  
  68.             client.close();  
  69.             connection();  
  70.             LOGGER.error(e.getMessage());  
  71.         }  
  72.     }  
  73.   
  74.     public static void main(String[] args) throws InterruptedException {  
  75.         ServiceProvider.getInstance();  
  76.         Thread.sleep(Integer.MAX_VALUE);  
  77.     }  
  78. }  


ServiceInvoker 服務呼叫方使用,獲取服務連線地址

  1. package com.vv.zkClient;  
  2.   
  3. import java.util.Iterator;  
  4. import java.util.List;  
  5. import java.util.Map;  
  6. import java.util.concurrent.Callable;  
  7. import java.util.concurrent.ConcurrentHashMap;  
  8. import java.util.concurrent.CopyOnWriteArrayList;  
  9. import java.util.concurrent.CountDownLatch;  
  10. import java.util.concurrent.ExecutionException;  
  11. import java.util.concurrent.ExecutorService;  
  12. import java.util.concurrent.Executors;  
  13.   
  14. import org.apache.curator.framework.CuratorFramework;  
  15. import org.apache.curator.framework.CuratorFrameworkFactory;  
  16. import org.apache.curator.framework.recipes.cache.PathChildrenCache;  
  17. import org.apache.curator.framework.recipes.cache.PathChildrenCache.StartMode;  
  18. import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;  
  19. import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;  
  20. import org.apache.curator.retry.RetryUntilElapsed;  
  21. import org.slf4j.Logger;  
  22. import org.slf4j.LoggerFactory;  
  23.   
  24. class Service {  
  25.     private long lastErrorTime = -1;  
  26.     private String name = null;  
  27.     private String url = null;  
  28.   
  29.     @Override  
  30.     public boolean equals(Object obj) {  
  31.         Service s = (Service) obj;  
  32.         return (this.getName() + this.getUrl()).equals(s.getName() + s.getUrl());  
  33.     }  
  34.   
  35.     public long getLastErrorTime() {  
  36.         return lastErrorTime;  
  37.     }  
  38.   
  39.     public String getName() {  
  40.         return name;  
  41.     }  
  42.   
  43.     public String getUrl() {  
  44.         return url;  
  45.     }  
  46.   
  47.     public void setLastErrorTime(long lastErrorTime) {  
  48.         this.lastErrorTime = lastErrorTime;  
  49.     }  
  50.   
  51.     public void setName(String name) {  
  52.         this.name = name;  
  53.     }  
  54.   
  55.     public void setUrl(String url) {  
  56.         this.url = url;  
  57.     }  
  58.   
  59. }  
  60.   
  61. public class ServiceInvoker {  
  62.     private static ServiceInvoker INVOKER = null;  
  63.   
  64.     private static Logger LOGGER = LoggerFactory.getLogger(ServiceInvoker.class);  
  65.     public static ServiceInvoker getInstance() {  
  66.         if (INVOKER == null) {  
  67.             synchronized (ServiceInvoker.class) {  
  68.   
  69.                 if (INVOKER == null) {  
  70.                     INVOKER = new ServiceInvoker();  
  71.                 }  
  72.             }  
  73.         }  
  74.         return INVOKER;  
  75.     }  
  76.   
  77.     private CuratorFramework client;  
  78.     private CountDownLatch isInitialized = new CountDownLatch(1);  
  79.     private Map<String, Iterator<Service>> itMap = new ConcurrentHashMap<String, Iterator<Service>>();  
  80.   
  81.     private Metadata meta;  
  82.   
  83.     private Map<String, List<Service>> providerMap = new ConcurrentHashMap<String, List<Service>>();  
  84.   
  85.     private ExecutorService threadPool = Executors.newSingleThreadExecutor();  
  86.   
  87.     private ServiceInvoker() {  
  88.         this.meta = Metadata.getInstance();  
  89.         connection();  
  90.         listener();  
  91.     }  
  92.   
  93.     private void connection() {  
  94.         try {  
  95.             this.client = CuratorFrameworkFactory.newClient(meta.getConnectionString(),  
  96.                     new RetryUntilElapsed(20001000));  
  97.             client.start();  
  98.             client.getZookeeperClient().getZooKeeper().addAuthInfo("digest", meta.getReadOnlyPassword().getBytes());  
  99.         } catch (Exception e) {  
  100.             e.printStackTrace();  
  101.             LOGGER.error(e.getMessage());  
  102.         }  
  103.     }  
  104.   
  105.     public String get(final String serviceNode) {  
  106.   
  107.         Callable<String> c = new Callable<String>() {  
  108.   
  109.             public String call() throws Exception {  
  110.                 List<Service> list = providerMap.get(serviceNode);  
  111.                 if (list == null) {  
  112.                     list = new CopyOnWriteArrayList<Service>();  
  113.                     providerMap.put(serviceNode, list);  
  114.                 }  
  115.   
  116.                 Iterator<Service> it = itMap.get(serviceNode);  
  117.                 if (it == null || !it.hasNext()) {  
  118.                     it = list.iterator();  
  119.                     itMap.put(serviceNode, it);  
  120.                 }  
  121.                 if (!it.hasNext()) {  
  122.                     LOGGER.error("節點:\"{}\",沒有任何可用服務", serviceNode);  
  123.                     return "";  
  124.                 }  
  125.                 Service service = it.next();  
  126.                 long now = System.currentTimeMillis();  
  127.                 int retryCount = 5;  
  128.                 while (service.getLastErrorTime() != -1 && (now - service.getLastErrorTime()) < 1000 * 60 * 5) {  
  129.                     retryCount--;  
  130.                     if (retryCount == 0) {  
  131.                         LOGGER.error("節點:\"{}\",沒有任何可用服務", serviceNode);  
  132.                         return "";  
  133.                     }  
  134.                     if (it.hasNext()) {  
  135.                         service = it.next();  
  136.                     } else {  
  137.                         it = providerMap.get(serviceNode).iterator();  
  138.                         itMap.put(serviceNode, it);  
  139.                     }  
  140.                 }  
  141.                 return service.getUrl();  
  142.             }  
  143.   
  144.         };  
  145.   
  146.         String serviceUrl = "";  
  147.   
  148.         try {  
  149.             isInitialized.await();  
  150.             serviceUrl = threadPool.submit(c).get();  
  151.         } catch (InterruptedException e) {  
  152.             e.printStackTrace();  
  153.         } catch (ExecutionException e) {  
  154.             e.printStackTrace();  
  155.         }  
  156.         return serviceUrl;  
  157.     }  
  158.   
  159.     private void listener() {  
  160.         for (String serviceNode : meta.getInvokerMap().values()) {  
  161.   
  162.             PathChildrenCache cache = new PathChildrenCache(client, serviceNode, true);  
  163.             try {  
  164.                 cache.start(StartMode.POST_INITIALIZED_EVENT);  
  165.             } catch (Exception e) {  
  166.                 e.printStackTrace();  
  167.             }  
  168.             cache.getListenable().addListener(new PathChildrenCacheListener() {  
  169.   
  170.                 public void childEvent(CuratorFramework arg0, final PathChildrenCacheEvent event) throws Exception {  
  171.   
  172.                     final String type = event.getType().name();  
  173.                     if (type.equals("INITIALIZED")) {  
  174.                         LOGGER.info("ZooKeeper資料初始化完成:INITIALIZED");  
  175.                         isInitialized.countDown();  
  176.                         return;  
  177.                     }  
  178.                     final String data = event.getData().getPath();  
  179.                     final String serviceNode = data.substring(0, data.lastIndexOf("/"));  
  180.   
  181.                     final String serviceUrl = new String(event.getData().getData()).split("\\|\\|")[1];  
  182.                     Runnable r = new Runnable() {  
  183.                         public void run() {  
  184.                             List<Service> list = providerMap.get(serviceNode);  
  185.                             if (list == null) {  
  186.                                 list = new CopyOnWriteArrayList<Service>();  
  187.                                 providerMap.put(serviceNode, list);  
  188.                             }  
  189.                             Service s = new Service();  
  190.                             s.setName(serviceNode);  
  191.                             s.setUrl(serviceUrl);  
  192.                             if (type.equals("CHILD_ADDED")) {  
  193.                                 list.add(s);  
  194.                                 LOGGER.info("新增節點:\"{}\",服務地址:\"{}\"", data, serviceUrl);  
  195.                             } else if (type.equals("CHILD_REMOVED")) {  
  196.                                 for (int i = 0; i < list.size(); i++) {  
  197.                                     Service service = list.get(i);  
  198.                                     if (service.equals(s)) {  
  199.                                         list.remove(i);  
  200.                                         LOGGER.info("刪除節點:\"{}\",服務地址:\"{}\"", data, serviceUrl);  
  201.                                     }  
  202.   
  203.                                 }  
  204.                             }  
  205.                         }  
  206.                     };  
  207.                     threadPool.submit(r);  
  208.                 }  
  209.             });  
  210.   
  211.         }  
  212.     }  
  213.   
  214.     public void setLastErrorTime(final String url) {  
  215.         Runnable r = new Runnable() {  
  216.             public void run() {  
  217.                 for (List<Service> list : providerMap.values()) {  
  218.                     Iterator<Service> it = list.iterator();  
  219.                     while (it.hasNext()) {  
  220.                         Service service = it.next();  
  221.                         if (service.getUrl().equals(url)) {  
  222.                             service.setLastErrorTime(System.currentTimeMillis());  
  223.                             LOGGER.error("節點:\"{}\",呼叫URL:\"{}\"異常,該節點停止服務5分鐘", service.getName(), service.getUrl());  
  224.                         }  
  225.                     }  
  226.                 }  
  227.             }  
  228.         };  
  229.         threadPool.submit(r);  
  230.     }  
  231.       
  232.     public static void main(String[] args) throws Exception {  
  233.         ServiceInvoker s = ServiceInvoker.getInstance();  
  234.         while (true) {  
  235.             String str = s.get("/service/vdfs/upload/dx");  
  236.             s.setLastErrorTime(str);  
  237.             Thread.sleep(5000);  
  238.         }  
  239.     }  
  240. }  


MAVEN配置:

  1.  <properties>  
  2.         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  
  3.     </properties>  
  4.   
  5.     <dependencies>  
  6.         <dependency>  
  7.             <groupId>org.apache.curator</groupId>  
  8.             <artifactId>curator-framework</artifactId>  
  9.             <version>2.4.2</version>  
  10.             <exclusions>  
  11.                 <exclusion>  
  12.                     <groupId>log4j</groupId>  
  13.                     <artifactId>log4j</artifactId>  
  14.                 </exclusion>  
  15.                 <exclusion>  
  16.                     <groupId>org.slf4j</groupId>  
  17.                     <artifactId>slf4j-log4j12</artifactId>  
  18.   
  19.                 </exclusion>  
  20.             </exclusions>  
  21.         </dependency>  
  22.         <dependency>  
  23.             <groupId>org.apache.curator</groupId>  
  24.             <artifactId>curator-recipes</artifactId>  
  25.             <version>2.4.2</version>  
  26.         </dependency>  
  27.         <dependency>  
  28.             <groupId>junit</groupId>  
  29.             <artifactId>junit</artifactId>  
  30.             <version>3.8.1</version>  
  31.             <scope>test</scope>  
  32.         </dependency>  
  33.         <dependency>  
  34.             <groupId>commons-codec</groupId>  
  35.             <artifactId>commons-codec</artifactId>  
  36.             <version>20041127.091804</version>  
  37.         </dependency>  
  38.         <dependency>  
  39.             <groupId>ch.qos.logback</groupId>  
  40.             <artifactId>logback-core</artifactId>  
  41.             <version>1.1.7</version>  
  42.         </dependency>  
  43.         <dependency>  
  44.             <groupId>ch.qos.logback</groupId>  
  45.             <artifactId>logback-classic</artifactId>  
  46.             <version>1.1.7</version>  
  47.         </dependency>  
  48.         <dependency>  
  49.             <groupId>org.slf4j</groupId>  
  50.             <artifactId>slf4j-api</artifactId>  
  51.             <version>1.7.7</version>  
  52.         </dependency>  
  53.         <dependency>  
  54.             <groupId>org.slf4j</groupId>  
  55.             <artifactId>log4j-over-slf4j</artifactId>  
  56.             <version>1.7.7</version>  
  57.         </dependency>  
  58.         <dependency>  
  59.             <groupId>org.slf4j</groupId>  
  60.             <artifactId>jcl-over-slf4j</artifactId>  
  61.             <version>1.7.7</version>  
  62.             <scope>runtime</scope>  
  63.         </dependency>  
  64.     </dependencies>  
  65.   
  66.     <build>  
  67.         <resources>  
  68.             <resource>  
  69.                 <directory>src/main/java</directory>  
  70.                 <includes>  
  71.                     <include>**/*.properties</include>  
  72.                 </includes>  
  73.             </resource>  
  74.             <resource>  
  75.                 <directory>src/main/resources</directory>  
  76.             </resource>  
  77.         </resources>  
  78.     </build>  

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29254281/viewspace-2108223/,如需轉載,請註明出處,否則將追究法律責任。

相關文章