最近的工作中心是容易扩容,刚刚把其它一个业务使用 sharding jdbc 把容量规划完成。因为系统采用的数据方案是:单写老库 -> 双写老库分片库 -> 单写分片库,使用 apollo 配置中心来切换数据写入规则。当有异常时可以进行方案回滚这种平滑的数据过渡方案,在上线过程中不可避免的会有中心状态,双写老库和分片库。
在系统处于中心状态的过程中,老库数据和分片库数据的一致性尤为重要。所以需要对老库和分片库的数据进行对比。数据对比方案经历过以下几个阶段:
系统方案一步一步进行优化,最终通过定时任务加钉钉群报警方式来检测在系统处于中间状态时,老库和新库的数据是否一致。
群机器人是钉钉为用户提供的智能群助手,帮助群里沟通协同更加高效。在【群设置】 - 【智能群助手】可以找到。目前群里支持最多添加6个机器人。
可以使用钉钉官方机器人小钉,也可以添加Github等机器人,还可以添加企业机器人或者自定义机器人。机器人是高效安全的,您请放心使用。
群成员可以在【手机钉钉】-【群聊】-【群设置】-【智能群助手】-【添加更多【选择机器人添加】。
步骤一,打开机器人管理页面。以PC端为例,打开PC端钉钉,点击头像,选择“机器人管理”。
步骤二,在机器人管理页面选择“自定义”机器人,输入机器人名字并选择要发送消息的群,同时可以为机器人设置机器人头像。
步骤三,完成必要的安全设置(至少选择一种),勾选 我已阅读并同意《自定义机器人服务及免责条款》,点击“完成”
步骤四,完成安全设置后,复制出机器人的Webhook地址,可用于向这个群发送消息,格式如下:
https://oapi.dingtalk.com/robot/send?access_token=XXXXXX
更多信息请查看 钉钉开发文档。
步骤五,在 Linux 服务器上进行测试。
curl 'https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxx' \
-H 'Content-Type: application/json' \
-d '{"msgtype": "text","text": {"content": "compare 测试数据,可忽略"}}'
数据对比服务开发,主要的工作就是开发一个 Elastic Job Simple 类型的定时任务,并配置完成。包含任务注册中心 zookeeper 以及 job 定义的配置。定时任务的逻辑主要是以下几个步骤:
发送钉钉信息的类:
发送钉钉消息请求类。
DingMessageRequest
@Getter
@Setter
public class DingMessageRequest {
private String msgtype;
private MessageText text;
}
具体消息包装类
MessageText
@Getter
@Setter
public class MessageText {
private String content;
}
http 发送模板配置类,使用的是 okhttp。
@Configuration
public class RestTemplateConfig {
@Bean("restTemplate")
public RestTemplate restTemplate(){
return new RestTemplate(requestFactory());
}
private OkHttp3ClientHttpRequestFactory requestFactory(){
return new OkHttp3ClientHttpRequestFactory(okHttpClient());
}
private OkHttpClient okHttpClient() {
return new OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory(), x509TrustManager())
.retryOnConnectionFailure(false)
.connectionPool(connectionPoll())
.connectTimeout(10L, TimeUnit.SECONDS)
.readTimeout(10L, TimeUnit.SECONDS)
.build();
}
private X509TrustManager x509TrustManager() {
return new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) {
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
};
}
private SSLSocketFactory sslSocketFactory() {
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{x509TrustManager()}, new SecureRandom());
return sslContext.getSocketFactory();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
return null;
}
private ConnectionPool connectionPoll() {
return new ConnectionPool(200, 5, TimeUnit.MINUTES);
}
}
当发送消息到 webhook 地址时,如果响应的 errcode 为 0 表示消息发送成功
DingMessageResponse
@Getter
@Setter
public class DingMessageResponse {
private int errcode;
private String errmsg;
}
这个类主要是发送消息到群 webhook,并且处理响应结果
@Slf4j
@Service("dingDingMessageProcessor")
public class DingDingMessageProcessor implements MessageProcessor {
@Resource
private RestTemplate restTemplate;
@Value("${sharding.dingding.group}")
private String shardingDingDingGroup;
@Override
public void process(String fileType, String message) {
log.info("DingDingServiceImpl#sendMessage message is {}", message);
DingMessageRequest request = new DingMessageRequest();
request.setMsgtype("text");
MessageText text = new MessageText();
text.setContent(message);
request.setText(text);
DingMessageResponse response = restTemplate.postForObject(shardingDingDingGroup, request, DingMessageResponse.class);
if(response == null) {
log.error("DingDingServiceImpl#sendMessage send message response is null");
}
if(response.getErrcode() == 0){
log.info("DingDingServiceImpl#sendMessage send success");
} else {
log.error("DingDingServiceImpl#sendMessage send fail {}", response.getErrmsg());
}
}
}
钉钉对比结果显示:
参考文章: