前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Docker安装canal、mysql进行简单测试与实现redis和mysql缓存一致性

Docker安装canal、mysql进行简单测试与实现redis和mysql缓存一致性

作者头像
掉发的小王
发布于 2022-07-11 08:14:34
发布于 2022-07-11 08:14:34
95200
代码可运行
举报
文章被收录于专栏:小王知识分享小王知识分享
运行总次数:0
代码可运行

一、简介

canal [kə'næl],译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费。 早期阿里巴巴因为杭州和美国双机房部署,存在跨机房同步的业务需求,实现方式主要是基于业务 trigger 获取增量变更。从 2010 年开始,业务逐步尝试数据库日志解析获取增量变更进行同步,由此衍生出了大量的数据库增量订阅和消费业务。

Canal 是用 Java 开发的基于数据库增量日志解析,提供增量数据订阅&消费的中间件。 目前,Canal 主要支持了 MySQLBinlog 解析,解析完成后才利用 Canal Client 来处理获得 的相关数据。(数据库同步需要阿里的 Otter 中间件,基于 Canal)。

当前的 canal 支持源端 MySQL 版本包括 5.1.x , 5.5.x , 5.6.x , 5.7.x , 8.0.x

canal github地址

二、MySQL 的 Binlog

1. Binlog介绍

MySQL 的二进制日志可以说 MySQL 最重要的日志了,它记录了所有的 DDL 和 DML(除 了数据查询语句)语句,以事件形式记录,还包含语句所执行的消耗的时间,MySQL 的二进 制日志是事务安全型的。 一般来说开启二进制日志大概会有 1%的性能损耗。二进制有两个最重要的使用场景:

  • MySQL Replication 在 Master 端开启 Binlog,Master 把它的二进制日志传递给 Slaves 来达到 Master-Slave 数据一致的目的,这就是我们常用的主从复制。
  • 就是数据恢复了,通过使用 MySQL Binlog 工具来使恢复数据,生产上要开启,不然真的要删库跑路了 。

2. Binlog 的分类

MySQL Binlog 的格式有三种,分别是 STATEMENT,MIXED,ROW。在配置文件中可以选择配 置 binlog_format= statement|mixed|row。

  • statement:语句级,binlog 会记录每次一执行写操作的语句。比如 update user set create_date=now() 优点:节省空间。 缺点:有可能造成数据不一致。
  • row:行级, binlog 会记录每次操作后每行记录的变化。 优点:保持数据的绝对一致性 缺点:占用较大空间
  • mixed:statement 的升级版,一定程度上解决了,因为一些情况而造成的 statement 模式不一致问题,默认还是 statement,一些会产生不一致的情况还是会选择row。

==综合对比== Canal 想做监控分析,选择 row 格式比较合适。

三、工作原理

1. MySQL主备复制原理

  • MySQL master 将数据变更写入二进制日志( binary log, 其中记录叫做二进制日志事件binary log events,可以通过 show binlog events 进行查看)
  • MySQL slave 将 master 的 binary log events 拷贝到它的中继日志(relay log)
  • MySQL slave 重放 relay log 中事件,将数据变更反映它自己的数据

2. canal 工作原理

  • canal 模拟 MySQL slave 的交互协议,伪装自己为 MySQL slave ,向 MySQL master 发送dump 协议
  • MySQL master 收到 dump 请求,开始推送 binary log 给 slave (即 canal )
  • canal 解析 binary log 对象(原始为 byte 流)

==总结:==

我们可以把canal理解为从机,拿到数据然后进行后续操作,可以同步到redis上,再也不需要进行延迟双删来保证mysql和redis的数据一致性了,而且还不会出现各种各样的问题!

四、canal使用场景

场景一: 阿里 Otter 中间件的一部分 Otter 是阿里用于进行异地数据库之间的同步框架,Canal 是其中一部分。

otter github地址

场景二:保证缓存和数据库一致性(我们今天要测试的)

场景三:实时数据分析

抓取业务表的新增变化数据,用于制作实时统计

五、安装mysql、redis

1. 安装mysql

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
sudo docker run -p 3306:3306 --name mysql \
-v /mydata/mysql/log:/var/log/mysql \
-v /mydata/mysql/data:/var/lib/mysql \
-v /mydata/mysql/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7

2. Docker配置MySQL

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
vim /mydata/mysql/conf/my.cnf # 创建并进入编辑

添加如下配置:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
[mysqld]
init_connect='SET collation_connection = utf8_unicode_ci'
init_connect='SET NAMES utf8'
character-set-server=utf8
collation-server=utf8_unicode_ci
skip-character-set-client-handshake
skip-name-resolve
# 开启binlog日志:目录为docker里的目录
log-bin=/var/lib/mysql/mysql-bin
# server_id 需保证唯一,不能和 canal 的 slaveId 重复
server-id=123456
binlog_format=row
# test数据库开启,不设置则所有库开启
binlog-do-db=test

3. 重新启动mysql

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
docker restart mysql

4. 创建用户并赋权限

查看mysql的 id:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
docker ps

进入docker容器

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
docker exec -it 7d /bin/bash

连接到mysql:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mysql -u root -p

创建用户并赋予权限:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%' IDENTIFIED BY 'canal' ;

刷新:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
flush privileges;

5. Win10连接mysql创建user表

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
CREATE TABLE `user`  (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `name` varchar(25) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `sex` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

6. 创建redis

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
docker run -p 6379:6379 --name redis \
-v /mydata/redis/data:/data \
-v /mydata/redis/conf/redis.conf:/etc/redis/redis.conf \
-d redis redis-server /etc/redis/redis.conf

六、安装canal

1. 启动容器

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
docker run -it --name canal -p 11111:11111 -d canal/canal-server:v1.1.5

查看三个容器:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
docker ps

2. 配置canal

进入容器:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
docker exec -it 56 /bin/bash

切换目录:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cd canal-server/conf/example

修改两个地方:

第一个是mysql的地址,第二个是我们创建数据库名字(可以使用默认带的,就是全部的库都进行收集binlog日志)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
canal.instance.master.address=192.168.84.138:3306

canal.instance.filter.regex=test\..*

3. 查看日志

我们查看一下canal的日志,看是否启动成功! 首先进入容器:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
docker exec -it 56 /bin/bash

切换目录:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cd canal-server/logs/example/

查看日志:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cat example.log

无报错,刚刚新建的表这里也可以检测到!

4. 查看canal.properties

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cd /canal-server/conf
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cat canal.properties

我们可以看到有很多个模式,可以把canal收集到的binlog发送到三大MQ中,或者tcp。

本次以tcp为准测试,如果大家有需求可以进行发送到MQ,往下滑都有对应的配置!

七、简单测试

1. 新建springboot项目,导入依赖

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>com.alibaba.otter</groupId>
    <artifactId>canal.client</artifactId>
    <version>1.1.0</version>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.8.6</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.module</groupId>
    <artifactId>jackson-module-jaxb-annotations</artifactId>
    <version>2.8.6</version>
</dependency>

2. 编写测试文件

来自官方例子:

==我把statis关键字删除了,方便和redis进行整合==

例子地址

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import com.alibaba.otter.canal.client.CanalConnector;
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.alibaba.otter.canal.client.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


/**
 * @author wangzhenjun
 * @date 2022/6/29 9:31
 */
@Configuration
public class SimpleCanalClientExample {

//    private static String REDIS_DATABASE = "mall";
//    private static String REDIS_KEY_ADMIN = "ums:admin";

    @Bean
    public void canalSync() {
        // 创建链接,第一个参数是canal的ip,第二个参数是canal的端口号,
        // 第三个参数是canal虚拟的模块名称,canal是创建的数据库账号密码
        CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress("192.168.84.138",
                11111), "example", "canal", "canal");
        int batchSize = 1000;
        int emptyCount = 0;
        try {
            connector.connect();
            // 对应上面的配置只对test库进行获取binlog文件
            connector.subscribe("test\\..*");
            connector.rollback();
            int totalEmptyCount = 120;
            while (emptyCount < totalEmptyCount) {
                Message message = connector.getWithoutAck(batchSize); // 获取指定数量的数据
                long batchId = message.getId();
                int size = message.getEntries().size();
                if (batchId == -1 || size == 0) {
                    emptyCount++;
                    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("================&gt; binlog[%s:%s] , name[%s,%s] , eventType : %s",
                    entry.getHeader().getLogfileName(), entry.getHeader().getLogfileOffset(),
                    entry.getHeader().getSchemaName(), entry.getHeader().getTableName(),
                    eventType));

            for (RowData rowData : rowChage.getRowDatasList()) {
                if (eventType == EventType.DELETE) {
                    printColumn(rowData.getBeforeColumnsList());
                } else if (eventType == EventType.INSERT) {
                    printColumn(rowData.getAfterColumnsList());
                } else {
                    System.out.println("-------&gt; before");
                    printColumn(rowData.getBeforeColumnsList());
                    System.out.println("-------&gt; after");
                    printColumn(rowData.getAfterColumnsList());
                }
            }
        }
    }
	
    private void printColumn(List<Column> columns) {
        for (Column column : columns) {
            System.out.println(column.getName() + " : " + column.getValue() + "    update=" + column.getUpdated());
        }
    }
}

3. 启动项目

4. 插入一条数据

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
INSERT INTO user VALUES (1,'小红','女');

==总结:== 我们测试是可以获取到binlog日志的,下面我们进入实战:实现redis缓存同步

八、实战redis同步缓存

1. 编写redis序列化配置类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * @author wangzhenjun
 * @date 2022/6/30 9:24
 */
@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        Jackson2JsonRedisSerializer<?> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

}

2. 添加redis增删改方法

主要添加了同步到redis的两个方法,这里是2分钟就会停止监听,大家可以按自己的来调整:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int totalEmptyCount = 120;
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import java.net.InetSocketAddress;
import java.util.List;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.otter.canal.client.CanalConnector;
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.alibaba.otter.canal.client.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;


/**
 * @author wangzhenjun
 * @date 2022/6/29 9:31
 */
@Configuration
public class SimpleCanalClientExample {

    @Autowired
    private RedisTemplate redisTemplate;

    private static final String KEY = "user:info";

    @Bean
    public void canalSync() {
        // 创建链接,第一个参数是canal的ip,第二个参数是canal的端口号,
        // 第三个参数是canal虚拟的模块名称,canal是创建的数据库账号密码
        CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress("192.168.84.138",
                11111), "example", "canal", "canal");
        int batchSize = 1000;
        int emptyCount = 0;
        try {
            connector.connect();
            // 对应上面的配置只对test库进行获取binlog文件
            connector.subscribe("test\\..*");
            connector.rollback();
            int totalEmptyCount = 120;
            while (emptyCount < totalEmptyCount) {
                Message message = connector.getWithoutAck(batchSize); // 获取指定数量的数据
                long batchId = message.getId();
                int size = message.getEntries().size();
                if (batchId == -1 || size == 0) {
                    emptyCount++;
                    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("================&gt; binlog[%s:%s] , name[%s,%s] , eventType : %s",
                    entry.getHeader().getLogfileName(), entry.getHeader().getLogfileOffset(),
                    entry.getHeader().getSchemaName(), entry.getHeader().getTableName(),
                    eventType));

            for (RowData rowData : rowChage.getRowDatasList()) {
                if (eventType == EventType.DELETE) {
                    printColumn(rowData.getBeforeColumnsList());
                    // 同步到redis
                    delete(rowData.getBeforeColumnsList());
                } else if (eventType == EventType.INSERT) {
                    printColumn(rowData.getAfterColumnsList());
                    // 同步到redis
                    insertOrUpdate(rowData.getAfterColumnsList());
                } else {
                    System.out.println("-------&gt; before");
                    printColumn(rowData.getBeforeColumnsList());
                    System.out.println("-------&gt; after");
                    printColumn(rowData.getAfterColumnsList());
                    // 同步到redis
                    insertOrUpdate(rowData.getAfterColumnsList());
                }
            }
        }
    }

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

    /**
     * 更新或者添加触发同步到redis
     * @param columns
     */
    private void insertOrUpdate (List<Column> columns) {
        if (columns.size() > 0) {
            JSONObject json = new JSONObject();
            for (Column column : columns) {
                json.put(column.getName(), column.getValue());
            }
            redisTemplate.opsForHash().put(KEY,columns.get(0).getValue(),json.toJSONString());
        }
    }

    /**
     * 删除触发同步到redis
     * @param columns
     */
    private void delete (List<Column> columns) {
        if (columns.size() > 0) {
            redisTemplate.opsForHash().delete(KEY, columns.get(0).getValue());
        }
        
    }
}

3. 测试添加

数据库插入一条:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
insert into user values (1,'我是测试添加','男');

控制台捕捉到信息:

我们看到redis已经有数据了,同步成功!

4. 测试更新

更细我们刚刚添加的那条数据:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
update user set name = '修改了' where id = 1;

控制台捕捉到了更新信息:

redis也同步修改了!

5. 测试删除

我们先多添加几条哈:

删除id为1的那条数据:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
delete from user where id = 1;

控制台捕捉到了删除信息:

redis也同步删除了!

九、总结

这样就实现了一个canal的应用场景,当然也可以把binlog的数据发送到MQ来!


Q.E.D.

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-06-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
android-ipc介绍
按照操作系统中的描述 线程是CPU调度的最小单元 同时线程是一种有限的系统资源 进程一般指一个执行单元 在PC和移动设备上指一个应用 一个进程可以包含多个线程 因此进程和线程是包含与被包含的关系 最简单的情况下一个进程中可以只有一个线程即主线程 在Android里面主线程也叫UI线程 在UI线程里才能操作界面元素
tea9
2022/07/16
4600
android-ipc介绍
Android项目实战(十九):Android Studio 优秀插件: Parcelable Code Generator                       Android Studi
Android Studio 优秀插件系列: Android Studio 优秀插件(一):GsonFormat Android Studio 优秀插件(二): Parcelable Code Generator ----------------------------------------------------------------------------- Parcelable  , 这个词大家应该不陌生吧,用于序列化对象的一个接口 不清楚的可以看一下这篇博客:Intent传递对象的两种方法
听着music睡
2018/05/18
1.5K0
Android十八章:多进程基础
这一章主要讲述多进程的作用,序列化和反序列化,Serializable和Parcelable
ppjun
2018/09/05
4750
android之Parcelable介绍
Parcelable类的概述在SDK中是这样的:这些类的接口可以将实例写入或从中还原 Parcel。
李小白是一只喵
2020/12/31
6590
安卓中Serializable 比 Parcelable好在哪?
(3)在安卓中使用 Intent 进行传输时候,数据类型较为复杂的需要进行序列化操作 。
没关系再继续努力
2021/11/11
4920
Android中的序列化:Parcelable和Serializable
概述 序列化:将一个对象转换成 可存储或 可传输的状态。 Parcelable和Serializable的区别 作用 Serializable的作用是为了保存对象的属性到本地文件、数据库、网络流、rmi以方便数据传输,当然这种传输可以是程序内的也可以是两个程序间的。 Parcelable的设计初衷是因为Serializable效率过慢,为了在程序内不同组件间以及不同Android程序间(AIDL)高效的传输数据而设计,这些数据仅在内存中存在,Parcelable是通过IBinder通信的消息的载体。 性能比
用户1205080
2018/12/21
7990
Serializable和Parcelable的再次回忆
自己开发Android也有些时间了,Serializable和Parcelable遇到过不止一次了。但是每次别人问起具体的内容自己偏偏记得不是很清晰。因为某些原因再次梳理一下,以文章的形式给自己存储下来。温故而知新!!
静默加载
2020/05/31
6100
AIDL源码解析in、out和inout
为什么会想写这篇文章,只因为一个error idl.exe E 4928 5836 type_namespace.cpp:130] 'Book' can be an out type, so you must declare it as in, out or inout. 看过上一篇文章Android:IPC之AIDL的学习和总结的同学都知道这是因为在AIDL文件中使用非常规类型作为参数传递的时候没有标记指向tag,那么到底为什么会是这样子的呢,作为一个好奇宝宝我想好好看看。
静默加载
2020/05/29
1.9K0
AS插件-Android Parcelable code generator.
输入 Android Parcelable code generator ,点击安装即可,安装之后 重启,会看到上图选中部分所示。
小小工匠
2021/08/16
4180
android一个弹出菜单的动画(二)「建议收藏」
代码:http://download.csdn.net/detail/baidu_nod/7731115
全栈程序员站长
2022/07/08
5440
android一个弹出菜单的动画(二)「建议收藏」
parcel和parcelable
Parcel 在英文中有两个意思,其一是名词,为包裹,小包的意思; 其二为动词,意为打包,扎包。邮寄快递中的包裹也用的是这个词。Android采用这个词来表示封装消息数据。这个是通过IBinder通信的消息的载体。需要明确的是Parcel用来存放数据的是内存(RAM),而不是永久性介质(Nand等)。 Parcelable,定义了将数据写入Parcel,和从Parcel中读出的接口。一个实体(用类来表示),如果需要封装到消息中去,就必须实现这一接口,实现了这一接口,该实体就成为“可打包的”了。 接口的
xiangzhihong
2018/01/30
9250
Java IO之对象的序列化、ObjectInputStream和ObjectOutputStream类
Java将数据从源(文件、内存、键盘、网络)读入到内存 中,形成了流,然后将这些流还可以写到另外的目的地(文件、内存、控制台、网络),之所以称为流,是因为这个数据序列在不同时刻所操作的是源的不同部分。按照不同的分类标准,IO流分为不同类型。主要有以下几种方式:按照数据流方向、数据处理的单位和功能。
用户7886150
2021/04/25
1.1K0
Android:IPC之AIDL的学习和总结
为了使得一个程序能够在同一时间里处理许多用户的要求。即使用户可能发出一个要求,也肯能导致一个操作系统中多个进程的运行(PS:听音乐,看地图)。而且多个进程间需要相互交换、传递信息,IPC方法提供了这种可能。IPC方法包括管道(PIPE)、消息排队、旗语、共用内存以及套接字(Socket)。
静默加载
2020/05/29
1.4K0
Android Parcelable
Interface for classes whose instances can be written to and restored from a Parcel. 
阳光岛主
2019/02/19
6260
Art of Android Development Reading Notes 2
(1)任何一个操作系统都需要有相应的IPC机制,Linux上可以通过命名通道、共享内存、信号量等来进行进程间通信。 (2)Android系统不仅可以使用Binder机制来实现IPC,还可以使用Socket实现任意两个终端之间的通信。
宅男潇涧
2018/08/01
5220
Art of Android Development Reading Notes 2
Parcelable与Serializable
由于 Java 的 Serializable 的性能较低,Parcelable 正式在这个背景下产生的,它核心作用就是为了解决 Android 中大量跨进程通信的性能问题。
老马的编程之旅
2022/06/22
1.2K0
Android开发笔记(二十七)对象序列化
程序中存储和传递信息,需要有个合适的数据结构,最简单的是定义几个变量,变量多了之后再分门别类,便成了聚合若干变量的对象。代码在函数调用时可以直接传递对象,但更多的场合例如与文件交互、与网络交互、组件之间交互等等,就无法直接使用未经处理的对象。因此Java引入了序列化的概念,用于把一个对象转换为字节序列,然后再对这个字节序列做存储和传递操作。与之对应的是反序列化,反序列化是把一个字节序列恢复为Java对象的过程,而序列化是把Java对象转化为字节序列的过程。
aqi00
2019/01/18
6620
Android序列化总结
公园里,一位仙风鹤骨的老者在打太极,一招一式都仙气十足,一个年轻人走过去:“大爷,太极这玩意儿花拳绣腿,你练它干啥?”老者淡淡一笑:“年轻人,你还没有领悟到太极的真谛,这样,你用最大力气打我试试。”于是年轻人用力打了老头一拳,被讹了八万六。
大公爵
2018/09/05
8460
Android序列化总结
彻底理解Serializable和Parcelable
这里有二个关键字,存储和传输,存储的场景比如对象的持久化,传输的场景比如将对象通过网络传输,然后在需要使用的时候,反序列化,重新创建对象。
三好码农
2019/03/15
1.2K0
Android技能树 — 多进程相关小结
这次是讲Android存储路径及IO的基本操作。因为我们在开发的时候会经常这种方便的需求。这篇文章的内容我写的可能很少,都没有细写。别吐槽。o( ̄︶ ̄)o
青蛙要fly
2018/08/29
4570
Android技能树 — 多进程相关小结
相关推荐
android-ipc介绍
更多 >
LV.1
腾讯开发工程师
目录
  • 一、简介
  • 二、MySQL 的 Binlog
    • 1. Binlog介绍
    • 2. Binlog 的分类
  • 三、工作原理
    • 1. MySQL主备复制原理
    • 2. canal 工作原理
  • 四、canal使用场景
  • 五、安装mysql、redis
    • 1. 安装mysql
    • 2. Docker配置MySQL
    • 3. 重新启动mysql
    • 4. 创建用户并赋权限
    • 5. Win10连接mysql创建user表
    • 6. 创建redis
  • 六、安装canal
    • 1. 启动容器
    • 2. 配置canal
    • 3. 查看日志
    • 4. 查看canal.properties
  • 七、简单测试
    • 1. 新建springboot项目,导入依赖
    • 2. 编写测试文件
    • 3. 启动项目
    • 4. 插入一条数据
  • 八、实战redis同步缓存
    • 1. 编写redis序列化配置类
    • 2. 添加redis增删改方法
    • 3. 测试添加
    • 4. 测试更新
    • 5. 测试删除
  • 九、总结
加入讨论
的问答专区 >
1合伙人擅长4个领域
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档