springCloud+canal.deploy实现同步数据和多级缓存

1.打开mysql C:\Program Files\MySQL\MySQL Server 5.5\my.ini添加三行代码

log-bin=C:/Program Files/MySQL/MySQL Server 5.5/data/mysql-bin //自己mysql路径
binlog-format=ROW
server_id=1

2.将canal.deploy中 E:\fwq\canal.deployer-1.1.5-SNAPSHOT\conf\example\instance.properties修改为自己数据库账号密码

 3.编写lua脚本

          查询数据库的数据然后存入redis(lua脚本)

--从数据库取并存入redis(用于同步信息)

ngx.header.content_type="application/json;charset=utf8"
local function close_redis(red)
    if not red then
        return
    end
    -- 释放连接(连接池实现),毫秒
    local pool_max_idle_time = 10000 
    -- 连接池大小
    local pool_size = 100 
    local ok, err = red:set_keepalive(pool_max_idle_time, pool_size)
    local log = ngx_log
    if not ok then
        log(ngx_ERR, "set redis keepalive error : ", err)
    end
end

local uri_args = ngx.req.get_uri_args()
local cid = uri_args['cid']

local mysqlModel = require("resty.mysql")
local db = mysqlModel:new()
db:set_timeout(1000)
local ok = db:connect{
	host="127.0.0.1",
	port=3306,
	database="shop_content",
	user="root",
	password="ok"
}

if not ok then
	ngx.say('链接失败')
	db:close()
	return false;
end 

res = db:query("SELECT * FROM `tb_content` WHERE category_id="..cid)

local cjson = require("cjson")
--ngx.say(cjson.encode(res))

db:close()


local redisModel = require("resty.redis")
local redis = redisModel.new()
redis:set_timeout(1000)
local ok = redis:connect('127.0.0.1',6379)

if not ok then
	ngx.say('链接redis失败')
	return close_redis(redis)
end 


redis:select(0)

redis:set("content:"..cid,cjson.encode(res))


close_redis(redis)
ngx.say("{'flag':'success'}")

        使用java代码实现redis数据同步

依赖

<dependencies>
        <dependency>
            <groupId>com.alibaba.otter</groupId>
            <artifactId>canal.client</artifactId>
            <version>1.1.2</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.otter</groupId>
            <artifactId>canal.client-adapter</artifactId>
            <version>1.1.2</version>
            <type>pom</type>
        </dependency>

代码结构

        AsyncProccess工具类通过restTemplate请求调用lua脚本实现

package com.zb.util;

import com.alibaba.otter.canal.protocol.CanalEntry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@Component
public class AsyncProccess {

    @Autowired
    private RestTemplate restTemplate;
    
    //多线程
    @Async
    public void updateContentSync(List<CanalEntry.Column> columns) {
        for (CanalEntry.Column column : columns) {
            if (column.getName().equals("category_id")) {//根据id查询
                System.out.println("图片数据同步");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                String cid = column.getValue();
                String url = "http://localhost:9098/mysave?cid=" + cid;//lua请求地址
                String data = restTemplate.getForObject(url, String.class);
                System.out.println(data);
            }
        }
    }

}

ClientSample工具类

package com.zb.util;

import com.alibaba.otter.canal.client.CanalConnector;
import com.alibaba.otter.canal.client.CanalConnectors;
import com.alibaba.otter.canal.common.utils.AddressUtils;
import com.alibaba.otter.canal.protocol.CanalEntry.*;
import com.alibaba.otter.canal.protocol.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.net.InetSocketAddress;
import java.util.List;

@Component
public class ClientSample {

    @Autowired
    private AsyncProccess asyncProccess;

    public void main() {
        System.out.println("开启同步");
        // 创建链接
        CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress(AddressUtils.getHostIp(),
                11111), "example", "", "");
        int batchSize = 1000;
        try {
            //创建连接
            connector.connect();
            //监听mysql所有的库和表
            connector.subscribe(".*\\..*");
            //回滚到未进行ack的地方,下次fetch的时候,可以从最后一个没有ack的地方开始拿
            connector.rollback();
            boolean flag = true;
            while (flag) {
                Message message = connector.getWithoutAck(batchSize); // 获取指定数量的数据
                long batchId = message.getId();
                int size = message.getEntries().size();
                //用户没有更改数据库中的数据
                if (batchId == -1 || size == 0) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    //获取修改的每一条记录
                    printEntry(message.getEntries());
                }
                connector.ack(batchId); // 提交确认
            }
        } finally {
            connector.disconnect();
        }
    }


    private void printEntry(List<Entry> entrys) {
        for (Entry entry : entrys) {
            //检查到当前执行的代码是事物操作, 跳转下次
            if (entry.getEntryType() == EntryType.TRANSACTIONBEGIN || entry.getEntryType() == EntryType.TRANSACTIONEND) {
                continue;
            }

            //代码固定,获取rowChage对象
            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);
            }

            //rowChage getEventType 获取事件类型对象
            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));
            if (entry.getHeader().getSchemaName().equals("shop_content") && entry.getHeader().getTableName().equals("tb_content")) {
                for (RowData rowData : rowChage.getRowDatasList()) {
                    if (eventType == EventType.DELETE) {
                        //rowData.getBeforeColumnsList()获取删除之前的数据
                        printColumn(rowData.getBeforeColumnsList());
                    } else if (eventType == EventType.INSERT) {
                        //rowData.getAfterColumnsList()获取添加之后的数据
                        asyncProccess.updateContentSync(rowData.getBeforeColumnsList());
                    } else {
                        System.out.println("1---");
                        asyncProccess.updateContentSync(rowData.getBeforeColumnsList());
                        System.out.println("3---");
                    }
                }
            }

        }
    }


    private void printColumn(List<Column> columns) {
        for (Column column : columns) {
            System.out.println(column.getName() + " : " + column.getValue() + "  update=" + column.getUpdated());
        }
    }
}

4.启动类

package com.zb;

import com.zb.util.ClientSample;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableDiscoveryClient
@EnableAsync //开启异步
public class ContentApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(ContentApplication.class, args);
        ClientSample bean = run.getBean(ClientSample.class);
        bean.main();
    }

    @Bean
    public RestTemplate createRestTemplate() {
        return new RestTemplate();
    }


}

以上就是数据同步的全部步骤

多级缓存(lua脚本)

首先会先从redis中取若没有会从数据库取并放到redis中最后存到缓存中

--多级缓存

ngx.header.content_type="application/json;charset=utf8"
local uri_args = ngx.req.get_uri_args()
local cid = uri_args['cid']
--获取nginx缓存对象
local ngxCache = ngx.shared.my_cache
--获取缓存数据
local ngxData = ngxCache:get('content:'..cid)
if ngxData=="" or ngxData==nil then
	local redisModel = require("resty.redis")
	local redis = redisModel.new()
	redis:set_timeout(1000)
	local ok = redis:connect('127.0.0.1',6379)
	redis:select(0)
	local redisData = redis:get("content:"..cid);
	if ngx.null==redisData then
		local mysqlModel = require("resty.mysql")
		local db = mysqlModel:new()
		db:set_timeout(1000)
		local ok = db:connect{
			host="127.0.0.1",
			port=3306,
			database="shop_content",
			user="root",
			password="ok"
		}

		if not ok then
			ngx.say('链接失败')
			db:close()
			return false;
		end 

		res = db:query("SELECT * FROM `tb_content` WHERE category_id="..cid)

		local cjson = require("cjson")
		local jsonVal = cjson.encode(res)
		ngx.say("================>DB")
		ngx.say(jsonVal)
		redis:set("content:"..cid,jsonVal)
	else
		ngx.say("================>redis")
		ngx.say(redisData)
		--redis数据存储到缓存中
		ngxCache:set('content:'..cid,redisData,60)//缓存存储时间60s
	end 
else
	ngx.say("================>nginx")
	ngx.say(ngxData)
end 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 可以回答这个问题。canal中间件可以实现redis和mysql数据同步,它可以监控mysql数据库的binlog日志,将数据变更同步到redis中。同时,canal也支持将mysql数据同步到其他数据库,如elasticsearch等。 ### 回答2: Canal中间件是一种用于实现数据数据同步的工具。它可以实现Redis和MySQL之间的数据同步。 首先,我们需要设置Canal中间件与Redis和MySQL的连接。通过配置Canal中间件的连接参数,使其能够连接到Redis和MySQL数据库。可以设置监控的表和字段,以确定需要同步数据范围。 当MySQL数据库中的数据发生变化时,Canal中间件会自动捕获这些变化并生成相应的Binlog日志。通过监听MySQL数据库的Binlog日志,Canal中间件能够实时获取更新的数据。 接下来,Canal中间件将获取到的数据进行处理,将变化的数据转换成Redis可以接受的数据格式,并将其写入到Redis数据库中。这样就实现MySQL数据变化的同步到Redis数据库的功能。 通过Canal中间件,我们可以实现MySQL和Redis之间的双向数据同步。当MySQL中的数据发生改变时,Canal中间件会将变化的数据同步到Redis数据库中。同样地,当Redis中的数据发生改变时,Canal中间件也能够捕获这些变化并同步MySQL数据库中。 这种数据同步的方式可以提高应用程序的性能和可靠性。通过将热点数据存储到Redis中,可以提高读取性能。同时,由于Canal中间件的实时同步机制,可以保证数据的一致性。 总结起来,Canal中间件可以实现Redis和MySQL之间的数据同步。它通过捕获MySQL数据库的Binlog日志,并将变化的数据转换成Redis可以接受的格式,实现数据同步。这种方式可以提高应用程序的性能和可靠性。 ### 回答3: Canal中间件是一个开源的数据同步工具,用于将数据库中的数据同步到其他数据源。在实现Redis和MySQL数据同步的场景下,可以通过以下步骤实现数据同步: 第一步,安装配置Canal中间件。首先需要在服务器上安装Canal中间件,并配置Canal的相关参数,如数据源的地址、端口号、用户名和密码等。 第二步,配置Canal中间件连接MySQL数据库。需要在Canal配置文件中添加MySQL数据源的相关信息,包括MySQL服务器的地址、端口号、用户名和密码等。 第三步,配置Canal中间件连接Redis数据库。同样需要在Canal配置文件中添加Redis数据源的相关信息,包括Redis服务器的地址、端口号、密码等。 第四步,创建数据库表数据的监听和同步。通过创建Canal的实例,并指定需要监听的数据库和表,可以实现对特定表数据的监听和同步。 第五步,通过Canal中间件将MySQL数据同步到Redis。当MySQL数据库中的数据发生变化时,Canal中间件会将变更的数据解析并转发给Redis数据库,实现数据同步。 通过以上步骤,即可实现Redis和MySQL数据同步。Canal中间件作为数据同步的桥梁,能够实时监听MySQL数据库的变化,并将变更的数据同步到Redis,确保数据的一致性和及时性。 需要注意的是,在配置Canal中间件时,需要确保CanalMySQL、Redis之间的网络连接正常,并且对应的用户有足够的权限来进行数据操作。同时,还需要进行相关的性能测试和优化,以确保数据同步的效率和稳定性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值