本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。
前言
由于前面使用前端页面来进行打印后,效果并不理想,在加载 522 张图片的时候需要 10 秒左右的预览时间。因此,将这打印步骤放入后端完成。再选购打印机后,但该打印机只能连接内网打印,而我们的服务是部署在公网中的,如何让内网的数据消费到公网的数据呢?这时候就需要一个中间件,它帮我们做到这两块内容的消息传送。
本次中间件选用的是 Kafka,它具备超一流的读写性能。在面对大数据量消息传输的时候具备很好的高吞吐率。而我们后续的应用会使用到 kafka ,所以这边我们首先来使用 Kafka 来完成这一功能。而为什么不采用 redis。区别如下:
- 1、存储方式不同:redis 是存储是内存的,而 Kafka 是存储在硬盘的,redis 的数据可能会丢失,而 Kafka 相对会安全很多,同时,硬盘比内存的成本会小很多。
- 2、订阅机制不同:Kafka 是专业的消息队列,除了主题之外,不仅可以消费已经消费的数据,还可以进行分区消费和分消费者组。
一、设计概述
1.1 打印时序图
就是两个服务之间的服务调用关系。
1.2 运维子系统打印业务
打印功能相对比二维码的生成比较简单,只需找到所有的设备,然后将这些设备发送给中间件即可。
分为三种打印情况:
- 1、单独打印
- 2、按设备类型批量打印
- 3、按设备类型组装成目录批量打印
先从数据库中获取动态分区,然后发送给指定的 主题和分区即可。
kafkaTemplate.send(new ProducerRecord(PRODUCE_TOPIC,devicePrint.getPartition(),null,mapper.writeValueAsString(messageVO)));
然后打印服务只需要对打印失败的 retry 次数 +1 即,接下来就是打印业务的处理了。
二、开发流程
后端
@KafkaListener(id = "consumer2", topicPartitions = { @org.springframework.kafka.annotation.TopicPartition(topic = "print_consumer_consumerPrintInfo", partitions = { "0" }) },groupId="subStation")
public void consumer2(String str) {
try {
DeviceKafkaMessageVO messageVO = mapper.readValue(str, DeviceKafkaMessageVO.class);
// 消费失败次数三次以内继续消费
if(0==messageVO.getRetry()){
socketApi.sendMsg(SocketDTO
.builder()
.password(configComponent.getPassword())
.topic(ElectricityStationConst.DEVICE_BATCH_PRINT + messageVO.getSysId())
.data(messageVO)
.build()
);
DeviceInfo deviceInfo = new DeviceInfo();
deviceInfo.setId(messageVO.getId()).setIsPrint(0);
redisSdk.lLeftAdd(configComponent.getPrintListSuccess(),new ObjectMapper().writeValueAsString(deviceInfo));
}
else if(messageVO.getRetry()==3){
socketApi.sendMsg(SocketDTO
.builder()
.password(configComponent.getPassword())
.topic(ElectricityStationConst.DEVICE_BATCH_PRINT + messageVO.getSysId())
.data(messageVO)
.build()
);
DeviceInfo deviceInfo = new DeviceInfo();
deviceInfo.setId(messageVO.getId()).setIsPrint(1);
redisSdk.lLeftAdd(configComponent.getPrintListError(),new ObjectMapper().writeValueAsString(deviceInfo));
System.out.println("值"+redisSdk.lLen(configComponent.getPrintListError()));
}
else{
kafkaTemplate.send(new ProducerRecord(DeviceServiceImpl.PRODUCE_TOPIC,0,null,mapper.writeValueAsString(messageVO)));
}
if(redisSdk.lLen(configComponent.getPrintListError())+redisSdk.lLen(configComponent.getPrintListSuccess())>=messageVO.getTotal()){
int sus = (int) redisSdk.lLen(configComponent.getPrintListSuccess());
int errs = (int) redisSdk.lLen(configComponent.getPrintListError());
// 插入消息通知
if(errs>0){
MessageAccept messageAccept = new MessageAccept();
messageAccept.setContent("部分设备打印失败!总执行数:"+(sus+errs)+",失败数:"+(errs));
messageAccept.setTitle(SmConst.SM_MESAGE_TYPE_CONST_200.getName());
messageAccept.setSysId(41040020001L);
messageAccept.setType(SmConst.SM_MESAGE_TYPE_CONST_200.getStatus());
messageAccept.setCreateTime(new DateTime());
messageAccept.setUpdateTime(new DateTime());
messageAccept.setDeleteFlag(0);
messageAcceptMapper.insert(messageAccept);
}
// 批量更新状态
while(redisSdk.lLen(configComponent.getPrintListSuccess())>0){
ObjectMapper objectMapper = new ObjectMapper();
ArrayList<DeviceInfo> successList = new ArrayList<>();
List<String> list = redisSdk.pLRightPop(configComponent.getPrintListSuccess(),5000);
for (String string : list) {
if(StrUtil.isNotEmpty(string)) {
DeviceInfo deviceInfo = objectMapper.readValue(string, DeviceInfo.class);
successList.add(deviceInfo);
}
}
List<Long> collect = successList.stream().map(e -> e.getId()).collect(Collectors.toList());
deviceInfoMapper.updateBatchIsPrintSuccess(collect);
}
while(redisSdk.lLen(configComponent.getPrintListError())>0){
ObjectMapper objectMapper = new ObjectMapper();
ArrayList<DeviceInfo> failList = new ArrayList<>();
List<String> list = redisSdk.pLRightPop(configComponent.getPrintListError(),5000);
for (String string : list) {
if(StrUtil.isNotEmpty(string)) {
DeviceInfo deviceInfo = objectMapper.readValue(string, DeviceInfo.class);
failList.add(deviceInfo);
}
}
List<Long> collect = failList.stream().map(e -> e.getId()).collect(Collectors.toList());
deviceInfoMapper.updateBatchIsPrintFail(collect);
}
}
} catch (Exception e) {
log.error("消费失败");
e.printStackTrace();
}
}
前端:
//批量打印
printBatch(row){
this.$data.printshow=true;
this.$data.printDialog=true;
StoreService.setPrintNumber();
let _this=this;
console.log(row)
this.$api.req("/am/device/img/menu/printBatch", {id:row.id},res =>{
this.$data.loading = false;
},res=>{
this.$data.printDialog=false;
this.$data.printshow=false;
_this.$data.loading = false;
_this.$message.error(res.msg);
})
},
printstomp(){
var _this=this
// 监听批量打印
_this.$bus.$on(TopicConst.DEVICE_BATCH_PRINT+StoreService.getStationId(),data =>{
var a = JSON.parse(data)
// 将计数器 +1
StoreService.incryPrintNumber()
if(a.retry == 3){
StoreService.incryPrintErrNumber()
}
_this.$data.printshow=true
_this.$data.printDialog=true
_this.$data.printvalue=parseInt(Math.round(StoreService.getPrintNumber()/a.total*100))
if(a.total == StoreService.getPrintNumber()){
if(StoreService.getPrintErrNumber()>0){
this.$message.error("打印总数:"+StoreService.getPrintNumber()+",失败个数:"+StoreService.getPrintErrNumber())
Utils.$emit('dot',true)
}else {
this.$message.success("打印完成!")
}
setTimeout(function(){
_this.$data.printshow=false
_this.$data.printDialog=false
_this.$data.printvalue=0
}, 1000);
StoreService.setPrintNumber(0)
}
})
},
最后,上成品。