SpringBoot定时任务实现数据同步
业务的需求是,通过中台调用api接口获得,设备数据,要求现实设备数据的同步。
方案一:通过轮询接口的方式执行 pullData() 方法实现数据同步
该方式的原理是先清空之前的所有数据,然后重新插入通过api调用获取的最新数据。该方法的优点,逻辑简单。缺点是,频繁删除、插入数据。再调用查询数据时候,某一时刻,数据全部删除,还没及时插入的时候。数据可能有异常。
方案二:通过轮询接口的方式执行 pullDataNew() 方法实现数据同步
该方式的原理是先查询数据库,已有数据,然后和通过api调用获取的最新数据进行比对,找出数据中增量、减量和变量,进行同步更新。
该方法的优点,减少对数据库的频繁操作,提升性能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
|
package com.hxtx.spacedata.task; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.google.api.client.util.Lists; import com.hxtx.spacedata.common.domain.ResponseDTO; import com.hxtx.spacedata.config.SpringContextUtil; import com.hxtx.spacedata.controller.file.FilesMinioController; import com.hxtx.spacedata.domain.entity.entityconfig.EntityPointEntity; import com.hxtx.spacedata.service.entityconfig.EntityPointService; import com.hxtx.spacedata.util.HttpProxyUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; /** * 中台设备数据 定时任务执行 * * @author Tarzan Liu * @version 1.0.0 * @description * @date 2020/12/07 */ @Component @Slf4j public class EntityPointTask { @Autowired private EntityPointService entityPointService; @Value ( "${middleGround.server.host}" ) private String host; @Value ( "${middleGround.server.port}" ) private String port; private static FilesMinioController filesMinioController = SpringContextUtil.getBean(FilesMinioController. class ); /** * 设备定义点数据拉取 * * @author tarzan Liu * @date 2020/12/2 */ @Scheduled (cron = "0/30 * * * * ?" ) // 30秒校验一次 public void pullDataTaskByCorn() { String result = HttpProxyUtil.sendGet( "http://" + host + ":" + port + "/interface/system/list" ); JSONObject jsonObject = JSON.parseObject(result); if (Objects.nonNull(jsonObject)) { JSONArray array = jsonObject.getJSONArray( "data" ); if (array != null && array.size() != 0 ) { for ( int i = 0 ; i < array.size(); i++) { JSONObject obj = array.getJSONObject(i); String systemId = obj.getString( "id" ); pullDataNew(systemId); } } } } @Transactional (rollbackFor = Throwable. class ) public ResponseDTO<String> pullData(String code) { List<EntityPointEntity> list = Lists.newArrayList(); String result = HttpProxyUtil.sendGet( "http://" + host + ":" + port + "/interface/defintionView/listBySystemId/" + code); JSONObject jsonObject = JSON.parseObject(result); if (Objects.nonNull(jsonObject)) { JSONArray array = jsonObject.getJSONArray( "data" ); if (array != null && array.size() != 0 ) { for ( int i = 0 ; i < array.size(); i++) { JSONObject obj = array.getJSONObject(i); String pointId = obj.getString( "pointId" ); String name = obj.getString( "name" ); list.add(EntityPointEntity.builder().pointId(pointId).name(name).code(code).build()); } List<EntityPointEntity> existList = entityPointService.list( new LambdaQueryWrapper<EntityPointEntity>().eq(EntityPointEntity::getCode, code).isNotNull(EntityPointEntity::getValue)); if (CollectionUtils.isNotEmpty(existList)) { Map<String, String> existMap = existList.stream().collect(Collectors.toMap(EntityPointEntity::getPointId, EntityPointEntity::getValue)); list.forEach(e -> { String value = existMap.get(e.getPointId()); if (value != null ) { e.setValue(value); } }); } entityPointService.remove( new LambdaQueryWrapper<EntityPointEntity>().eq(EntityPointEntity::getCode, code)); entityPointService.saveBatch(list); } } return ResponseDTO.succ(); } @Transactional (rollbackFor = Throwable. class ) public ResponseDTO<String> pullDataNew(String code) { String result = HttpProxyUtil.sendGet( "http://" + host + ":" + port + "/interface/defintionView/listBySystemId/" + code); JSONObject jsonObject = JSON.parseObject(result); if (Objects.nonNull(jsonObject)) { JSONArray data = jsonObject.getJSONArray( "data" ); List<EntityPointEntity> list = data.toJavaList(EntityPointEntity. class ); if (CollectionUtils.isNotEmpty(list)) { list.forEach(e -> e.setCode(code)); List<EntityPointEntity> existList = entityPointService.list( new LambdaQueryWrapper<EntityPointEntity>().eq(EntityPointEntity::getCode, code)); if (CollectionUtils.isNotEmpty(existList)) { //存在map Map<String, String> existMap = existList.stream().collect(Collectors.toMap(EntityPointEntity::getPointId, EntityPointEntity::getName)); //传输map Map<String, String> dataMap = list.stream().collect(Collectors.toMap(EntityPointEntity::getPointId, EntityPointEntity::getName)); //增量 List<EntityPointEntity> increment = list.stream().filter(e -> existMap.get(e.getPointId()) == null ).collect(Collectors.toList()); if (CollectionUtils.isNotEmpty(increment)) { entityPointService.saveBatch(increment); } //减量 List<EntityPointEntity> decrement = existList.stream().filter(e -> dataMap.get(e.getPointId()) == null ).collect(Collectors.toList()); if (CollectionUtils.isNotEmpty(decrement)) { entityPointService.removeByIds(decrement.stream().map(EntityPointEntity::getId).collect(Collectors.toList())); } //变量 List<EntityPointEntity> variable = existList.stream().filter(e -> dataMap.get(e.getPointId()) != null && !dataMap.get(e.getPointId()).equals(e.getName())).collect(Collectors.toList()); if (CollectionUtils.isNotEmpty(variable)) { variable.forEach(e -> { e.setName(dataMap.get(e.getPointId())); }); entityPointService.updateBatchById(variable); } } else { entityPointService.saveBatch(list); } } } return ResponseDTO.succ(); } } |
数据库对应实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; import java.util.Date; @Builder @NoArgsConstructor @AllArgsConstructor @Data @TableName (value = "t_entity_point" ) public class EntityPointEntity implements Serializable { private static final long serialVersionUID = 2181036545424452651L; /** * 定义点id */ @TableId (value = "id" , type = IdType.ASSIGN_ID) private Long id; /** * 定义点id */ private String pointId; /** * 名称 */ private String name; /** * 绘制数据 */ private String value; /** * 编码 */ private String code; /** * 创建时间 */ private Date createTime; } |
HTTP请求代理工具类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
|
import lombok.extern.slf4j.Slf4j; import org.apache.http.Consts; import org.apache.http.HttpEntity; import org.apache.http.HttpStatus; import org.apache.http.NameValuePair; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicNameValuePair; import org.apache.http.ssl.SSLContextBuilder; import org.apache.http.util.EntityUtils; import javax.net.ssl.SSLContext; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.URL; import java.net.URLConnection; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * HTTP请求代理类 * * @author tarzan Liu * @description 发送Get Post请求 */ @Slf4j public class HttpProxyUtil { /** * 使用URLConnection进行GET请求 * * @param api_url * @return */ public static String sendGet(String api_url) { return sendGet(api_url, "" , "utf-8" ); } /** * 使用URLConnection进行GET请求 * * @param api_url * @param param * @return */ public static String sendGet(String api_url, String param) { return sendGet(api_url, param, "utf-8" ); } /** * 使用URLConnection进行GET请求 * * @param api_url 请求路径 * @param param 请求格式有name1=value1&name2=value2、json、xml、map或其他形式,具体要看接收方的取值, 可以为空 * @param charset 字符集 * @return */ public static String sendGet(String api_url, String param, String charset) { StringBuffer buffer = new StringBuffer(); try { // 判断有无参数,若是拼接好的url,就不必再拼接了 if (param != null && ! "" .equals(param)) { api_url = api_url + "?" + param; } log.info( "请求的路径是:" + api_url); URL realUrl = new URL(api_url); // 打开联接 URLConnection conn = realUrl.openConnection(); // 设置通用的请求属性 conn.setRequestProperty( "accept" , "*/*" ); conn.setRequestProperty( "connection" , "Keep-Alive" ); conn.setRequestProperty( "user-agent" , "Mozilla/4.0(compatible; MSIE 6.0; Windows NT 5.1; SV1)" ); conn.setConnectTimeout( 12000 ); //设置连接主机超时(单位:毫秒) conn.setReadTimeout( 12000 ); // 设置从主机读取数据超时(单位:毫秒) conn.connect(); // 建立实际的联接 // 定义 BufferedReader输入流来读取URL的相应 try (BufferedReader in = new BufferedReader( new InputStreamReader(conn.getInputStream(), charset))) { String line; while ((line = in.readLine()) != null ) { // buffer.append("\n"+line); buffer.append(line); } } } catch (Exception e) { log.error( "发送GET请求出现异常! " + e.getMessage()); return null ; } // log.info("响应返回数据:" + buffer.toString()); return buffer.toString(); } /** * 使用URLConnection进行POST请求 * * @param api_url 请求路径 * @param param 请求格式有name1=value1&name2=value2、json、xml、map或其他形式,具体要看接收方的取值,最好不为空 * @return */ public static String sendPost(String api_url, String param) { return sendPost(api_url, param, "utf-8" ); } /** * 使用URLConnection进行POST请求 * * @param api_url 请求路径 * @param param 请求格式有name1=value1&name2=value2、json、xml、map或其他形式,具体要看接收方的取值,最好不为空 * @param charset 字符集 * @return */ public static String sendPost(String api_url, String param, String charset) { StringBuffer buffer = new StringBuffer(); try { log.info( "请求的路径是:" + api_url + ",参数是:" + param); URL realUrl = new URL(api_url); // 打开联接 URLConnection conn = realUrl.openConnection(); // 设置通用的请求属性 conn.setRequestProperty( "accept" , "*/*" ); conn.setRequestProperty( "connection" , "Keep-Alive" ); conn.setRequestProperty( "user-agent" , "Mozilla/4.0(compatible; MSIE 6.0; Windows NT 5.1; SV1)" ); conn.setConnectTimeout( 12000 ); //设置连接主机超时(单位:毫秒) conn.setReadTimeout( 12000 ); // 设置从主机读取数据超时(单位:毫秒) // 发送POST请求必须设置如下两行 conn.setDoOutput( true ); conn.setDoInput( true ); // 获取URLConnection对象对应的输出流 try (PrintWriter out = new PrintWriter(conn.getOutputStream())) { out.print(param); // 发送请求参数 out.flush(); // flush输出流的缓冲 } // 定义 BufferedReader输入流来读取URL的相应,得指明使用UTF-8编码,否则到API<a href="http://www.zhuxianfei.com/server/" target="_blank" class="infotextkey">服务器</a>XML的中文不能被成功识别 try (BufferedReader in = new BufferedReader( new InputStreamReader(conn.getInputStream(), charset))) { String line; while ((line = in.readLine()) != null ) { // buffer.append("\n"+line); buffer.append(line); } } } catch (Exception e) { log.error( "发送POST请求出现异常! " + e.getMessage()); e.printStackTrace(); } log.info( "响应返回数据:" + buffer.toString()); return buffer.toString(); } public static CloseableHttpClient createSSLClientDefault() throws Exception { SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial( null , new AllTrustStrategy()).build(); SSLConnectionSocketFactory sslSf = new SSLConnectionSocketFactory(sslContext); return HttpClients.custom().setSSLSocketFactory(sslSf).build(); } // 加载证书 private static class AllTrustStrategy implements TrustStrategy { public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { return true ; } } /** * 支持https请求 * * @param url * @param param * @return * @throws Exception */ public static String sendHttpClientPost(String url, Map<String, String> param) throws Exception { CloseableHttpClient httpClient = createSSLClientDefault(); HttpPost httpPost = null ; CloseableHttpResponse response = null ; String result = "" ; try { // 发起HTTP的POST请求 httpPost = new HttpPost(url); List<NameValuePair> paramList = new ArrayList<NameValuePair>(); for (String key : param.keySet()) { paramList.add( new BasicNameValuePair(key, param.get(key))); } log.info( "http请求地址:" + url + ",参数:" + paramList.toString()); // UTF8+URL编码 httpPost.setEntity( new UrlEncodedFormEntity(paramList, Consts.UTF_8)); httpPost.setConfig(RequestConfig.custom().setConnectTimeout( 30000 ).setSocketTimeout( 30000 ).build()); response = httpClient.execute(httpPost); HttpEntity entity = response.getEntity(); int statusCode = response.getStatusLine().getStatusCode(); if (HttpStatus.SC_OK == statusCode) { // 如果响应码是200 } result = EntityUtils.toString(entity); log.info( "状态码:" + statusCode + ",响应信息:" + result); } finally { if (response != null ) { response.close(); } if (httpPost != null ) { httpPost.releaseConnection(); } httpClient.close(); } return result; } } |
希望对大家的学习有所帮助,也希望大家多多支持
SpringBoot定时任务实现数据同步的更多相关文章
- Oracle Job定时任务详解、跨数据库数据同步
业务需求,需要与A公司做数据对接,我们公司用的Oracle,A公司用的SQL Server数据库,如何跨数据库建立连接呢?这里使用的是DBLink,不会配置的请看我的另外一篇博客:https://ww ...
- canal整合springboot实现mysql数据实时同步到redis
业务场景: 项目里需要频繁的查询mysql导致mysql的压力太大,此时考虑从内存型数据库redis里查询,但是管理平台里会较为频繁的修改增加mysql里的数据 问题来了: 如何才能保证mysql的数 ...
- springboot定时任务之旅
springboot定时任务 假设场景:单体应用的定时任务,假设我们已经有了一个搭建好的springboot应用,但是需要添加一个定时执行的部分(比如笔者遇到的是定时去请求一个接口数据来更新某个表), ...
- SQL Server数据同步的研究(单向/双向)
思路: 1.做中间件(简单:定时采集:复杂:分布式,订阅中心的形式,如微信的中间件:https://github.com/tencent-wechat/phxsql) 2.采用触发器的形式,有数据触发 ...
- rsync+inotify实现服务器数据同步
一.什么是rsync rsync,remote synchronize是一款实现远程同步功能的软件,它在同步文件的同时,可以保持原来文件的权限.时间.软硬链接等附加信息.rsync是用 “rsync算 ...
- Elasticsearch和mysql数据同步(logstash)
1.版本介绍 Elasticsearch: https://www.elastic.co/products/elasticsearch 版本:2.4.0 Logstash: https://www ...
- rsync服务架设(数据同步|文件增量备份)
近期由于业务需要,需要将两台服务器数据保持同步.方案有很多,rsync是其中一种解决方案,本文对rsync的安装及配置进行简单说明,其他实现方式有兴趣可以研究.以下是本文提纲,供参考: rsy ...
- oracle数据同步方案
数据同步方案:--用DBLINK 创建与所需同步表的链接------------------------------------------------------------------------ ...
- Linux实战教学笔记21:Rsync数据同步工具
第二十一节 Rsync数据同步工具 标签(空格分隔): Linux实战教学笔记-陈思齐 ---本教学笔记是本人学习和工作生涯中的摘记整理而成,此为初稿(尚有诸多不完善之处),为原创作品,允许转载,转载 ...
- rsync (windows 服务端,linux客户端)将windows上的数据同步到linux服务器,反之也可
一:总体概述. 1.windows上面首先装CW_rsync_Server.4.1.0_installer,安装时要输入的用户名密码要记住哦!接下来就是找到rsyncd.conf进入配置细节 2.li ...
随机推荐
- MyBatis xml文件头
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC & ...
- [oeasy]python0041_ 转义字符_转义序列_escape_序列_sequence
转义序列 回忆上次内容 上次回顾了5bit-Baudot博多码的来历 从 莫尔斯码 到 博多码 原来 人 来 收发电报 现在 机器 来 收发电报 输入方式 从 电键 改成 键盘 输出方式 从 纸带 变 ...
- 学习笔记--Java中this关键字
Java中this关键字 关于Java语言中的this关键字 this 是一个关键字,翻译为:这个 this 是一个引用,一个变量,this变量中保存的内存地址指向自身 每一个对象都有自己的this, ...
- Android SDK Build-tools的版本已经高于Android SDK Platform-tools版本 的解决办法
解决Unknown error: Unable to build: the file dx.jar was not loaded from the SDK folder!最近渐渐迁移到Android ...
- Django 用户认证系统使用总结
Django用户认证系统使用总结 by:授客 QQ:1033553122 测试环境 Win7 Django 1.11 使用Django认证系统 本文按默认配置讲解Django认证系统的用法.如果默 ...
- Jmeter函数助手27-urlencode
urlencode函数用于将字符串进行application/x-www-form-urlencoded编码格式化. String to encode in URL encoded chars:填入字 ...
- 【Vue2】 Watch 监听器
监听器案例 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF ...
- 强化学习中的“sample efficiency”应该如何翻译 —— “样本效率”还是“采样效率”
问题: 强化学习中的"sample efficiency"应该如何翻译 -- "样本效率"还是"采样效率" 答案: 具体看上下文内容.如果是 ...
- VcXsrv: 一个好用的Windows X11 Server
windows10没有系统自带的X11服务器,使用了几款X11的windows下X11服务器软件后发现了一个好用的软件--VcXsrv. 下载地址: https://sourceforge.net/p ...
- 除了Ubuntu以外的Linux系统可以安装Tensorflow/Pytorch的GPU版本吗???
废话: 平时没事有用到的Linux系统有Centos/Redhat/Ubuntu/UOS/Deepin,其中Ubuntu系统主要是用来工作生产的,UOS就是看看国家队的进展如何,Deepin就是看看民 ...