001Diamond学习003源码分析

1 服务端

1.1 数据初始化

在初始化PersistService后,会对数据源进行初始化:

 1 @PostConstruct
 2 public void initDataSource() throws Exception {
 3     // 读取jdbc.properties配置, 加载数据源
 4     Properties props = ResourceUtils.getResourceAsProperties("jdbc.properties");
 5     BasicDataSource ds = new BasicDataSource();
 6     ds.setDriverClassName(JDBC_DRIVER_NAME);
 7     ds.setUrl(ensurePropValueNotNull(props.getProperty("db.url")));
 8     ds.setUsername(ensurePropValueNotNull(props.getProperty("db.user")));
 9     ds.setPassword(ensurePropValueNotNull(props.getProperty("db.password")));
10     ds.setInitialSize(Integer.parseInt(ensurePropValueNotNull(props.getProperty("db.initialSize"))));
11     ds.setMaxActive(Integer.parseInt(ensurePropValueNotNull(props.getProperty("db.maxActive"))));
12     ds.setMaxIdle(Integer.parseInt(ensurePropValueNotNull(props.getProperty("db.maxIdle"))));
13     ds.setMaxWait(Long.parseLong(ensurePropValueNotNull(props.getProperty("db.maxWait"))));
14     ds.setPoolPreparedStatements(Boolean.parseBoolean(ensurePropValueNotNull(props
15         .getProperty("db.poolPreparedStatements"))));
16 
17     this.jt = new JdbcTemplate();
18     this.jt.setDataSource(ds);
19     // 设置最大记录数,防止内存膨胀
20     this.jt.setMaxRows(MAX_ROWS);
21     // 设置JDBC执行超时时间
22     this.jt.setQueryTimeout(QUERY_TIMEOUT);
23 }

在TimerTaskService中,会初始化一个定时任务,用于每隔600秒从数据库查询最新数据,查询数据后,首先更新缓存,然后写入磁盘:

 1 @PostConstruct
 2 public void init() {
 3     this.scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
 4 
 5         public Thread newThread(Runnable r) {
 6             Thread t = new Thread(r);
 7             t.setName(THREAD_NAME);
 8             t.setDaemon(true);
 9             return t;
10         }
11 
12     });
13 
14     DumpConfigInfoTask dumpTask = new DumpConfigInfoTask(this);
15     dumpTask.run();
16     this.scheduledExecutorService.scheduleWithFixedDelay(dumpTask, SystemConfig.getDumpConfigInterval(),
17         SystemConfig.getDumpConfigInterval(), TimeUnit.SECONDS);
18 }

在NotifyService中,会读取配置文件加载服务端节点:

 1 @PostConstruct
 2 public void loadNodes() {
 3     InputStream in = null;
 4     try {
 5         in = ResourceUtils.getResourceAsStream("node.properties");
 6         nodeProperties.load(in);
 7     }
 8     catch (IOException e) {
 9         log.error("加载节点配置文件失败");
10     }
11     finally {
12         try {
13             if (in != null)
14                 in.close();
15         }
16         catch (IOException e) {
17             log.error("关闭node.properties失败", e);
18         }
19     }
20     log.info("节点列表:" + nodeProperties);
21 }

1.2 更新配置

登录过程省略,提交数据进入AdminController类的postConfig方法。

先对参数进行检验处理,在进入具体的处理操作。

源码如下:

 1 @RequestMapping(params = "method=postConfig", method = RequestMethod.POST)
 2 public String postConfig(HttpServletRequest request, HttpServletResponse response,
 3         @RequestParam("dataId") String dataId, @RequestParam("group") String group,
 4         @RequestParam("content") String content, ModelMap modelMap) {
 5     response.setCharacterEncoding("GBK");
 6 
 7     boolean checkSuccess = true;
 8     String errorMessage = "参数错误";
 9     if (StringUtils.isBlank(dataId) || DiamondUtils.hasInvalidChar(dataId.trim())) {
10         checkSuccess = false;
11         errorMessage = "无效的DataId";
12     }
13     if (StringUtils.isBlank(group) || DiamondUtils.hasInvalidChar(group.trim())) {
14         checkSuccess = false;
15         errorMessage = "无效的分组";
16     }
17     if (StringUtils.isBlank(content)) {
18         checkSuccess = false;
19         errorMessage = "无效的内容";
20     }
21     if (!checkSuccess) {
22         modelMap.addAttribute("message", errorMessage);
23         return "/admin/config/new";
24     }
25 
26     dataId = dataId.trim();
27     group = group.trim();
28 
29     this.configService.addConfigInfo(dataId, group, content);
30 
31     modelMap.addAttribute("message", "提交成功!");
32     return listConfig(request, response, dataId, group, 1, 20, modelMap);
33 }

进入ConfigService类的addConfigInfo方法。

先将数据保存到数据库,然后更新缓存,再保存到本地磁盘。

最后还需要通知其他服务节点同步更改。

源码如下:

 1 public void addConfigInfo(String dataId, String group, String content) {
 2     checkParameter(dataId, group, content);
 3     ConfigInfo configInfo = new ConfigInfo(dataId, group, content);
 4     // 保存顺序:先数据库,再磁盘
 5     try {
 6         persistService.addConfigInfo(configInfo);
 7         // 切记更新缓存
 8         this.contentMD5Cache.put(generateMD5CacheKey(dataId, group), configInfo.getMd5());
 9         diskService.saveToDisk(configInfo);
10         // 通知其他节点
11         this.notifyOtherNodes(dataId, group);
12     }
13     catch (Exception e) {
14         log.error("保存ConfigInfo失败", e);
15         throw new ConfigServiceException(e);
16     }
17 }

1.3 节点通知

在ConfigService类中调用Service类的notifyConfigInfoChange方法。

在更新配置后,还需要通知其他节点重新从数据库上同步配置。

源码如下:

1 private void notifyOtherNodes(String dataId, String group) {
2     this.notifyService.notifyConfigInfoChange(dataId, group);
3 }

进入NotifyService类的notifyConfigInfoChange方法。

通过URL调用通知节点的接口。

源码如下:

 1 public void notifyConfigInfoChange(String dataId, String group) {
 2     Enumeration<?> enu = nodeProperties.propertyNames();
 3     while (enu.hasMoreElements()) {
 4         String address = (String) enu.nextElement();
 5         if (address.contains(SystemConfig.LOCAL_IP)) {
 6             continue;
 7         }
 8         String urlString = generateNotifyConfigInfoPath(dataId, group, address);
 9         final String result = invokeURL(urlString);
10         log.info("通知节点" + address + "分组信息改变:" + result);
11     }
12 }

进入NotifyController类的notifyConfigInfo方法。

执行同步逻辑。

源码如下:

1 @RequestMapping(method = RequestMethod.GET, params = "method=notifyConfigInfo")
2 public String notifyConfigInfo(@RequestParam("dataId") String dataId, @RequestParam("group") String group) {
3     dataId = dataId.trim();
4     group = group.trim();
5     this.configService.loadConfigInfoToDisk(dataId, group);
6     return "200";
7 }

进入ConfigService类的loadConfigInfoDisk方法。

先更新缓存,然后保存到本地磁盘。

源码如下:

 1 public void loadConfigInfoToDisk(String dataId, String group) {
 2     try {
 3         ConfigInfo configInfo = this.persistService.findConfigInfo(dataId, group);
 4         if (configInfo != null) {
 5             this.contentMD5Cache.put(generateMD5CacheKey(dataId, group), configInfo.getMd5());
 6             this.diskService.saveToDisk(configInfo);
 7         }
 8         else {
 9             // 删除文件
10             this.contentMD5Cache.remove(generateMD5CacheKey(dataId, group));
11             this.diskService.removeConfigInfo(dataId, group);
12         }
13     }
14     catch (Exception e) {
15         log.error("保存ConfigInfo到磁盘失败", e);
16         throw new ConfigServiceException(e);
17     }
18 }

2 客户端

2.1 DiamondManager

创建DiamondManager,通过DefaultDiamondManager构造器创建。

通过DiamondManager的getAvailableConfigureInfomation方法获取配置内容。

源码如下:

 1 @Test
 2 void testClient() {
 3     DiamondManager diamondManager = new DefaultDiamondManager("cnpark", "cnpark-station", new ManagerListener() {
 4         public void receiveConfigInfo(String configInfo) {
 5             System.out.println("receiveConfigInfo" + configInfo);
 6         }
 7         public Executor getExecutor() {
 8             return null;
 9         }
10     });
11     diamondManager.getDiamondConfigure().setPort(8080);
12     String availableConfigureInfomation = diamondManager.getAvailableConfigureInfomation(5000);
13     System.out.println("content" + availableConfigureInfomation);
14 }

构造DefaultDiamondManager对象。

创建并启动DiamondSubscriber。

源码如下:

 1 public DefaultDiamondManager(String group, String dataId, ManagerListener managerListener) {
 2     this.dataId = dataId;
 3     this.group = group;
 4 
 5     diamondSubscriber = DiamondClientFactory.getSingletonDiamondSubscriber();
 6 
 7     this.managerListeners.add(managerListener);
 8     ((DefaultSubscriberListener) diamondSubscriber.getSubscriberListener()).addManagerListeners(this.dataId,
 9         this.group, this.managerListeners);
10     diamondSubscriber.addDataId(this.dataId, this.group);
11     diamondSubscriber.start();
12 }

2.2 ManagerListener

ManagerListener是一个接口,需要实现并重写其方法。

receiveConfigInfo方法用于接收配置信息,当配置信息发生改动时,可以最新配置信息。

源码如下:

1 public interface ManagerListener {
2 
3     public Executor getExecutor();
4 
5     public void receiveConfigInfo(final String configInfo);
6 }

2.3 DiamondSubscriber

通过DiamondClientFactory创建DiamondSubscriber,实际上创建的是DefaultDiamondSubscriber。

DiamondSubscriber用于订阅持久的文本配置信息。

源码如下:

1 private static DiamondSubscriber diamondSubscriber = new DefaultDiamondSubscriber(new DefaultSubscriberListener());
2 
3 public static DiamondSubscriber getSingletonDiamondSubscriber() {
4     return diamondSubscriber;
5 }

创建DefaultDiamondSubscriber。

传入SubscriberListener,创建DiamondConfigure。

源码如下:

1 public DefaultDiamondSubscriber(SubscriberListener subscriberListener) {
2     this.subscriberListener = subscriberListener;
3     this.diamondConfigure = new DiamondConfigure();
4 }

2.4 SubscriberListener

创建SubscriberListener,实际上创建的是DefaultSubscriberListener。

源码如下:

1 public Executor getExecutor();
2 
3 public void receiveConfigInfo(final ConfigureInfomation configureInfomation);

DefaultSubscriberListener中维护了一个ManagerListener的Map集合,Key是dataId和group的组合,Value是ManagerListener的List集合。

重写了receiveConfigInfo方法,配置改动时会遍历ManagerListener并执行其receiveConfigInfo方法。

源码如下:

 1 public void receiveConfigInfo(final ConfigureInfomation configureInfomation) {
 2     String dataId = configureInfomation.getDataId();
 3     String group = configureInfomation.getGroup();
 4     if (null == dataId) {
 5         dataLog.error("[receiveConfigInfo] dataId is null");
 6         return;
 7     }
 8 
 9     String key = makeKey(dataId, group);
10     CopyOnWriteArrayList<ManagerListener> listeners = allListeners.get(key);
11     if (listeners == null || listeners.isEmpty()) {
12         dataLog.warn("[notify-listener] no listener for dataId=" + dataId + ", group=" + group);
13         return;
14     }
15 
16     for (ManagerListener listener : listeners) {
17         try {
18             notifyListener(configureInfomation, listener);
19         }
20         catch (Throwable t) {
21             dataLog.error("call listener error, dataId=" + dataId + ", group=" + group, t);
22         }
23     }
24 }
25 
26 private void notifyListener(final ConfigureInfomation configureInfomation, final ManagerListener listener) {
27     if (listener == null) {
28         return;
29     }
30 
31     final String dataId = configureInfomation.getDataId();
32     final String group = configureInfomation.getGroup();
33     final String content = configureInfomation.getConfigureInfomation();
34 
35     dataLog.info("[notify-listener] call listener " + listener.getClass().getName() + ", for " + dataId + ", "
36             + group + ", " + content);
37 
38     Runnable job = new Runnable() {
39         public void run() {
40             try {
41                 listener.receiveConfigInfo(content);
42             }
43             catch (Throwable t) {
44                 dataLog.error(t.getMessage(), t);
45             }
46         }
47     };
48 
49     if (null != listener.getExecutor()) {
50         listener.getExecutor().execute(job);
51     }
52     else {
53         job.run();
54     }
55 }

2.5 DiamondConfigure

创建DiamondConfigure。

DiamondConfigure封装了客户端的一些配置信息,并且会在磁盘上创建一个配置目录。

源码如下:

1 public DiamondConfigure() {
2     filePath = System.getProperty("user.home") + "/diamond";
3     File dir = new File(filePath);
4     dir.mkdirs();
5 
6     if (!dir.exists()) {
7         throw new RuntimeException("创建diamond目录失败:" + filePath);
8     }
9 }

2.6 启动DiamondSubscriber

在方法中依次启动LocalConfigInfoProcessor和ServerAddressProcessor,最后轮询获取配置。

源码如下:

 1 public synchronized void start() {
 2     if (isRun) {
 3         return;
 4     }
 5 
 6     if (null == scheduledExecutor || scheduledExecutor.isTerminated()) {
 7         scheduledExecutor = Executors.newSingleThreadScheduledExecutor();
 8     }
 9 
10     localConfigInfoProcessor.start(this.diamondConfigure.getFilePath() + "/" + DATA_DIR);
11     serverAddressProcessor = new ServerAddressProcessor(this.diamondConfigure, this.scheduledExecutor);
12     serverAddressProcessor.start();
13 
14     this.snapshotConfigInfoProcessor =
15             new SnapshotConfigInfoProcessor(this.diamondConfigure.getFilePath() + "/" + SNAPSHOT_DIR);
16     // 设置domainNamePos值
17     randomDomainNamePos();
18     initHttpClient();
19 
20     // 初始化完毕
21     isRun = true;
22 
23     if (log.isInfoEnabled()) {
24         log.info("当前使用的域名有:" + this.diamondConfigure.getDomainNameList());
25     }
26 
27     if (MockServer.isTestMode()) {
28         bFirstCheck = false;
29     }
30     else {
31         // 设置轮询间隔时间
32         this.diamondConfigure.setPollingIntervalTime(Constants.POLLING_INTERVAL_TIME);
33     }
34     // 轮询
35     rotateCheckConfigInfo();
36 
37     addShutdownHook();
38 }

2.7 启动LocalConfigInfoProcessor

通过LocalConfigInfoProcessor的start方法创建数据目录。

源码如下:

 1 public synchronized void start(String rootPath) {
 2     if (this.isRun) {
 3         return;
 4     }
 5     this.rootPath = rootPath;
 6     this.isRun = true;
 7     if (this.singleExecutor == null || singleExecutor.isTerminated()) {
 8         singleExecutor = Executors.newSingleThreadScheduledExecutor();
 9     }
10     initDataDir(rootPath);
11     startCheckLocalDir(rootPath);
12 }

2.8 启动ServerAddressProcessor

如果是本地优先,通过本地文件获取ServerAddress,默认非本地优先。

如果非本地优先,首次同步获取ServerAddress,然后定时异步获取ServerAddress,默认执行此步骤。

源码如下:

 1 public synchronized void start() {
 2     if (isRun) {
 3         return;
 4     }
 5     isRun = true;
 6     initHttpClient();
 7     if (this.diamondConfigure.isLocalFirst()) {
 8         acquireServerAddressFromLocal();
 9     }
10     else {
11         synAcquireServerAddress();
12         asynAcquireServerAddress();
13     }
14 }

2.8.1 同步获取ServerAddress

调用acquireServerAddressOnce方法,先从线上服务器获取,再从日常服务器获取,最后从本地文件获取。

如果从服务器获取成功,则更新本地文件。

源码如下:

 1 protected void synAcquireServerAddress() {
 2     if (!isRun) {
 3         throw new RuntimeException("ServerAddressProcessor不在运行状态,无法同步获取服务器地址列表");
 4     }
 5     if (MockServer.isTestMode()) {
 6         diamondConfigure.addDomainName("测试模式,没有使用的真实服务器");
 7         return;
 8     }
 9     int acquireCount = 0;
10     if (diamondConfigure.getDomainNameList().size() == 0) {
11         if (!acquireServerAddressOnce(acquireCount)) {
12             acquireCount++;
13             if (acquireServerAddressOnce(acquireCount)) {
14                 // 存入本地文件
15                 storeServerAddressesToLocal();
16                 log.info("在同步获取服务器列表时,向日常ConfigServer服务器获取到了服务器列表");
17             }
18             else {
19                 log.info("从本地获取Diamond地址列表");
20                 reloadServerAddresses();
21                 if (diamondConfigure.getDomainNameList().size() == 0)
22                     throw new RuntimeException("当前没有可用的服务器列表");
23             }
24         }
25         else {
26             log.info("在同步获取服务器列表时,向线上ConfigServer服务器获取到了服务器列表");
27             // 存入本地文件
28             storeServerAddressesToLocal();
29         }
30     }
31 }

2.8.2 定时异步获取ServiceAddress

调用acquireServerAddressOnce方法,先从线上服务器获取,再从日常服务器获取。

如果从服务器获取成功,则更新本地文件。

每300秒更新一次。

源码如下:

 1 protected void asynAcquireServerAddress() {
 2     if (MockServer.isTestMode()) {
 3         return;
 4     }
 5     this.scheduledExecutor.schedule(new Runnable() {
 6         public void run() {
 7             if (!isRun) {
 8                 log.warn("ServerAddressProcessor不在运行状态,无法异步获取服务器地址列表");
 9                 return;
10             }
11             int acquireCount = 0;
12             if (!acquireServerAddressOnce(acquireCount)) {
13                 acquireCount++;
14                 if (acquireServerAddressOnce(acquireCount)) {
15                     // 存入本地文件
16                     storeServerAddressesToLocal();
17                 }
18             }
19             else {
20                 // 存入本地文件
21                 storeServerAddressesToLocal();
22             }
23             asynAcquireServerAddress();
24         }
25     }, asynAcquireIntervalInSec, TimeUnit.SECONDS);
26 }

2.8.3 从服务器获取ServiceAddress

获取服务器地址和端口,以及文件名称,获取文件中保存的ServiceAddress。

源码如下:

 1 private boolean acquireServerAddressOnce(int acquireCount) {
 2     HostConfiguration hostConfiguration = configHttpClient.getHostConfiguration();
 3     String configServerAddress;
 4     int port;
 5     if (null != diamondConfigure.getConfigServerAddress()) {
 6         configServerAddress = diamondConfigure.getConfigServerAddress();
 7         port = diamondConfigure.getConfigServerPort();
 8     }
 9     else {
10         if (acquireCount == 0) {
11             configServerAddress = Constants.DEFAULT_DOMAINNAME;
12             port = Constants.DEFAULT_PORT;
13         }
14         else {
15             configServerAddress = Constants.DAILY_DOMAINNAME;
16             port = Constants.DEFAULT_PORT;
17         }
18     }
19     hostConfiguration.setHost(configServerAddress, port);
20 
21     String serverAddressUrl = Constants.CONFIG_HTTP_URI_FILE;
22 
23     HttpMethod httpMethod = new GetMethod(serverAddressUrl);
24     // 设置HttpMethod的参数
25     HttpMethodParams params = new HttpMethodParams();
26     params.setSoTimeout(diamondConfigure.getOnceTimeout());
27     // ///////////////////////
28     httpMethod.setParams(params);
29 
30     try {
31         if (SC_OK == configHttpClient.executeMethod(httpMethod)) {
32             InputStreamReader reader = new InputStreamReader(httpMethod.getResponseBodyAsStream());
33             BufferedReader bufferedReader = new BufferedReader(reader);
34             String address = null;
35             List<String> newDomainNameList = new LinkedList<String>();
36             while ((address = bufferedReader.readLine()) != null) {
37                 address = address.trim();
38                 if (StringUtils.isNotBlank(address)) {
39                     newDomainNameList.add(address);
40                 }
41             }
42             if (newDomainNameList.size() > 0) {
43                 log.debug("更新使用的服务器列表");
44                 this.diamondConfigure.setDomainNameList(newDomainNameList);
45                 return true;
46             }
47         }
48         else {
49             log.warn("没有可用的新服务器列表");
50         }
51     }
52     catch (HttpException e) {
53         log.error(getErrorMessage(configServerAddress) + ", " + e);
54     }
55     catch (IOException e) {
56         log.error(getErrorMessage(configServerAddress) + ", " + e);
57     }
58     catch (Exception e) {
59         log.error(getErrorMessage(configServerAddress) + ", " + e);
60     }
61     finally {
62         httpMethod.releaseConnection();
63     }
64     return false;
65 }

2.8.4 保存到本地文件

如果是从服务器获取的ServiceAddress,还需要将读取的地址保存到ServiceAddress文件。

源码如下:

 1 void storeServerAddressesToLocal() {
 2     List<String> domainNameList = new ArrayList<String>(diamondConfigure.getDomainNameList());
 3     PrintWriter printWriter = null;
 4     BufferedWriter bufferedWriter = null;
 5     try {
 6         File serverAddressFile =
 7                 new File(generateLocalFilePath(this.diamondConfigure.getFilePath(), "ServerAddress"));
 8         if (!serverAddressFile.exists()) {
 9             serverAddressFile.createNewFile();
10         }
11         printWriter = new PrintWriter(serverAddressFile);
12         bufferedWriter = new BufferedWriter(printWriter);
13         for (String serveraddress : domainNameList) {
14             bufferedWriter.write(serveraddress);
15             bufferedWriter.newLine();
16         }
17         bufferedWriter.flush();
18     }
19     catch (Exception e) {
20         log.error("存储服务器地址到本地文件失败", e);
21     }
22     finally {
23         if (bufferedWriter != null) {
24             try {
25                 bufferedWriter.close();
26             }
27             catch (IOException e) {
28                 // ignore
29             }
30         }
31         if (printWriter != null) {
32             printWriter.close();
33         }
34     }
35 }

2.8.5 从本地文件读取ServerAddress

如果没有从服务器中获取,只能从文件中读取。

源码如下:

 1 void reloadServerAddresses() {
 2     FileInputStream fis = null;
 3     InputStreamReader reader = null;
 4     BufferedReader bufferedReader = null;
 5     try {
 6         File serverAddressFile =
 7                 new File(generateLocalFilePath(this.diamondConfigure.getFilePath(), "ServerAddress"));
 8         if (!serverAddressFile.exists()) {
 9             return;
10         }
11         fis = new FileInputStream(serverAddressFile);
12         reader = new InputStreamReader(fis);
13         bufferedReader = new BufferedReader(reader);
14         String address = null;
15         while ((address = bufferedReader.readLine()) != null) {
16             address = address.trim();
17             if (StringUtils.isNotBlank(address)) {
18                 diamondConfigure.getDomainNameList().add(address);
19             }
20         }
21         bufferedReader.close();
22         reader.close();
23         fis.close();
24     }
25     catch (Exception e) {
26         log.error("从本地文件取服务器地址失败", e);
27     }
28     finally {
29         if (bufferedReader != null) {
30             try {
31                 bufferedReader.close();
32             }
33             catch (Exception e) {
34             }
35         }
36         if (reader != null) {
37             try {
38                 reader.close();
39             }
40             catch (Exception e) {
41             }
42         }
43         if (fis != null) {
44             try {
45                 fis.close();
46             }
47             catch (Exception e) {
48             }
49         }
50     }
51 }

2.9 轮询获取配置

循环探测配置信息是否发生变化,通过三种途径:

1)检查本地配置文件,用缓存中的配置和本地配置文件中的配置比较,如果发生改变,则更新缓存,然后调用popConfigInfo方法。

2)检查服务器配置文件,用缓存中的配置和服务器配置文件中的配置比较,如果发生改变,则更新缓存,然后调用popConfigInfo方法。

3)检查本地磁盘快照,如果没有从本地配置文件中读取配置,也没有从服务器配置文件中读取配置,则获取本地磁盘快照中的配置,并更新缓存,然后调用popConfigInfo方法。

调用popConfigInfo方法后,会推送配置改动的订阅信息到监听器,然后调用saveSnapshot方法更新磁盘快照。

源码如下:

 1 private void rotateCheckConfigInfo() {
 2     scheduledExecutor.schedule(new Runnable() {
 3         public void run() {
 4             if (!isRun) {
 5                 log.warn("DiamondSubscriber不在运行状态中,退出查询循环");
 6                 return;
 7             }
 8             try {
 9                 checkLocalConfigInfo();
10                 checkDiamondServerConfigInfo();
11                 checkSnapshot();
12             }
13             catch (Exception e) {
14                 e.printStackTrace();
15                 log.error("循环探测发生异常", e);
16             }
17             finally {
18                 rotateCheckConfigInfo();
19             }
20         }
21     }, bFirstCheck ? 60 : diamondConfigure.getPollingIntervalTime(), TimeUnit.SECONDS);
22     bFirstCheck = false;
23 }

2.10 获取配置

通过DiamondManager的getAvailableConfigureInfomation方法获取服务器上的配置。

实际上调用的是DefaultDiamondManager的getAvailableConfigureInfomation方法。

源码如下:

1 public String getAvailableConfigureInfomation(long timeout) {
2     return diamondSubscriber.getAvailableConfigureInfomation(dataId, group, timeout);
3 }

继续调用DiamondSubscriber的getAvailableConfigureInfomation方法。

实际上调用的是DefaultDiamondSubscriber的getAvailableConfigureInfomation方法。

源码如下:

 1 public String getAvailableConfigureInfomation(String dataId, String group, long timeout) {
 2     // 尝试先从本地和网络获取配置信息
 3     try {
 4         String result = getConfigureInfomation(dataId, group, timeout);
 5         if (result != null && result.length() > 0) {
 6             return result;
 7         }
 8     }
 9     catch (Throwable t) {
10         log.error(t.getMessage(), t);
11     }
12 
13     // 测试模式不使用本地dump
14     if (MockServer.isTestMode()) {
15         return null;
16     }
17     return getSnapshotConfiginfomation(dataId, group);
18 }

2.10.1 从本地和服务器获取配置

调用getConfigureInfomation方法。

尝试从本地配置文件获取配置,获取成功则更新缓存,并更新本地磁盘快照。

尝试从服务器配置文件获取配置,获取成功则更新缓存,并更新本地磁盘快照。

源码如下:

 1 public String getConfigureInfomation(String dataId, String group, long timeout) {
 2     // 同步接口流控
 3     // flowControl();
 4     if (null == group) {
 5         group = Constants.DEFAULT_GROUP;
 6     }
 7     CacheData cacheData = getCacheData(dataId, group);
 8     // 优先使用本地配置
 9     try {
10         String localConfig = localConfigInfoProcessor.getLocalConfigureInfomation(cacheData, true);
11         if (localConfig != null) {
12             cacheData.incrementFetchCountAndGet();
13             saveSnapshot(dataId, group, localConfig);
14             return localConfig;
15         }
16     }
17     catch (IOException e) {
18         log.error("获取本地配置文件出错", e);
19     }
20     // 获取本地配置失败,从网络取
21     String result = getConfigureInfomation(dataId, group, timeout, false);
22     if (result != null) {
23         saveSnapshot(dataId, group, result);
24         cacheData.incrementFetchCountAndGet();
25     }
26     return result;
27 }

2.10.2 从本地磁盘快照获取配置

调用getSnapshotConfiginfomation方法。

源码如下:

 1 private String getSnapshotConfiginfomation(String dataId, String group) {
 2     if (group == null) {
 3         group = Constants.DEFAULT_GROUP;
 4     }
 5     try {
 6         CacheData cacheData = getCacheData(dataId, group);
 7         String config = this.snapshotConfigInfoProcessor.getConfigInfomation(dataId, group);
 8         if (config != null && cacheData != null) {
 9             cacheData.incrementFetchCountAndGet();
10         }
11         return config;
12     }
13     catch (Exception e) {
14         log.error("获取snapshot出错, dataId=" + dataId + ",group=" + group, e);
15         return null;
16     }
17 }

 

推荐这些文章:

String类的获取功能

 
 
 
 
 

...

使用官方升级助手:Windows 10 升级 Windows 11

 
 
 
 
 
 
 
 

 

 
 

 
 
 

自定义组件:

...

selenium log 原理分析

 参考博客园  https://www.cnblogs.com/nbkhic/p/9249330.html
 
https://www.cnblogs.com/snailrunning/p/9505009.html
 
自己查看log的方法  还没有找到更好的方法
1. 启动hub节点    java -jar selenium-server-4.0.0.jar hub --log C:\selenium-hub.log
    如果打印特别详细的信息修改--log-level  &...

有效解决mysql中ONLY_FULL_GROUP_BY办法---1055

一. (推荐)any_value
  mysql用来应对 ONLY_FULL_GROUP_BY 所出现的 any_value()
  1. mysql 5.7 以后 基本都会自带 ONLY_FULL_GROUP_BY ,很可能在部署以后,会出现 1055的错误
  2. ONLY_FULL_GROUP_BY 意思就是 select target list 中有明确的语义,在5.7之前不存在是允许模糊语义查询的。但是5.7以后,需要明确你所查询的所有数据,允许有聚合函数,不允许存在group by 后所不带字段
  3. 故而Mysql提供了 any_value(...

GROUP BY 怎么写 linq

问题
SELECT     Visitorid FROM         Visitors GROUP BY Visitorid
  这个怎么写 linq 
Visitors.GroupBy(v=>v.Visitorid)  这样 会出来多个合集.

最佳回答
这样的Group By的意思是什么呢?

...

文本聚合函数(wm_concat, listagg, group_concat, string_agg)

实现目标
  1.聚合文本
  2.聚合文本(去重)
  3.聚合文本(去重),按照指定字段排序
  4.聚合文本(去重),按照指定字段排序,替换默认逗号分隔符
 
MySQL: group_concat
Oracle: wm_concat(11g), listagg(12c)
SQL Server: for XML PATH
PostgreSQL: string_agg
 
数据

 
MySQL

 
Oracle

 
Sql Server

...

SAP WM初阶根据Group Number来查询与之有关的TO单

SAP WM初阶根据Group Number来查询与之有关的TO单
 
在SAP WM模块的2-Step picking流程里,我们会为需要做拣配的TR或者交货单创建Group,然后为Group来集中拣配物料,分配减配好的物料给到不同的参考单据(TR或者交货单)。这样在系统上就能为某个group number创建多个不同的TO单据。
 
如果要根据group number去查询与之关联的TO单据数据,可以采取不同的方式。
 
 
1, 使用事务代码LT23来做查询。
 
执行事务代码LT23后系统进入如下界面,

 

 ...

Java: i=i++字节码分析

public class B {

public static void main(String[] args) {
int bb = 5;
int pp = bb++;
System.out.println(pp);
}
}

 
 
 

...

SQL-group by|order by

数据库
1.查询排序:order by asc(升序)或desc(降序),默认asc(升序)
order by xxx asc -  按照升序将xxx排列
order by xxx desc -  按照降序将xxx排列
2. group BY-  将相同的数组合到一起
假如有相同的人存在这个表,我们需要求出这个人的钱,用group by 就可以
如:

 
 
 语句就可以这样写,
SELECT Customer,SUM(OrderPrice) FROM Orders  GROUP BY Custome...

1.关于sql中!=NULL和is not null

  今天在写一个简单的sql的时候,用到了!=null,结果返回空集合。后来被同是提醒需要使用 is not null。后来在网上参考这篇文章https://www.cnblogs.com/studynode/p/10068384.html,算理解了两种用法之间的区别。、
      总之记住一句话,有关null的判断就是 is null 和 is not null两种。
     
 

...

文章标题:001Diamond学习003源码分析
文章链接:https://www.dianjilingqu.com/51268.html
本文章来源于网络,版权归原作者所有,如果本站文章侵犯了您的权益,请联系我们删除,联系邮箱:saisai#email.cn,感谢支持理解。
THE END
< <上一篇
下一篇>>