1.借助Canal实现MySQL数据库间链接canal链接mysql
2.蓝牙耳机有监听功能吗?
借助Canal实现MySQL数据库间链接canal链接mysql
借助Canal实现MySQL数据库间链接
在现代化的源码应用开发中,不同的解析系统或应用之间可能需要共享同一个数据库,这时候就需要实现数据库间的源码链接。本文将介绍如何借助Canal实现MySQL数据库间链接。解析Canal是源码阿里巴巴开源的一款数据库增量订阅和消费组件,支持MySQL、解析微信源码社区Oracle等数据库,源码它可以将数据库更新的解析数据通过可靠的方式同步到其他数据存储、NoSQL等系统中。源码
一、解析Canal介绍
Canal是源码阿里巴巴开源的一款数据库增量订阅和消费组件,是解析基于MySQL数据库增量日志构建的,从而实现了与数据源(如MySQL)解耦,源码达到了异构神异的解析目的。Canal主要包括三个模块: Canal.Admin、源码Canal.Server和Canal.Client。
Canal.Admin: Canal控制台管理界面,用于管理Canal的启停和监控
Canal.Server: Canal的工作服务端,负责从数据源(如MySQL)订阅增量日志,并把日志传输给客户端
Canal.Client: Canal的客户端,用于订阅和消费Canal.Server传输的数据
二、Canal的使用场景
Canal主要应用于以下场景:
1、数据实时同步
提供不同数据存储的property源码python数据实时同步,如 MySQL 到 Elasticsearch 的同步,实时更新数据,保持数据一致
2、数据订阅
对于需要全量数据同步的场景,结合 snapshot 快照机制,可以实现数据全量订阅
3、实时数据分析
对数据实时抓取,进行数据分析计算
4、缓存更新
将数据更新到Cache(如Redis)中,提升系统性能
三、Canal的具体实现
将A库的数据同步到B库中,具体实现如下:
1、安装Canal
Canal的安装需要先下载源码,然后进行编译打包,具体步骤可以参考Canal官网: /alibaba/canal
2、配置Canal
Canal的配置文件位于config文件夹下,通过修改canal.properties实现配置。
(1)配置MySQL的主从关系
# mysql主从地址信息
canal.instance.master.address=.0.0.1:
canal.instance.dbUsername=canal_test
canal.instance.dbPassword=canal_test
# 配置binlog信息,也可以从当前解析到的binlog中获取,
# 优先从binlog position 获取,找不到才到 GTID_GET中获取, gtid模式推荐打开,
# 当前的timestamp可以通过show master status或 show binary logs获取
canal.instance.connectionCharset = UTF-8
canal.instance.gtidon=on
canal.instance.position =
(2)配置Canal连接内容
# 配置instance连接信息
canal.instance.filter.regex=canal_test.tb_goods
canal.instance.rds.accesskey=
canal.instance.rds.secretkey=
(3)配置数据输出方式
# 配置数据输出方式
canal.mq.topic=test
# 指定数据传输格式
canal.mq.flatMessage = false
(4)配置kafka通常参数和账号信息
# canal.mq.producerGroup 客户端group组名,同一个topic下的萝卜源码编译不同group组互不影响
# canal.mq.servers 指定mq服务器的地址
# canal.mq.topics 指定MQ topic主题名称
# authAccount ,配置到应用已指定的账号
canal.mq.properties.bootstrap.servers=...:
canal.mq.producer.bootstrap-servers=...:
canal.mq.producer.topic=myTest can
(5)启动Canal
执行bin目录下的startup脚本,即可启动Canal。
3、配置Canal客户端
在B库中新建表,同步A库的数据到该表中。
(1)在B库中创建表
mysql> create database canal_test2;
mysql> use canal_test2;
mysql> create table tb_goods(
-> id int() not null auto_increment primary key,
-> name varchar() not null,
-> price int() not null
-> )engine=innodb default charset=utf8;
(2)在Canal服务端中新增instance,即配置同步关系
mysql> create database canal_client;
mysql> use canal_client;
mysql> create table canal_client.tb_goods(
-> id int() not null,
-> name varchar() not null,
-> price int() not null
-> )engine=innodb default charset=utf8;
mysql> GRANT ALL PRIVILEGES ON canal_client.* TO canal@’%’ IDENTIFIED BY ‘canal_test’ WITH GRANT OPTION;
(3)在Canal客户端中启动Canal
通过Canal客户端,将A库的数据同步到B库中的表中,具体代码实现如下:
public class SimpleCanalClientExample {
public static void mn(String[] args) {
// 从控制台读取参数
String host = args[0];
int port = Integer.valueOf(args[1]);
String destination = args[2];
String username = args[3];
String password = args[4];
CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress(host,
port), destination, username, password);
int batchSize = ;
try {
connector.connect();
connector.subscribe(“canal_test.tb_goods”);
connector.rollback();
while (true) {
Message message = connector.getWithoutAck(batchSize); // 获取指定数量的数据
long batchId = message.getId();
int size = message.getEntries().size();
if (batchId == -1 || size == 0) {
Thread.sleep();
} else {
System.out.printf(“batchId: %s, size: %s \n”, batchId, size);
printEntry(message.getEntries());
}
connector.ack(batchId);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
connector.disconnect();
}
}
private static void printEntry(List entries) {
for (CanalEntry.Entry entry : entries) {
if (entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONBEGIN
|| entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONEND
|| entry.getEntryType() == CanalEntry.EntryType.HEARTBEAT) {
continue;
}
RowChange rowChange = null;
try {
rowChange = RowChange.parseFrom(entry.getStoreValue());
} catch (Exception e) {
continue;
}
for (CanalEntry.RowData rowData : rowChange.getRowDatasList()) {
if (rowChange.getEventType() == CanalEntry.EventType.DELETE) {
printColumn(rowData.getBeforeColumnsList());
} else if (rowChange.getEventType() == CanalEntry.EventType.INSERT) {
printColumn(rowData.getAfterColumnsList());
} else {
printColumn(rowData.getBeforeColumnsList());
System.out.println(“=======”);
printColumn(rowData.getAfterColumnsList());
}
}
}
}
private static void printColumn(List columns) {
for (CanalEntry.Column column : columns) {
System.out.println(column.getName() + “\t” + column.getValue() + “\t” + column.getUpdated());
}
}
}
通过以上代码,我们就可以将A库的tb_goods表中的数据实时同步到B库中的canal_client库的tb_goods表中。
四、总结
Canal是一款非常优秀的数据库增量订阅和消费组件,它可以很好地解决数据库间链接的问题,实现不同数据存储之间的数据同步。我们可以通过Canal的控制台管理界面,或者通过Canal的客户端代码实现数据库之间的数据同步。当然,Canal也有其缺点,就是在高并发场景下,可能会受到性能的限制,这需要我们在具体的应用场景中进行实际评估。
蓝牙耳机有监听功能吗?
1.蓝牙耳机的paddleocr源码运营使用
蓝牙耳机的使用说明书中都会有相关的详细使用说明,这里拣重点说明一下。除了电源开关,耳机上一般有三个键。如下所示:
它们每个都是多功能键,在不同的情况下有不同的功能。1号键的功能包括:开始播放音乐/停止插入音乐/接听电话/挂断电话;2号键的功能有:增加音量/上一曲;3号键的功能有 减小音量/下一曲。
注:暂不包括通话模式;其它型号蓝牙耳机并不一定完全相同。
2.蓝牙耳机上的按键实现原理
试用后会明白,一个按键会有多个功能,那么在Android系统中是如何表示的呢?其实对于Android系统,每次按键只会有一个唯一「键值」响应,使用起来感觉会模模糊糊的,但是其实对于系统来说是很清晰的。
AVRCP全称(Audio/Video Remote Control Profile),是蓝牙协议中的一个profile。从名字上就可以看出主要应用于Audio/Video控制。每个按键并不是独立的,上-曲/下一曲是在正在播放音乐的时候才会有效,即才会向Android发送「键值」。
基于按键从Linux到Android分析具体对于的键值:
Linux扫描码 功能 映射字串 Android键值
c8 开始放音乐 MEDIA_PLAY KEYCODE_MEDIA_PLAY
c9 停止放音乐 MEDIA_PAUSE KEYCODE_MEDIA_PAUSE
a3 下一曲 MEDIA_NEXT KEYCODE_MEDIA_NEXT
a5 上-曲 MEDIA_PREVIOUS KEYCODE_MEDIA_PREVIOUS
Android应用代码,完整测试应用:TeskKey。
总结:1号键会交替发送KEYCODE_MEDIA_PLAY/KEYCODE_MEDIA_PAUSE;2/3号键会在播放音乐时分别发送KEYCODE_MEDIA_PREVIOUS/.KEYCODE_MEDIA_NEXT。如果想要把蓝牙耳机上的react源码ppt按键利用起来,可以在接收到KEYCODE_MEDIA_PLAY时播放 无声音乐 以使能2/3号键。这样就能完整接收3种键值了自行控制了。这个具体自行设计(测试代码已经更新包含了)。
注:这种实现并不一定通用,比如我在深度定制的MIUI中测试,尽管启动的TestKey应用,系统自带的音乐播放器仍然能同时响应键值。
更新:
已经更新TestKey源码,添加对蓝牙耳机按键的监听,实现方法就是上述中推测的方法,已经成功验证过了。播放音乐参考《Android多媒体开发--资源文件播放》。效果图:
需要说明一点的是,程序带了两个音乐文件在/res/raw中,默认播放的是lapple.mp3一个有声音的mp3音乐文件,silencesec.mp3是一个无声音的秒钟音乐文件,实际应用中可以使用它。
问与答
1.这个只能在播放音乐的状态下才能监听到么?
答:根据上述的原理,这些按键也仅仅是应用在控制媒体时使用;且根据实际验证没有播放音乐时蓝牙耳机的2/3号键是并没有向Android设备发送键值(从底层Linux来看)。综上所述,需要通过播放音乐来实现激活其向Android设备发送键值,针对这种情况可以播放一个「没有声音」的音乐文件来实现,这样既可以监听到2/3号键又可以不影响其它声音的输出。可以在前台时播放音乐,后台停止播放。
2.我现在主要是想监听得到开关键(1号键)。 在做一个按下蓝牙开关键后启动一个语音识别的功能?
答:在我的测试条件下,1号键是可以正常监听到的。1号键会交替发送KEYCODE_MEDIA_PLAY/KEYCODE_MEDIA_PAUSE键值。这个键不需要模拟播放音乐就可以正常的监听到。
3. 4号按键的监听方法
答:所谓的4号按键,也就是指本文中的所测试型号的蓝牙耳机上并没有,但有可能其它型号的蓝牙耳机上有。我没有办法测试验证,所以这里就简单叙述一下「新按键」的键值确定思路:1.先使用TestKey测试应用测试按键,测试Android上层是否可以得到对应键值。2.如果没有得到,那么就使用adb shell getevent来看Linux底层可以不可以得到键值。然后根据按键从Linux到Android来确定Android上层使用的键值码到底是多少。(当然,如果你实在不知道如何监听,把蓝牙耳机寄给我,我给你确定也行。:))
注:其实上述文章完全是根据按键从Linux到Android测试确定下来的。那是篇文章是剥开Android外壳来看「按键」事件的流程的,方法适用于所有输入事件:各种按键/触摸/物理键盘/鼠标等待输入设备。没有一定的Linux开发经验很难看懂和理解。
4. Android后台监听按键怎么实现
或:如何启动一次应用后在后台一直监听播放键 因为有这样一个场景 在用户开车的时候需按一下开关键就启动语音识别的功能。
这个问题其实已经超出了本文讨论的范围,是Android系统对应用层的键盘事件(按键)的分发的问题了。正常情况下,按键只会向当前最端的应用分发键盘事件,也就是说在后台你边音量键都监听不了。
但是既然这种情况(后台应用监听按键)的需求存在,那么就一定有它存在的道理。比如「相机键」,按下后直接调出相机到最前台。从表面上看是相机响应了按键,但是从实现方法上来看,并不是通过键值来操作,必须通过其它方法,比如广播或者其它等等。
明白了其中的道理后,那么想要实现就好办了。先看这个按键有没有广播,如要有接听系统中发出来的广播;如果没有那么对于定制系统可以自己在系统中添加一个广播;总之,正常渠道是没有办法在后台监听一些不应该是你监听到的按键的。
更:查了一下,这个按键是有广播的。这样就可以后台响应了(不需要C/不需要root)。例子我就不试了,见Android官方例子RandomMusicPlayer。
其中的重点是这个广播android.intent.action.MEDIA_BUTTON。
更:
本来不想再更新那个Testkey了,好吧,我还是更新了一下,关于后台监听媒体相关按键的方法。没有在界面上更新,通过LogCat查看打印信息。
上述内容算「鱼」,下边把「渔」也提供了:
第一步先直接编译运行RandomMusicPlayer,然后发现确实不能收到广播,然后判断是系统版本问题,有可能是RandomMusicPlayer并没有更新为适合4.0以上的。在Capture media button on Android >=4.0 (works on 2.3)这里找到了问题所在,添加之成功。然后整合到Testkey上,具体改了哪些内容通过Github上的commit id查看。
更:
关于测试程序的源码,是托管在Github上的,文中找到"TestKey"链接,打开后如下图下载:
APK下载
更新源码,添加编译好的APK文件。
直接下载地址:/kangear/TestKey/raw/master/apk/TestKey.apk
更:
已经root的Android设备获取键值方法
如果设备已经root过了,那么可以更底层地获取键值。
1.下载终端模拟器/soft/item?docid=
2.运行su -c getevent 然后按下需要测试的按键 注:su -c getevent中间有空格.
3.会有如下输出,其中每次按下后输出数据的倒数第二行 倒数第二个是该按键的原始键值
4.将此键值告知我,我来判断如何实现相应功能
注:
1.该命令会监听所有输入事件 触摸屏/按键/蓝牙耳机/鼠标键盘 都会检测到,所以输出命令后不要再点击触摸屏 不然会有大量输出.
2.一般情况下每次按键会输出4行信息.
3.下图最后输出4行是按下 Vol- 的输出.如果你的测试没有达到这个效果,要自行去对比哪里出现了问题.
4.以下监听蓝牙按键的实例
更:
哪些键值会是蓝牙传递过来的
1. 连接蓝牙后,如果直接按下蓝牙耳机上的某个键,你的手机会自动播放音乐,那么说明是该键值是从蓝牙耳机传递过来的.
2. 播放音乐后,如果可以仅仅通过蓝牙耳机上的某个键,你的手机会切换音乐,那么说明是该键值是从蓝牙耳机传递过来的.
3. 播放音乐时,如果可以仅仅通过蓝牙耳机上的某个键,你的手机屏幕上显示增大或者减小音量,那么说明该键值是从蓝牙耳机传递过来的.反之如果只是蓝牙耳机内部音量进行了改变,Android没有任何的反应,那么说明该按键仅仅有控制蓝牙耳机音量的功能,并没有向Android上报键值.
4. 连接蓝牙通话,如果通过按下某个键,可以挂断电话,那么说明该按键是从蓝牙耳机传递过来的.
案例:
a.某个4.0版本协议的蓝牙耳机更注重省电,在控制音量时只是自身喇叭音量增大或者减小,并不是向Android系统报告需要增大或者减小音量的,那么说明这个按键根本从来就没有向Android报告过键值.(无按键上传)
b.某些型号的蓝牙耳机,本身不控制音量,当按下按下音量键时,会将键值传递给Android设备,要求Android进行增大或者减小音频源的音量 来实现音量的控制.(有按键上传)
以上两种情况,虽然用户体验是并无太大差别,但是实现原理极为不同的.
补充知识:
播放音量/音频通话是蓝牙耳机中两个不同的规范,一般情况下都会同时支持,但是有时候会仅仅支持后者(也称 单声道蓝牙耳机),为的是更省电.当然也会进一步压缩一些功能.
更 单通道耳机不能使用上述方法检测按键分析
蓝牙标准规范列表:https://en.wikipedia.org/wiki/List_of_Bluetooth_profiles
这里说明几个常用的规范:(规范也可以理解为通道)
1._正常_播放音乐
2._正常_播放音乐时按键
3.语音通话/以及语音通话时按键
注:对于单声道耳机(默认只能接听电话)通过某些软件实现的播放音乐的原理是将「音乐的信号」通过「语音通话通道」传输给耳机的。耳机实质还是工作在「语音通话」模式下。
说一千道一万,对于第三种情况下的非按键的按键如何检测,且听下回分解。;)
这里简单推测一下原理,第3种情况下"按键"是被包括在通话语音信号中的,应该是直接被Phone应用解析并进行了相应操作。Android4.2以上的版本中会有一个内置的无界面的应用程序名字叫Bluetooth.apk,它是Android系统对中蓝牙设备支持的核心,所有的规范(profile)都是通过它来解析的。所以要研究一下它和Phone应用之间做了什么见不得人的勾当才能知道如何。 ;)
当然简单一点,也可以通过监听音量变化也实现曲线监听等等,不过这个不是我研究的重点。
另外一点是 对于单声道耳机按键研究 目前也只能是挖的一个坑。什么时候埋暂时不好说。
先写到这里 (语音通话时“按键”处理过程)
文字描述:已经找到处理过程,和推测的一致。Bluetooth解析出特定的“按键”后直接进行处理,没有向外界通知什么。根据高亮的英文也能猜测出含意。第一张图是语音通话时的各种“按键”事件,第二张图是对“拨号/重拨”键的处理。高亮出是重拨时获取通话记录中最后一个号码。如果可以访问Google那么你也可以直接在线查看我截图的这两段代码片段。HeadsetStateMachine.java(另:我是基于4.2.2分析的),这是简单的分析过程,对于如何在APP中监听,下回再说。