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@Slf4jpublic 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请求 */@Slf4jpublic 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 ...
随机推荐
- Vue 怎么用 vm.$set() 解决对象新增属性不能响应的问题 ?
受现代 JavaScript 的限制 ,Vue 无法检测到对象属性的添加或删除.由于 Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在才能让 ...
- 分享一个国内可用的ChatGPT网站,免费无限制,支持AI绘画
背景 ChatGPT作为一种基于人工智能技术的自然语言处理工具,近期的热度直接沸腾. 作为一个AI爱好者,翻遍了各大基于ChatGPT的网站,终于找到一个免费!免登陆!手机电脑通用!国内可直接对话的C ...
- 【JavaScript高级04】作用域和作用域链
1,作用域 作用域表示的是变量的有效区域,JavaScript中作用域分为全局作用域和函数作用域(在es6之前没有块作用域).其确定时间为编写成功之后就已经确定好了. 作用域的作用是用来隔离变量,不同 ...
- IDEA整合Spring+Struts+Hibernate项目
新手使用idea整合Spring+Struts+Hibernate项目 项目所需jar下载: https://download.csdn.net/download/weixin_44906002/12 ...
- Jmeter函数助手20-eval
eval函数用于执行变量名.嵌套函数,允许在变量中的字符串中插入变量和函数引用 包含变量和函数引用的文本:填入变量名称或者函数或者字符,可以只填一种也可以组合都填入 1.eval函数填入的是变量名时则 ...
- 【郝斌C ST】02
自学视频<郝斌C语言自学教程> 10: https://www.bilibili.com/video/BV1os411h77o C语言大纲 - 1.简介 - 2.基本编译知识 - 3.数据 ...
- 目前国内全地形能力最强的双足机器人 —— 逐际动力 —— 提出迭代式预训练(Iterative Pre-training)方法的强化学习算法
相关: https://weibo.com/1255595687/O5k4Aj8l2 该公司对其产品的强化学习训练算法给出了较少的描述: 提出迭代式预训练(Iterative Pre-training ...
- WSL启动nvidia-docker镜像:报错libnvidia-ml.so.1- file exists- unknown
参考: https://blog.csdn.net/xujiamin0022016/article/details/124782913 https://zhuanlan.zhihu.com/p/361 ...
- Docker部署rabbitmq遇到的问题
1.背景 Docker部署rabbitmq遇到的如下两个问题 问题一:访问交换机时报错 Management API returned status code 500 问题二:访问channel时报错 ...
- [COCI2013-2014#6] KRUŽNICE 题解
前言 题目链接:洛谷. 题目分析 显然,手模样例发现答案分为以下几个贡献: 所有圆外面的那个大平面,贡献为 \(1\). 每个圆至少被分成一部分,贡献为 \(n\). 如果有一个圆被"拦腰截 ...