华为云云耀云服务器L实例评测|基于canal缓存自动更新流程SpringBoot项目应用案例和源码
- 软件开发
- 2025-08-17 21:30:01

前言
最近华为云云耀云服务器L实例上新,也搞了一台来玩,期间遇到各种问题,在解决问题的过程中学到不少和运维相关的知识。
在之前的博客中,介绍过canal的安装和配置,参考博客
拉取创建canal镜像配置相关参数 & 搭建canal连接MySQL数据库 & spring项目应用canal初步本篇博客给出了canal项目应用的案例,详细介绍基于canal实现数据库和缓存同步的流程,并给出了核心diamante的源码。
其他相关的华为云云耀云服务器L实例评测文章列表如下:
初始化配置SSH连接 & 安装MySQL的docker镜像 & 安装redis以及主从搭建 & 7.2版本redis.conf配置文件
安装Java8环境 & 配置环境变量 & spring项目部署 &【!】存在问题未解决
部署spring项目端口开放问题的解决 & 服务器项目环境搭建MySQL,Redis,Minio…指南
由于自己原因导致MySQL数据库被攻击 & MySQL的binlog日志文件的理解
认识redis未授权访问漏洞 & 漏洞的部分复现 & 设置连接密码 & redis其他命令学习
拉取创建canal镜像配置相关参数 & 搭建canal连接MySQL数据库 & spring项目应用canal初步
Docker版的Minio安装 & Springboot项目中的使用 & 结合vue进行图片的存取
在Redis的Docker容器中安装BloomFilter & 在Spring中使用Redis插件版的布隆过滤器
Elasticsearch的Docker版本的安装和参数设置 & 端口开放和浏览器访问
Elasticsearch的可视化Kibana工具安装 & IK分词器的安装和使用
Elasticsearch的springboot整合 & Kibana进行全查询和模糊查询
文章目录 前言引出基于canal缓存同步更新整体的流程 相关代码和流程1.canal通道的配置2.前端查询的业务代码3.数据库数据更新4.缓存更新前端展示 核心代码源码1.配置yml和配置类2.canal自动更新代码3.查询的业务层service代码4.主启动类5.前端vue代码 总结 引出1.介绍基于canal实现数据库和缓存同步的流程; 2.给出了核心diamante的源码;
基于canal缓存同步更新 整体的流程启动spring项目时,同步启动缓存自动更新,如果数据库的相关表格数据发生变化,canal通过就会监听到,然后更新缓存redis中的相应数据
哪些数据从缓存中取?——不经常更新的数据:比如公司的部门,仓库等;
在项目启动时,启动了canal,canal用来监听数据库的变化;
业务逻辑:前端请求相关数据–> 问Redis要
(1)如果redis里面有,则返回给前端;
(2)如果redis里面没有,则从数据库查询,并且存到redis里面,返回给前端;
(3)如果数据库发生更新,canal监听到修改,同步更新到Redis里面,保证缓存和数据库一致;
相关代码和流程 1.canal通道的配置缓存自动更新,读取配置文件中的ip和端口号
是否开启缓存自动更新,从配置文件中读取配置;
2.前端查询的业务代码在数据库数据没有更新时,获取缓存中的数据
3.数据库数据更新如果数据库的数据更新,canal监听到,发现是缓存对应的表,并且是对应的字段发生变化,则进行缓存的自动更新
缓存自动同步更新
存到Redis里面的最新的数据
4.缓存更新前端展示缓存更新后,前端再次查询,获得最新的数据
核心代码源码 1.配置yml和配置类配置yml文件
server: port: 10050 ## 是否启用安全框架 true为开启,false为关闭 security: isOpen: true ## 是否开启canal管道,true为开启,false为关闭 canal: isOpen: true # canal的相关配置 canalConfig: host: 124.70.138.34 port: 11111Redis存Java对象的配置类
package com.tianju.fresh.config.redis; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration public class RedisSerializeConfig { @Bean public RedisTemplate redisTemplateInit(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); //设置序列化Key的实例化对象 redisTemplate.setKeySerializer(new StringRedisSerializer()); //设置序列化Value的实例化对象 redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); /** * * 设置Hash类型存储时,对象序列化报错解决 */ redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); return redisTemplate; } } 2.canal自动更新代码canal自动更新的代码,用canal管道监听MySQL数据变化,自动更新redis缓存
package com.tianju.fresh.config.redis; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import com.alibaba.otter.canal.client.CanalConnectors; import com.alibaba.otter.canal.client.CanalConnector; import com.alibaba.otter.canal mon.utils.AddressUtils; import com.alibaba.otter.canal.protocol.Message; import com.alibaba.otter.canal.protocol.CanalEntry.Column; import com.alibaba.otter.canal.protocol.CanalEntry.Entry; import com.alibaba.otter.canal.protocol.CanalEntry.EntryType; import com.alibaba.otter.canal.protocol.CanalEntry.EventType; import com.alibaba.otter.canal.protocol.CanalEntry.RowChange; import com.alibaba.otter.canal.protocol.CanalEntry.RowData; import com.baomidou.mybatisplus.annotation.TableField; import com.tianju.fresh.entity mon.GoodsTypeVo; import com.tianju.fresh.entity mon.WarehouseVo; import com.tianju.fresh.entity.goods.GoodsType; import com.tianju.fresh.mapper.goods.GoodsTypeMapper; import com.tianju.fresh.mapper.warehouse.StorehouseMapper; import com.tianju.fresh.service mon.CommonStaticMethod; import com.tianju.fresh.util.Constance; import com.tianju.fresh.util.RedisUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; /** * 用canal管道监听MySQL数据变化,自动更新redis缓存 */ @Slf4j @Component public class AutoUpdateRedis { @Value("${canalConfig.host}") private String host; @Value("${canalConfig.port}") private Integer port; @Autowired private RedisTemplate<String,Object> redisTemplate; @Autowired private GoodsTypeMapper typeMapper; @Autowired private StorehouseMapper storehouseMapper; @Autowired private RedisUtil redisUtil; public void run() { // 创建链接 final InetSocketAddress HOST = new InetSocketAddress(host,port); // final InetSocketAddress HOST = new InetSocketAddress("192.168.111.130",11111); CanalConnector connector = CanalConnectors.newSingleConnector(HOST, "example", "", ""); int batchSize = 1000; int emptyCount = 0; try { connector.connect(); connector.subscribe(".*\\..*"); connector.rollback(); int totalEmptyCount = 120; // while (emptyCount < totalEmptyCount) { while (true) { Message message = connector.getWithoutAck(batchSize); // 获取指定数量的数据 long batchId = message.getId(); int size = message.getEntries().size(); if (batchId == -1 || size == 0) { emptyCount++; if(emptyCount % 100==0 || emptyCount==1){ System.out.println("empty count : " + emptyCount); } try { Thread.sleep(1000); } catch (InterruptedException e) { } } else { emptyCount = 0; // System.out.printf("message[batchId=%s,size=%s] \n", batchId, size); printEntry(message.getEntries()); } connector.ack(batchId); // 提交确认 // connector.rollback(batchId); // 处理失败, 回滚数据 } // System.out.println("empty too many times, exit"); } finally { connector.disconnect(); } } private void printEntry(List<Entry> entrys) { for (Entry entry : entrys) { if (entry.getEntryType() == EntryType.TRANSACTIONBEGIN || entry.getEntryType() == EntryType.TRANSACTIONEND) { continue; } RowChange rowChage = null; try { rowChage = RowChange.parseFrom(entry.getStoreValue()); } catch (Exception e) { throw new RuntimeException("ERROR ## parser of eromanga-event has an error , data:" + entry.toString(), e); } EventType eventType = rowChage.getEventType(); System.out.println(String.format("================> binlog[%s:%s] , name[%s,%s] , eventType : %s", entry.getHeader().getLogfileName(), entry.getHeader().getLogfileOffset(), entry.getHeader().getSchemaName(), entry.getHeader().getTableName(), eventType)); String tableName = entry.getHeader().getTableName(); if (Constance.LISTEN_TAB_NAMES.contains(tableName)){ for (RowData rowData : rowChage.getRowDatasList()) { if (eventType == EventType.DELETE){ // 删除之前 log.debug("-------删除之前before"); changeBefore(rowData.getBeforeColumnsList()); log.debug("-------删除之后after"); // 传修改的表名,和常量中的map对比,从而对应的redis里的key // changeAfter(rowData.getAfterColumnsList(),tableName); }else if (eventType == EventType.INSERT){ // 插入之前 log.debug("-------插入之前before"); changeBefore(rowData.getBeforeColumnsList()); log.debug("-------插入之后after"); // 传修改的表名,和常量中的map对比,从而对应的redis里的key // changeAfter(rowData.getAfterColumnsList(),tableName); }else { // 修改之前 log.debug("-------修改之前before"); changeBefore(rowData.getBeforeColumnsList()); log.debug("-------修改之后after"); // 传修改的表名,和常量中的map对比,从而对应的redis里的key changeAfter(rowData.getAfterColumnsList(),tableName); } } } } } /** * 数据库更新之前 * @param columns */ private void changeBefore(List<Column> columns) { for (Column column : columns) { System.out.println(column.getName() + " : " + column.getValue() + " update=" + column.getUpdated()); } } private void changeAfter(List<Column> columns, String tableName) { for (Column column : columns) { System.out.println(column.getName() + " : " + column.getValue() + " update=" + column.getUpdated()); // 如果是商品类别表变化 if ("name".equals(column.getName()) && Constance.GOODS_TYPE_TAB_NAME.equals(tableName)){ // 默认是名称那一列变化才更新缓存 // 先删除key,再设置好新的key Map tabNameToRedisKey = Constance.getTabNameToRedisKey(); String redisKey = (String) tabNameToRedisKey.get(tableName); redisTemplate.delete(redisKey); // TODO:设置新的key List<GoodsTypeVo> list = CommonStaticMethod.getGoodsTypeVos(typeMapper); redisUtil.saveObjectToRedis(Constance.GOODS_TYPE_REDIS_KEY, list); break; } // 如果是仓库那张表变化 storehouse if ("name".equals(column.getName()) && Constance.STOREHOUSE_TAB_NAME.equals(tableName)){ // 默认是名称那一列变化才更新缓存 // 先删除key,再设置好新的key Map tabNameToRedisKey = Constance.getTabNameToRedisKey(); String redisKey = (String) tabNameToRedisKey.get(tableName); redisTemplate.delete(redisKey); // 设置新的key,存到redis里面 List<WarehouseVo> list = CommonStaticMethod.getStorehouseVos(storehouseMapper); redisUtil.saveObjectToRedis(Constance.STOREHOUSE_REDIS_KEY,list); break; } } } }redis的工具类
package com.tianju.fresh.util; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; @Component public class RedisUtil { @Autowired private RedisTemplate<String,Object> redisTemplate; public void saveObjectToRedis(String key,Object json){ redisTemplate.opsForValue().set(key,json); } /** * 从redis里面获取json对象,如果没有,返回null * @param key * @return */ public Object getJsonFromRedis(String key){ return redisTemplate.opsForValue().get(key); } }监听数据库表,列名的常量类
package com.tianju.fresh.util; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 专门放各种常量 */ public interface Constance { // 设置哪些数据库表需要监听,比如单位unit,仓库warehouse,商品类型等 List<String> LISTEN_TAB_NAMES = Arrays.asList("goods_type","unit_commodity","warehouse_center","warehouse_tab"); String GOODS_TYPE_TAB_NAME = "goods_type"; String GOODS_TYPE_REDIS_KEY = "goodsTypeVo"; String UNIT_TAB_NAME = "unit_commodity"; String UNIT_REDIS_KEY = "unitVo"; String WAREHOUSE_TAB_NAME = "warehouse_center"; String WAREHOUSE_REDIS_KEY = "warehouseCenterVo"; String STOREHOUSE_TAB_NAME = "warehouse_tab"; String STOREHOUSE_REDIS_KEY = "storehouseVo"; static Map getTabNameToRedisKey(){ Map<String,String> map = new HashMap<>(); map.put(GOODS_TYPE_TAB_NAME,"goodsTypeVo"); map.put("unit_commodity","unitVo"); map.put("warehouse_center","warehouseCenterVo"); map.put("warehouse_tab","storehouseVo"); return map; } } 3.查询的业务层service代码 @Override public List<WarehouseVo> findStorehouse() { // List<Storehouse> storehouses = storehouseMapper.selectList(null); // List<WarehouseVo> list = new ArrayList<>(storehouses.size()); // storehouses.forEach(s->{ // list.add(new WarehouseVo(s.getId()+"", s.getName())); // }); // 是否有小仓库的redis的key Boolean hasKey = redisTemplate.hasKey(Constance.STOREHOUSE_REDIS_KEY); if (hasKey){ // 如果有,走缓存 List<WarehouseVo> list = (List<WarehouseVo>) redisTemplate.opsForValue() .get(Constance.STOREHOUSE_REDIS_KEY); log.debug("get storehouseVo from redis: "+list); return list; } // 如果没有从数据库查询,存到redis里面 List<WarehouseVo> list = CommonStaticMethod.getStorehouseVos(storehouseMapper); log.debug("get storehouseVo from mysql: "+list); redisUtil.saveObjectToRedis(Constance.STOREHOUSE_REDIS_KEY, list); return list; } 4.主启动类主启动类实现implements CommandLineRunner方法,启动canal通道,进行监听数据库的变化,实现缓存同步更新
package com.woniu.fresh; import com.woniu.fresh.config.redis.AutoUpdateRedis; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.CommandLineRunner; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication @Slf4j @EnableAspectJAutoProxy // 让动态代理生效 @EnableScheduling // 让定时任务生效 public class FreshApp implements CommandLineRunner { public static void main(String[] args) { SpringApplication.run(FreshApp.class); } @Autowired private AutoUpdateRedis autoUpdateRedis; @Value("${canal.isOpen}") private Boolean isCanal; @Override public void run(String... args) throws Exception { if (isCanal){ log.debug(">>>>>启动缓存自动更新"); autoUpdateRedis.run(); } } } 5.前端vue代码 <template> <div> <el-row> <el-col :span="24"> <el-form :inline="true" label-width="80px"> <el-form-item label="领料单号"> <el-input v-model="findGoodsParams.shoppingNo"></el-input> </el-form-item> <el-form-item label="领料数量≥"> <el-input v-model="findGoodsParams.nums"></el-input> </el-form-item> <el-form-item label="原料名称"> <el-input v-model="findGoodsParams.rawName"></el-input> </el-form-item> <el-form-item label="领料仓库"> <el-select v-model="findGoodsParams.warehouseId" placeholder="请选择"> <el-option v-for="item in commondata.storehouse" :key="item.value" :label="item.label" :value="item.value"> </el-option> </el-select> </el-form-item> <el-form-item label="状态"> <el-select v-model="findGoodsParams.status" placeholder="请选择"> <el-option v-for="item in commondata.status" :key="item.value" :label="item.label" :value="item.value"> </el-option> </el-select> </el-form-item> <el-form-item> <el-button type="primary" @click="findGoods">查询</el-button> <el-button type="success" @click="reFindGoods">重置</el-button> </el-form-item> </el-form> </el-col> </el-row> <el-row> <el-col :span="24"> <el-button type="success" @click="addGoodsBtn">新增</el-button> <el-button type="warning" icon="el-icon-edit" @click="batchDelete">批量审批</el-button> <el-button type="primary" icon="el-icon-magic-stick" @click="batchPickDoing">批量领取中</el-button> <el-button type="primary" icon="el-icon-shopping-cart-full" @click="batchPickDown">批量已领完</el-button> </el-col> </el-row> <el-row> <el-col :span="24"> <el-table :data="tableData" style="width: 100%" @selection-change="handleSelectionChange"> <el-table-column type="selection" width="55"> </el-table-column> <el-table-column prop="pickNo" label="领料单号" width="240"> </el-table-column> <el-table-column prop="name" label="原材料名" width="100"> </el-table-column> <el-table-column prop="nums" label="领料数量" width="80"> </el-table-column> <el-table-column prop="unit" label="单位" width="80"> </el-table-column> <el-table-column prop="warehouse" label="领料仓库" width="180"> </el-table-column> <el-table-column prop="emp" label="仓管员" width="150"> </el-table-column> <el-table-column prop="status" label="状态" width="80"> <template slot-scope="scope"> <span v-if="scope.row.status == '0'" style="color: red;">未审批</span> <span v-if="scope.row.status == '1'" style="color: rgb(9, 209, 109);">已审批</span> <span v-if="scope.row.status == '2'" style="color: rgb(44, 39, 205);">已领取</span> <span v-if="scope.row.status == '3'" style="color: rgb(173, 16, 157);">已领完</span> <!-- {{ scope.row.status == 1 ? '已上架' : '下架' }} --> </template> </el-table-column> <el-table-column label="操作"> <template slot-scope="scope"> <el-button type="primary" icon="el-icon-edit" circle @click="loadBtn(scope.row.id)">审批</el-button> </template> </el-table-column> </el-table> </el-col> </el-row> <el-row> <el-col :span="24"> <div class="block"> <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page.sync="currentPage" :page-sizes="[3, 5, 10]" :page-size="3" layout="total,sizes, prev, pager, next" :total=total> </el-pagination> </div> </el-col> </el-row> <!-- 新增原材料入库弹窗 ******* --> <el-dialog title="添加原材料单" :visible.sync="b"> <el-form> <el-row> <el-col :span="12"> <el-form-item label="原料名称" clearable> <el-select v-model="shoppingNoId" placeholder="请选择" style="width: 355px;margin-right:px;margin-left:px" @change="selectBuyNo"> <el-option v-for="item in shoppingIdToNo" :key="item.label" :label="item.label" :value="item.label"> </el-option> </el-select> </el-form-item> <el-form-item label="数量" :label-width="formLabelWidth"> <el-input v-model="goods.nums" autocomplete="off"></el-input> </el-form-item> <el-form-item label="单位" clearable> <el-select v-model="goods.unit" placeholder="请选择商品单位" style="width: 355px;margin-right:px;margin-left:px"> <el-option v-for="item in commondata.unit" :key="item.value" :label="item.label" :value="item.value"> </el-option> </el-select> </el-form-item> <el-form-item label="中心仓库" clearable> <el-select v-model="goods.warehouse" placeholder="请选择" disabled style="width: 355px;margin-right:px;margin-left:px"> <el-option v-for="item in commondata.warehouse" :key="item.value" :label="item.label" :value="item.value"> </el-option> </el-select> </el-form-item> <el-form-item label="领料仓库" clearable> <el-select v-model="goods.storehouse" placeholder="请选择" style="width: 355px;margin-right:px;margin-left:px"> <el-option v-for="item in commondata.storehouse" :key="item.value" :label="item.label" :value="item.value"> </el-option> </el-select> </el-form-item> </el-col> </el-row> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="b = false">取 消</el-button> <el-button type="primary" @click="addGoods()">确 定</el-button> </div> </el-dialog> </div> </template> <script> export default { data() { return { findGoodsParams: { "pickNo": "" , "name": "" , "nums": "" , "storehouseId": "" , "status": "" }, // 批量删除的id deleteIds: [], tableData: [ { "id": 1, "pickNo": "PICK2023090818003927", "name": "富士苹果", "nums": "350.00", "unit": "千克", "warehouse": "南京江宁生鲜1号仓库", "storehouseId": 2, "emp": "领料操作员1李四", "status": "0" } ], // 分页相关的参数 total: 10, currentPage: 1, pageSize: 3, options: [ { value: '0', label: '未审批' }, { value: '1', label: '审批通过' }, { value: '2', label: '已领取' }, { value: '3', label: '已领完' }, ], commondata: { "storehouse": [ { "value": 1, "label": "南京中心仓库南京总统府" },], "status": [ { "value": 0, "label": "未审批" }, { "value": 1, "label": "审批通过" }] }, // 新增商品弹窗控制变量 b: false, // 新增的入库信息 goods shoppingNoId goods: { "name":"富士苹果", "nums":"200.56", "unit":"1", // 中心仓库 "warehouse":"1", // 目标仓库 "storehouse":"2" }, formLabelWidth: '100px', // 新增原材料入库清单 addRawTab:[{ "name": { "unit": "", "warehouse": { "1": 1000.20 } }}, ], // 和上面进行比对 shoppingNoId:"", // 选择采购清单的下拉框 shoppingIdToNo: [ {"label": "BUY2023091317093927"},], } }, methods: { handleSizeChange(val) { console.log(`每页 ${val} 条`); this.pageSize = val this.findGoods() }, handleCurrentChange(val) { console.log(`当前页: ${val}`); this.pageNum = val this.findGoods() }, findGoods() { let params = {} params.pageNum = this.currentPage params.pageSize = this.pageSize params.param = this.findGoodsParams console.log(params) this.$axios.post("/api/warehouse/findPagePickRaw", params) .then(response => { let resp = response.data console.log(resp) if (resp.resultCode.code == 20000) { this.tableData = resp.results.list this.total = resp.results.total this.currentPage = resp.results.pageNum this.pageSize = resp.results.pageSize } }) }, reFindGoods() { let params = {} this.findGoodsParams = { "pickNo": "" , "name": "" , "nums": "" , "storehouseId": "" , "status": "" }, params.pageNum = this.currentPage params.pageSize = this.pageSize params.param = this.findGoodsParams console.log(params) this.$axios.post("/api/warehouse/findPagePickRaw", params) .then(response => { let resp = response.data console.log(resp) if (resp.resultCode.code == 20000) { this.tableData = resp.results.list this.total = resp.results.total this.currentPage = resp.results.pageNum this.pageSize = resp.results.pageSize } }) }, handleSelectionChange(val) { this.deleteIds = [] console.log(val); val.forEach(e => this.deleteIds.push(e.id)) }, // 批量修改领取中 batchPickDoing() { console.log(this.deleteIds) this.$axios.put("/api/warehouse/batchDoingPickMaterial", this.deleteIds) .then(response => { let resp = response.data console.log(resp) if (resp.resultCode.code == 20000) { this.findGoods() } else { alert(resp.results) } }) }, // 批量修改已领完 batchPickDown() { console.log(this.deleteIds) this.$axios.put("/api/warehouse/batchDownPickMaterial", this.deleteIds) .then(response => { let resp = response.data console.log(resp) if (resp.resultCode.code == 20000) { this.findGoods() } else { alert(resp.results) } }) }, // 批量审批通过 batchDelete() { console.log(this.deleteIds) this.$axios.put("/api/warehouse/batchPassPickMaterial", this.deleteIds) .then(response => { let resp = response.data console.log(resp) if (resp.resultCode.code == 20000) { this.findGoods() } else { alert(resp.results) } }) }, // 逐一审批通过 loadBtn(val) { console.log(val) const deleteIds = [] deleteIds.push(val) console.log(deleteIds) this.$axios.put("/api/warehouse/batchPassPickMaterial", deleteIds) .then(response => { let resp = response.data console.log(resp) if (resp.resultCode.code == 20000) { this.findGoods() } else { alert(resp.results) } }) }, // 获取公共数据 getCommonData() { this.$axios.get("/api/common/pickCommon") .then(response => { let resp = response.data if (resp.resultCode.code == 20000) { this mondata = resp.results console.log("#############") console.log(this mondata) } }) }, addGoodsBtn() { this.b = true this.goods = {} this.shoppingIdToNo = [] this.$axios.get('/api/warehouse/findRawNames') .then(res => { if (res.data.resultCode.code == 20000) { this.addRawTab = res.data.results console.log(this.addRawTab) this.addRawTab.forEach(r=>{ this.shoppingIdToNo.push( {"label": Object.keys(r)[0]} ) }) console.log(this.shoppingIdToNo) } }) }, // 弹出的新增窗口的添加 addGoods() { console.log("#############") console.log(this.goods) this.$axios.post('/api/warehouse/addPickRaw', this.goods) .then(res => { console.log("&&&&&") console.log(res.data) if (res.data.resultCode.code == 20000) { alert('添加成功') this.findGoods() }else{ alert('添加失败') } }), this.b = false }, // 绑定下拉框的选择事件 selectBuyNo(){ console.log("change") const goodsTemp = this.addRawTab.filter(r=> Object.keys(r)[0] == this.shoppingNoId)[0] console.log(goodsTemp) const nameTmp = Object.keys(goodsTemp)[0] const nextTmp = Object.values(goodsTemp)[0].warehouse const keyTmp = Object.keys(nextTmp)[0] console.log(nextTmp) this.goods={ name:nameTmp, nums:nextTmp[keyTmp], unit:Object.values(goodsTemp)[0].unit+"", warehouse:Object.keys(nextTmp)[0] } console.log(this.goods) }, }, created() { this.findGoods() this.getCommonData() } } </script>总结
1.介绍基于canal实现数据库和缓存同步的流程; 2.给出了核心diamante的源码;
华为云云耀云服务器L实例评测|基于canal缓存自动更新流程SpringBoot项目应用案例和源码由讯客互联软件开发栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“华为云云耀云服务器L实例评测|基于canal缓存自动更新流程SpringBoot项目应用案例和源码”