zoukankan      html  css  js  c++  java
  • 模拟实现配置中心配置发生变化时秒级推送至客户端代码思路

      1 import com.alibaba.fastjson.JSON;
      2 import com.xuebusi.spring.study.http.BasicHttpUtil;
      3 import org.springframework.beans.factory.InitializingBean;
      4 import org.springframework.web.bind.annotation.GetMapping;
      5 import org.springframework.web.bind.annotation.PathVariable;
      6 import org.springframework.web.bind.annotation.RequestMapping;
      7 import org.springframework.web.bind.annotation.RestController;
      8 import org.springframework.web.context.request.async.DeferredResult;
      9 
     10 import java.util.*;
     11 import java.util.concurrent.ConcurrentHashMap;
     12 import java.util.concurrent.ExecutorService;
     13 import java.util.concurrent.Executors;
     14 
     15 /**
     16  * 模拟实现配置中心配置发生变化时秒级推送至客户端
     17  * 操作步骤:
     18  * 1.请求http://host:port/conf/add?key=&value=接口添加数据
     19  * 2.请求http://host:port/conf/get?key=接口查看数据
     20  * 3.请求http://host:port/conf/update?key=&value=接口修改数据
     21  * 4.观察控制台日志会看到配置变化的通知
     22  * <p>
     23  * 原理:
     24  * 1.项目启动时,模拟一个客户端循环调用(超时重试) /conf/monitor 接口请求数据,配置中心接受到请求之后创建DeferredResult对象,
     25  * 以客户端ID为key,以DeferredResult对象为value放入 deferredResultMap;
     26  * 2.当客户端调用修改接口时,就将修改后的数据放入 changedDataMap(这里为了测试方便仅用一个map存);
     27  * 3.通过另一个线程定时(间隔1s)查看 changedDataMap 中变化的数据(如果数据存在数据库A表,则每隔1s去扫一下A表);
     28  * 4.如果数据有变化就遍历deferredResultMap通过 deferredResult.setResult() 通知给所有的客户端,通知完的客户端以及数据要移除;
     29  *
     30  * @author syj
     31  */
     32 @RestController
     33 @RequestMapping
     34 public class ConfController implements InitializingBean {
     35 
     36     private static final int TIME_OUT = 30;
     37 
     38     private static ExecutorService executorService = Executors.newCachedThreadPool();
     39 
     40     private static Map<String, ConfData> changedDataMap = new ConcurrentHashMap<>();
     41 
     42     private static Map<String, List<ConfDataLog>> logDataMap = new ConcurrentHashMap<>();
     43 
     44     private Map<String, DeferredResult> deferredResultMap = new ConcurrentHashMap();
     45 
     46     // 模拟数据库存储配置
     47     public static Map<String, ConfData> confDataMap = new ConcurrentHashMap<>();
     48 
     49     /**
     50      * 获取最新配置
     51      * 使用 DeferredResult 返回异步结果
     52      *
     53      * @param clientId
     54      * @return
     55      */
     56     @GetMapping("/conf/monitor")
     57     public DeferredResult<String> monitor(String clientId) {
     58         DeferredResult<String> result = new DeferredResult<>(TIME_OUT * 1000L, "超时啦");
     59         deferredResultMap.put(clientId, result);
     60         return result;
     61     }
     62 
     63     /**
     64      * 查询所有配置
     65      *
     66      * @return
     67      */
     68     @GetMapping("/conf/list")
     69     public Result<Map<String, ConfData>> list() {
     70         return new Result<>(confDataMap);
     71     }
     72 
     73     /**
     74      * 查询配置
     75      *
     76      * @param key
     77      * @return
     78      */
     79     @GetMapping("/conf/get")
     80     public Result<ConfData> get(String key) {
     81         if (!confDataMap.containsKey(key)) {
     82             return new Result<>(Result.FAIL_CODE, "key不存在");
     83         } else {
     84             return new Result<>(confDataMap.get(key));
     85         }
     86     }
     87 
     88     /**
     89      * 修改配置
     90      *
     91      * @param key
     92      * @param value
     93      * @return
     94      */
     95     @GetMapping("/conf/update")
     96     public Result<String> update(String key, String value) {
     97         if (!confDataMap.containsKey(key)) {
     98             return new Result<>(Result.FAIL_CODE, "key不存在");
     99         } else {
    100             ConfData confData = confDataMap.get(key);
    101             if (!confData.getValue().equals(value)) {
    102                 confData.setValue(value);
    103                 confDataMap.put(key, confData);
    104                 writeLog(key, value, 2);
    105                 // 同时放到 changedDataMap 中一份最新数据
    106                 changedDataMap.put(key, confData);
    107             }
    108             return Result.SUCCESS;
    109         }
    110     }
    111 
    112     /**
    113      * 新增配置
    114      *
    115      * @param key
    116      * @param value
    117      * @return
    118      */
    119     @GetMapping("/conf/add")
    120     public Result<String> add(String key, String value) {
    121         if (confDataMap.containsKey(key)) {
    122             return new Result<>(Result.FAIL_CODE, "key已经存在了");
    123         }
    124         confDataMap.put(key, new ConfData(value));
    125         writeLog(key, value, 1);
    126         changedDataMap.put(key, new ConfData(value));
    127         return Result.SUCCESS;
    128     }
    129 
    130     /**
    131      * 移除配置
    132      *
    133      * @param key
    134      * @return
    135      */
    136     @GetMapping("/conf/delete")
    137     public Result<String> delete(String key) {
    138         if (!confDataMap.containsKey(key)) {
    139             return new Result<>(Result.FAIL_CODE, "key不存在");
    140         }
    141         update(key, "");
    142         return Result.SUCCESS;
    143     }
    144 
    145     /**
    146      * 查询所有配置修改日志
    147      *
    148      * @return
    149      */
    150     @GetMapping("/conf/log")
    151     public Result<Collection<List<ConfDataLog>>> history() {
    152         return new Result<>(logDataMap.values());
    153     }
    154 
    155     /**
    156      * 查询指定配置修改日志
    157      *
    158      * @param key
    159      * @return
    160      */
    161     @GetMapping("/conf/log/{key}")
    162     public Result<List<ConfDataLog>> logByKey(@PathVariable("key") String key) {
    163         if (logDataMap.containsKey(key)) {
    164             return new Result<>(logDataMap.get(key));
    165         } else {
    166             return new Result<>(null);
    167         }
    168     }
    169 
    170     @Override
    171     public void afterPropertiesSet() throws Exception {
    172 
    173         // 模拟客户端 http请求最新配置
    174         executorService.execute(() -> {
    175             String clientId = UUID.randomUUID().toString().replace("-", "");
    176             while (true) {
    177                 String url = "http://localhost:8888/conf/monitor?clientId=" + clientId;
    178                 String result = BasicHttpUtil.get(url, TIME_OUT + 30);
    179                 System.out.println(Thread.currentThread().getName() + "----http调用结果:" + result);
    180             }
    181         });
    182 
    183         // 配置中心启动线程监控配置变化
    184         executorService.execute(() -> {
    185             while (true) {
    186                 Collection<ConfData> values = changedDataMap.values();
    187                 if (values != null && values.size() > 0) {
    188                     for (String key : changedDataMap.keySet()) {
    189                         for (String clientId : deferredResultMap.keySet()) {
    190                             DeferredResult deferredResult = deferredResultMap.get(clientId);
    191                             String msg = "通知客户端" + clientId + "配置" + key + "发生变化:" + JSON.toJSONString(changedDataMap.get(key));
    192                             deferredResult.setResult(msg);
    193                             // 移除已经通知过的客户端
    194                             deferredResultMap.remove(clientId);
    195                         }
    196                         // 移除已经通知过的数据
    197                         changedDataMap.remove(key);
    198                     }
    199                 }
    200                 try {
    201                     Thread.sleep(1000L);
    202                 } catch (InterruptedException e) {
    203                     e.printStackTrace();
    204                 }
    205             }
    206         });
    207     }
    208 
    209     /**
    210      * 记录修改日志
    211      *
    212      * @param key
    213      * @param value
    214      * @param optType 操作类型 1添加 2修改
    215      */
    216     private void writeLog(String key, String value, int optType) {
    217         if (logDataMap.containsKey(key)) {
    218             logDataMap.get(key).add(new ConfDataLog(key, value, optType, new Date()));
    219         } else {
    220             List<ConfDataLog> list = new ArrayList<>();
    221             list.add(new ConfDataLog(key, value, optType, new Date()));
    222             logDataMap.put(key, list);
    223         }
    224     }
    225 
    226     /**
    227      * 返回结果
    228      *
    229      * @param <T>
    230      */
    231     public static class Result<T> {
    232 
    233         public static int SUCCESS_CODE = 200;
    234         public static int FAIL_CODE = 500;
    235         public static final Result<String> SUCCESS = new Result<>(SUCCESS_CODE, "操作成功");
    236         public static final Result<String> FAIL = new Result<>(FAIL_CODE, "操作失败");
    237 
    238         private int code;
    239         private String msg;
    240         private T data;
    241 
    242         public Result(T data) {
    243             this.code = SUCCESS_CODE;
    244             this.msg = "操作成功";
    245             this.data = data;
    246         }
    247 
    248         public Result(int code, String msg) {
    249             this.code = code;
    250             this.msg = msg;
    251         }
    252 
    253         public Result(int code, String msg, T data) {
    254             this.code = code;
    255             this.msg = msg;
    256             this.data = data;
    257         }
    258 
    259         public int getCode() {
    260             return code;
    261         }
    262 
    263         public void setCode(int code) {
    264             this.code = code;
    265         }
    266 
    267         public String getMsg() {
    268             return msg;
    269         }
    270 
    271         public void setMsg(String msg) {
    272             this.msg = msg;
    273         }
    274 
    275         public T getData() {
    276             return data;
    277         }
    278 
    279         public void setData(T data) {
    280             this.data = data;
    281         }
    282     }
    283 
    284     /**
    285      * 配置
    286      */
    287     public class ConfData {
    288 
    289         private String value;
    290 
    291         public ConfData(String value) {
    292             this.value = value;
    293         }
    294 
    295         public String getValue() {
    296             return value;
    297         }
    298 
    299         public void setValue(String value) {
    300             this.value = value;
    301         }
    302     }
    303 
    304     /**
    305      * 配置修改日志
    306      */
    307     public class ConfDataLog {
    308 
    309         private String key;
    310         private String value;
    311         private int optType;// 1添加,2修改
    312         private Date updateTime;
    313 
    314         public ConfDataLog() {
    315         }
    316 
    317         public ConfDataLog(String key, String value, int optType, Date updateTime) {
    318             this.key = key;
    319             this.value = value;
    320             this.optType = optType;
    321             this.updateTime = updateTime;
    322         }
    323 
    324         public String getKey() {
    325             return key;
    326         }
    327 
    328         public void setKey(String key) {
    329             this.key = key;
    330         }
    331 
    332         public String getValue() {
    333             return value;
    334         }
    335 
    336         public void setValue(String value) {
    337             this.value = value;
    338         }
    339 
    340         public int getOptType() {
    341             return optType;
    342         }
    343 
    344         public void setOptType(int optType) {
    345             this.optType = optType;
    346         }
    347 
    348         public Date getUpdateTime() {
    349             return updateTime;
    350         }
    351 
    352         public void setUpdateTime(Date updateTime) {
    353             this.updateTime = updateTime;
    354         }
    355     }
    356 }
  • 相关阅读:
    数据库之主表、从表、主键、外键
    eclipse编写js代码没有提示
    思维导图xmind的使用方法
    整理一下Apache与Tomcat的关系
    全栈开发者,一个很好的自学编程网站
    svn文件被锁不能提交的解决办法
    在SQL Server数据库中执行存储过程很快,在c#中调用很慢的问题
    php安装redis扩展
    PHP点击按钮拷贝
    PHP文件下载
  • 原文地址:https://www.cnblogs.com/jun1019/p/10807055.html
Copyright © 2011-2022 走看看