├── README.md └── hiriver-modules ├── .gitignore ├── LICENSE ├── hiriver-sample ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── hiriver │ │ └── sample │ │ └── SampleConsumer.java │ ├── resources │ ├── hiriver-sample.properties │ ├── logback.xml │ └── spring │ │ ├── spring-binlog.xml │ │ ├── spring-gtid.xml │ │ ├── spring-root.xml │ │ └── spring-timestamp.xml │ └── webapp │ └── WEB-INF │ └── web.xml ├── hiriver ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── hiriver │ │ ├── channel │ │ ├── BinlogDataSet.java │ │ └── stream │ │ │ ├── BinlogPositionStoreTrigger.java │ │ │ ├── BufferableBinlogDataSet.java │ │ │ ├── ChannelBuffer.java │ │ │ ├── ChannelStream.java │ │ │ ├── Consumer.java │ │ │ ├── TransactionRecognizer.java │ │ │ └── impl │ │ │ ├── AbstractConsumer.java │ │ │ ├── AbstractTransactionRecognizer.java │ │ │ ├── BinlogNameAndPosTransactionRecognizer.java │ │ │ ├── ChannelStreamContext.java │ │ │ ├── DefaultBufferableBinlogDataSet.java │ │ │ ├── DefaultChannelBuffer.java │ │ │ ├── DefaultChannelStream.java │ │ │ ├── GTIDTransactionRecognizer.java │ │ │ ├── LimitByRowsChannelBuffer.java │ │ │ ├── PersistPosBufferableBinlogDataSet.java │ │ │ └── TimestampTransactionRecognizer.java │ │ ├── position │ │ └── store │ │ │ ├── BinlogPositionStore.java │ │ │ └── impl │ │ │ ├── AbstractBinlogPositionStore.java │ │ │ └── FileBinlogPositionStore.java │ │ └── streamsource │ │ ├── DbHostInfo.java │ │ ├── DbHostInfoSupplier.java │ │ ├── StreamSource.java │ │ ├── StreamSourceSelector.java │ │ └── impl │ │ ├── AbstractStreamSource.java │ │ ├── HAStreamSource.java │ │ ├── MysqlStreamSource.java │ │ ├── RandomStreamSourceSelector.java │ │ ├── StaticDbHostInfoSupplier.java │ │ └── TimestampBasedStreamSource.java │ └── test │ └── java │ └── com │ └── hiriver │ ├── AppTest.java │ ├── channel │ └── stream │ │ └── impl │ │ └── LimitByRowsChannelBufferTest.java │ └── streamsource │ └── impl │ └── TimestampBasedStreamSourceTest.java ├── mysql-proto ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ ├── hiriver │ │ ├── local │ │ │ ├── DefaultLocalEventProcessor.java │ │ │ ├── LocalBinlogReader.java │ │ │ └── LocalEventProcessor.java │ │ └── unbiz │ │ │ └── mysql │ │ │ └── lib │ │ │ ├── AbstractBlockingTransport.java │ │ │ ├── BinlogStreamBlockingTransport.java │ │ │ ├── BinlogStreamBlockingTransportImpl.java │ │ │ ├── BlockingTransport.java │ │ │ ├── CharsetMapping.java │ │ │ ├── ColumnType.java │ │ │ ├── ResultContentReader.java │ │ │ ├── SocketReadTimeoutHanlder.java │ │ │ ├── TextProtocolBlockingTransport.java │ │ │ ├── TextProtocolBlockingTransportImpl.java │ │ │ ├── TransportConfig.java │ │ │ ├── filter │ │ │ ├── TableFilter.java │ │ │ └── impl │ │ │ │ └── BlackWhiteNameListTableFilter.java │ │ │ ├── output │ │ │ ├── BinlogColumnValue.java │ │ │ ├── BinlogResultRow.java │ │ │ ├── ColumnDefinition.java │ │ │ └── RowModifyTypeEnum.java │ │ │ └── protocol │ │ │ ├── AbstractRequest.java │ │ │ ├── AbstractResponse.java │ │ │ ├── CapabilityFlagConst.java │ │ │ ├── ColumnFlagConst.java │ │ │ ├── EOFPacket.java │ │ │ ├── ERRPacket.java │ │ │ ├── OKPacket.java │ │ │ ├── PacketHeader.java │ │ │ ├── Position.java │ │ │ ├── Request.java │ │ │ ├── Response.java │ │ │ ├── binary │ │ │ ├── BitColumnTypeValueParser.java │ │ │ ├── BlobColumnTypeValueParser.java │ │ │ ├── ColumnTypeValueParser.java │ │ │ ├── ColumnTypeValueParserFactory.java │ │ │ ├── DateColumnTypeValueParser.java │ │ │ ├── DateTime2ColumnTypeValueParser.java │ │ │ ├── DateTimeColumnTypeValueParser.java │ │ │ ├── DecimalColumnTypeValueParser.java │ │ │ ├── DoubleColumnTypeValueParser.java │ │ │ ├── EnumColumnTypeValueParser.java │ │ │ ├── FloatColumnTypeValueParser.java │ │ │ ├── IntegerColumnTypeValueParser.java │ │ │ ├── LongColumnTypeValueParser.java │ │ │ ├── NullColumnTypeValueParser.java │ │ │ ├── SetColumnTypeValueParser.java │ │ │ ├── StringColumnTypeValueParser.java │ │ │ ├── Time2ColumnTypeValueParser.java │ │ │ ├── TimeColumnTypeValueParser.java │ │ │ ├── TimeStamp2ColumnTypeValueParser.java │ │ │ ├── TimeStampColumnTypeValueParser.java │ │ │ ├── UnsupportColumnTypeValueParser.java │ │ │ └── YearColumnTypeValueParser.java │ │ │ ├── binlog │ │ │ ├── AbstractBinlogResponse.java │ │ │ ├── AbstractTableMetaProvider.java │ │ │ ├── BinlogContext.java │ │ │ ├── BinlogEvent.java │ │ │ ├── BinlogEventHeader.java │ │ │ ├── BinlogEventType.java │ │ │ ├── BinlogFileBinlogPosition.java │ │ │ ├── DumpRequest.java │ │ │ ├── GTidBinlogPosition.java │ │ │ ├── GTidDumpRequest.java │ │ │ ├── GtId.java │ │ │ ├── GtIdInterval.java │ │ │ ├── GtIdSet.java │ │ │ ├── InternelColumnDefinition.java │ │ │ ├── RegisterRequest.java │ │ │ ├── ShowColumnListCommandTableMetaProvider.java │ │ │ ├── ShowColumnSqlTableMetaProvider.java │ │ │ ├── TableMeta.java │ │ │ ├── TableMetaProvider.java │ │ │ ├── TimestampBinlogPosition.java │ │ │ ├── ValidBinlogOutput.java │ │ │ ├── ValidEventType.java │ │ │ ├── event │ │ │ │ ├── AbstractBinlogEvent.java │ │ │ │ ├── BaseRowEvent.java │ │ │ │ ├── EventFactory.java │ │ │ │ ├── FormatDescriptionEvent.java │ │ │ │ ├── GTidEvent.java │ │ │ │ ├── QueryEvent.java │ │ │ │ ├── RotateEvent.java │ │ │ │ ├── RowEventV0.java │ │ │ │ ├── RowEventV1.java │ │ │ │ ├── RowEventV2.java │ │ │ │ ├── StopEvent.java │ │ │ │ ├── TableMapEvent.java │ │ │ │ ├── UnkonwnEvent.java │ │ │ │ └── XidEvent.java │ │ │ ├── exp │ │ │ │ ├── FetalParseValueExp.java │ │ │ │ ├── InvalidBinlogVer.java │ │ │ │ ├── InvalidColumnType.java │ │ │ │ ├── ReadTimeoutExp.java │ │ │ │ └── TableAlreadyModifyExp.java │ │ │ └── extra │ │ │ │ └── BinlogPosition.java │ │ │ ├── connect │ │ │ ├── HandShakeResponseV41.java │ │ │ └── HandShakeV10.java │ │ │ ├── datautils │ │ │ ├── MysqlNumberUtils.java │ │ │ └── MysqlStringUtils.java │ │ │ ├── text │ │ │ ├── AbstractTextCommandRequest.java │ │ │ ├── ColumnDefinitionResponse.java │ │ │ ├── ColumnValue.java │ │ │ ├── ColumnValueProvider.java │ │ │ ├── FieldListCommandResponse.java │ │ │ ├── PingRequest.java │ │ │ ├── ResultsetRowResponse.java │ │ │ ├── TextColumnValueProvider.java │ │ │ ├── TextCommandFieldListRequest.java │ │ │ ├── TextCommandQueryRequest.java │ │ │ └── TextCommandQueryResponse.java │ │ │ └── tool │ │ │ ├── GTSidTool.java │ │ │ ├── GenericStringTypeChecker.java │ │ │ ├── MysqlDecimal.java │ │ │ ├── PacketTool.java │ │ │ ├── PassSecure.java │ │ │ ├── SafeByteArrayOutputStream.java │ │ │ └── StringTool.java │ │ └── hiriverunbiz │ │ └── mysql │ │ └── lib │ │ └── exp │ │ ├── BaseExecuteException.java │ │ ├── HandShakeException.java │ │ ├── InvalidMysqlDataException.java │ │ ├── NetworkException.java │ │ ├── NotExpectPayloadException.java │ │ ├── PeerResetNetworkException.java │ │ └── UnOpenedSocket.java │ └── test │ └── java │ └── com │ └── hiriver │ └── unbiz │ ├── msyql │ ├── Connection.java │ ├── DumpTestCase.java │ ├── DumpTestCase2.java │ ├── TestCase.java │ └── TestNumberUtils.java │ └── mysql │ └── tool │ └── DecimalTestCase.java └── pom.xml /hiriver-modules/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | ### Eclipse template 3 | 4 | .metadata 5 | bin/ 6 | tmp/ 7 | *.tmp 8 | *.bak 9 | *.swp 10 | *~.nib 11 | local.properties 12 | .settings/ 13 | .loadpath 14 | .recommenders 15 | 16 | # Eclipse Core 17 | .project 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # PyDev specific (Python IDE for Eclipse) 26 | *.pydevproject 27 | 28 | # CDT-specific (C/C++ Development Tooling) 29 | .cproject 30 | 31 | # JDT-specific (Eclipse Java Development Tools) 32 | .classpath 33 | 34 | # Java annotation processor (APT) 35 | .factorypath 36 | 37 | # PDT-specific (PHP Development Tools) 38 | .buildpath 39 | 40 | # sbteclipse plugin 41 | .target 42 | 43 | # Tern plugin 44 | .tern-project 45 | 46 | # TeXlipse plugin 47 | .texlipse 48 | 49 | # STS (Spring Tool Suite) 50 | .springBeans 51 | 52 | # Code Recommenders 53 | .recommenders/ 54 | ### JetBrains template 55 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 56 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 57 | 58 | # User-specific stuff: 59 | .idea/workspace.xml 60 | .idea/tasks.xml 61 | .idea/dictionaries 62 | .idea/vcs.xml 63 | .idea/jsLibraryMappings.xml 64 | 65 | # Sensitive or high-churn files: 66 | .idea/dataSources.ids 67 | .idea/dataSources.xml 68 | .idea/dataSources.local.xml 69 | .idea/sqlDataSources.xml 70 | .idea/dynamic.xml 71 | .idea/uiDesigner.xml 72 | 73 | # Gradle: 74 | .idea/gradle.xml 75 | .idea/libraries 76 | 77 | # Mongo Explorer plugin: 78 | .idea/mongoSettings.xml 79 | 80 | ## File-based project format: 81 | *.iws 82 | 83 | ## Plugin-specific files: 84 | 85 | # IntelliJ 86 | /out/ 87 | 88 | # mpeltonen/sbt-idea plugin 89 | .idea_modules/ 90 | 91 | # JIRA plugin 92 | atlassian-ide-plugin.xml 93 | 94 | # Crashlytics plugin (for Android Studio and IntelliJ) 95 | com_crashlytics_export_strings.xml 96 | crashlytics.properties 97 | crashlytics-build.properties 98 | fabric.properties 99 | ### Maven template 100 | target/ 101 | pom.xml.tag 102 | pom.xml.releaseBackup 103 | pom.xml.versionsBackup 104 | pom.xml.next 105 | release.properties 106 | dependency-reduced-pom.xml 107 | buildNumber.properties 108 | .mvn/timing.properties 109 | 110 | .idea 111 | *.iml 112 | target 113 | lib.tar.gz 114 | 115 | .DS_Store 116 | .ser -------------------------------------------------------------------------------- /hiriver-modules/hiriver-sample/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.hiriver 7 | hiriver-modules 8 | ../ 9 | 1.2.5-SNAPSHOT 10 | 11 | hiriver-sample 12 | war 13 | 14 | hiriver-sample Maven Webapp 15 | http://maven.apache.org 16 | 17 | true 18 | 19 | 20 | 21 | junit 22 | junit 23 | 24 | 25 | org.springframework 26 | spring-beans 27 | 28 | 29 | org.springframework 30 | spring-core 31 | 32 | 33 | org.springframework 34 | spring-context 35 | 36 | 37 | org.springframework 38 | spring-context-support 39 | 40 | 41 | org.springframework 42 | spring-web 43 | 44 | 45 | 46 | ch.qos.logback 47 | logback-core 48 | 49 | 50 | ch.qos.logback 51 | logback-classic 52 | 53 | 54 | org.slf4j 55 | slf4j-api 56 | 57 | 58 | org.codehaus.janino 59 | janino 60 | 61 | 62 | com.hiriver 63 | hiriver 64 | ${project.version} 65 | 66 | 67 | 68 | hiriver-sample 69 | 70 | 71 | org.apache.maven.plugins 72 | maven-compiler-plugin 73 | 74 | 1.7 75 | 1.7 76 | 77 | 78 | 79 | org.mortbay.jetty 80 | jetty-maven-plugin 81 | 8.1.9.v20130131 82 | 83 | 84 | 85 | 8080 86 | 87 | 88 | 9090 89 | a 90 | 10 91 | 92 | / 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver-sample/src/main/java/com/hiriver/sample/SampleConsumer.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.sample; 2 | 3 | import java.util.List; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.stereotype.Component; 8 | 9 | import com.hiriver.channel.BinlogDataSet; 10 | import com.hiriver.channel.stream.Consumer; 11 | import com.hiriver.channel.stream.impl.AbstractConsumer; 12 | import com.hiriver.unbiz.mysql.lib.output.BinlogColumnValue; 13 | import com.hiriver.unbiz.mysql.lib.output.BinlogResultRow; 14 | import com.hiriver.unbiz.mysql.lib.output.RowModifyTypeEnum; 15 | 16 | @Component 17 | public class SampleConsumer extends AbstractConsumer implements Consumer { 18 | private static final Logger LOG = LoggerFactory.getLogger(SampleConsumer.class); 19 | 20 | @Override 21 | protected void consumeRowData(final BinlogDataSet rowData) { 22 | LOG.info("out put data {},gtid is {}, binlog pos is {}", rowData.getRowDataMap().size(), rowData.getGtId(), 23 | rowData.getBinlogPos()); 24 | for (String tb : rowData.getRowDataMap().keySet()) { 25 | LOG.info("=======start table:" + tb + "======="); 26 | List rowList = rowData.getRowDataMap().get(tb); 27 | 28 | int index = 0; 29 | for (BinlogResultRow row : rowList) { 30 | LOG.info("=======start row [{}]=======,{}", index, row.getBinlogOccurTime()); 31 | if (row.getRowModifyType() == RowModifyTypeEnum.INSERT) { 32 | outputRow(row.getAfterColumnValueList()); 33 | } 34 | if (row.getRowModifyType() == RowModifyTypeEnum.DELETE) { 35 | outputRow(row.getBeforeColumnValueList()); 36 | } 37 | if (row.getRowModifyType() == RowModifyTypeEnum.UPDATE) { 38 | outputRow(row.getBeforeColumnValueList()); 39 | outputRow(row.getAfterColumnValueList()); 40 | } 41 | LOG.info("=======end row [" + (index) + "]======="); 42 | index++; 43 | } 44 | LOG.info("=======end table:" + tb + "======="); 45 | } 46 | } 47 | 48 | private void outputRow(List rowValues) { 49 | for (BinlogColumnValue val : rowValues) { 50 | LOG.info(val.toString()); 51 | } 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver-sample/src/main/resources/hiriver-sample.properties: -------------------------------------------------------------------------------- 1 | # 数据库相关属性 2 | user_name=root 3 | password= 4 | hostUrl=localhost:3306 5 | # 65535内任意数字,表示hiriver在复制体系中的id 6 | serverId=3333 7 | # 支持master/slave模式,开发时可以同hostUrl 8 | slaveHostUrl=localhost:3306 9 | 10 | # 数据库连接超时 11 | connectTimeout=10000 12 | # 数据库读写数据超时,参加socket 13 | soTimeout=120000 14 | 15 | # 当与mysql失去连接后,线程sleep的时间,超过该时间后再进行重连 16 | faultTolerantTimeout=5000 17 | # 当发生致命错误时下次重试的间隔时间 18 | fetalWaitTimeout=120000 19 | 20 | # 白名单,通过表名过滤,支持正则,表名必须是 库名.表名格式 21 | table.white=demo.* 22 | # 黑名单,通过表明过滤,支持正则,表名必须是 库名.表名格式 23 | table.black= 24 | 25 | # 初始同步点 26 | channel.0000.id=channel_0000 27 | 28 | channel.0000.gtid=044b0d88-5215-11e4-9f6f-4fa13c48b4ad:9015604 29 | 30 | channel.0000.binlog=mysql-bin.000007 31 | channel.0000.binlog.pos=191 32 | 33 | # 同步点存储路径 34 | position.store.path=/Users/hexiufeng/misc/hiriver 35 | 36 | # 内存缓存的大小 37 | channel.buffer.limit=5000 38 | 39 | channel.0000.timestamp=1540186271 40 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver-sample/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver-sample/src/main/resources/spring/spring-binlog.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver-sample/src/main/resources/spring/spring-gtid.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver-sample/src/main/resources/spring/spring-root.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 12 | 14 | 15 | 16 | 18 | 19 | 20 | classpath:*.properties 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver-sample/src/main/resources/spring/spring-timestamp.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver-sample/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | hiriver-sample 8 | 9 | 10 | contextConfigLocation 11 | classpath:spring/spring-root.xml 12 | 13 | 14 | 15 | org.springframework.web.context.ContextLoaderListener 16 | 17 | 18 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.hiriver 8 | hiriver-modules 9 | ../ 10 | 1.2.5-SNAPSHOT 11 | 12 | hiriver 13 | hiriver 14 | 15 | http://maven.apache.org 16 | 17 | 18 | junit 19 | junit 20 | 21 | 22 | org.apache.commons 23 | commons-lang3 24 | 25 | 26 | commons-io 27 | commons-io 28 | 29 | 30 | 31 | joda-time 32 | joda-time 33 | 34 | 35 | 36 | 37 | org.slf4j 38 | slf4j-api 39 | 40 | 41 | org.codehaus.janino 42 | janino 43 | 44 | 45 | com.hiriver 46 | mysql-proto 47 | ${project.version} 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | org.apache.maven.plugins 56 | maven-compiler-plugin 57 | 58 | 1.7 59 | 1.7 60 | 61 | 62 | 63 | org.apache.maven.plugins 64 | maven-source-plugin 65 | 66 | 67 | attach-sources 68 | 69 | jar 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/main/java/com/hiriver/channel/stream/BinlogPositionStoreTrigger.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.channel.stream; 2 | 3 | /** 4 | * 用于触发记录同步点的触发器,用于使用了{@link ChannelBuffer}提高性能,需要消费者触发何时来 5 | * 记录同步点,但记录同步点的具体操作不应该暴露给消费者,因此使用回调模式处理,BinlogPositionStoreTrigger 6 | * 就是回调的描述 7 | * 8 | * @author hexiufeng 9 | * 10 | */ 11 | public interface BinlogPositionStoreTrigger { 12 | /** 13 | * 记录同步点操作 14 | */ 15 | void triggerStoreBinlogPos(); 16 | } 17 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/main/java/com/hiriver/channel/stream/BufferableBinlogDataSet.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.channel.stream; 2 | 3 | import com.hiriver.channel.BinlogDataSet; 4 | 5 | /** 6 | * 内部使用的接口,描述可以被发送到Queue缓冲的{@link com.hiriver.channel.BinlogDataSet}数据, 7 | * 用于封装{@link com.hiriver.channel.BinlogDataSet},主要用于区分具体的binlog数据和事务结束 8 | * 信号数据。 9 | * 10 | * @author hexiufeng 11 | * @see {@link com.hiriver.channel.BinlogDataSet} 12 | * 13 | */ 14 | public interface BufferableBinlogDataSet { 15 | /** 16 | * 获取 BinlogDataSet数据 17 | * 18 | * @return BinlogDataSet 数据 19 | */ 20 | BinlogDataSet getBinlogDataSet(); 21 | } 22 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/main/java/com/hiriver/channel/stream/ChannelBuffer.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.channel.stream; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | /** 6 | * 为提高性能用于缓冲binlog事件数据的缓冲区,用于解耦binlog接收线程和数据消费线程,线程安全 7 | * 8 | * @author hexiufeng 9 | * 10 | */ 11 | public interface ChannelBuffer { 12 | /** 13 | * 发送数据,支持超时 14 | * 15 | * @param ds BufferableBinlogDataSet数据 16 | * @param timeout 预期超时 17 | * @param timeUnit 超时单位 18 | * @return 是否发送成功 19 | */ 20 | boolean push(BufferableBinlogDataSet ds,long timeout,TimeUnit timeUnit); 21 | /** 22 | * 从缓冲区中读取数据,先进先出。支持超时 23 | * 24 | * @param timeout 预期超时 25 | * @param timeUnit 超时单位 26 | * @return 读取到的数据,当缓冲区为空时,返回null 27 | */ 28 | BufferableBinlogDataSet pop(long timeout,TimeUnit timeUnit); 29 | } 30 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/main/java/com/hiriver/channel/stream/ChannelStream.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.channel.stream; 2 | 3 | /** 4 | * 描述从一个mysql数据源接收数据并被消费者消费的一个整体的流程。 5 | * 6 | * @author hexiufeng 7 | * 8 | */ 9 | public interface ChannelStream { 10 | /** 11 | * 开启数据接收 12 | */ 13 | void start(); 14 | /** 15 | * 是否资源 16 | */ 17 | void release(); 18 | } 19 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/main/java/com/hiriver/channel/stream/Consumer.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.channel.stream; 2 | 3 | import com.hiriver.channel.BinlogDataSet; 4 | 5 | /** 6 | * 业务方消费binlog数据的抽象描述. 7 | * 强烈推荐业务方式继承{@link com.hiriver.channel.stream.impl.AbstractConsumer} 8 | * 9 | * 10 | * @author hexiufeng 11 | * 12 | */ 13 | public interface Consumer { 14 | 15 | /** 16 | * 17 | * 描述消费binlog数据的接口。 18 | * 19 | * 注意:
20 | *
    21 | *
  • 要在本方法的实现中处理所有异常。如果没有处理,内部消费者线程会被中断。
  • 22 | *
  • 消费数据失败,需要在本方法中自行处理,比如记录日之后,跳过本条数据
  • 23 | *
24 | * 25 | * @param ds 一般是从binlog中解析的数据,也可能是空数据,用于标示需要记录同步点标示数据, 通过{@link BinlogDataSet#getIsPositionStoreTrigger()}=true来识别 26 | * @param storeTrigger 记录同步点的触发器,当{@link BinlogDataSet#getIsPositionStoreTrigger()}=true被调用 27 | */ 28 | void consume(final BinlogDataSet ds, final BinlogPositionStoreTrigger storeTrigger); 29 | } 30 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/main/java/com/hiriver/channel/stream/TransactionRecognizer.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.channel.stream; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.ValidBinlogOutput; 4 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.extra.BinlogPosition; 5 | 6 | /** 7 | * 事务信息的识别器描述 8 | * 9 | * @author hexiufeng 10 | * 11 | */ 12 | public interface TransactionRecognizer { 13 | /** 14 | * 当前接收的事件是否是事务的开始事件。Query event并且执行的sql是BEGIN则表示该事件是事务开始事件 15 | * 16 | * @param validOutput 接收的事件 17 | * @return 是否是事务开始事件 18 | */ 19 | boolean isStart(ValidBinlogOutput validOutput); 20 | /** 21 | * 是否是事务结束事件,当接收到的事件是Xid event时,表示它是事务结束事件 22 | * 23 | * @param validOutput 接收的事件 24 | * @return 是否是事务结束事件 25 | */ 26 | boolean isEnd(ValidBinlogOutput validOutput); 27 | /** 28 | * 接收的事件是否可以提前到事务的同步点 29 | * 30 | * @param validOutput 接收的事件 31 | * @return 是否是事务的同步点 32 | */ 33 | boolean tryRecognizePos(ValidBinlogOutput validOutput); 34 | /** 35 | * 获取当前的事务开始的同步点 36 | * 37 | * @return 同步点 38 | */ 39 | BinlogPosition getCurrentTransBeginPos(); 40 | 41 | /** 42 | * 读取当前事务的gtid,如果mysql不支持gtid,则返回null 43 | * 44 | * @return 当前事务的gtid 45 | */ 46 | String getGTId(); 47 | 48 | /** 49 | * 读取事务的binlog pos 50 | * 51 | * @return 52 | */ 53 | String getTransBinlogPos(); 54 | } 55 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/main/java/com/hiriver/channel/stream/impl/AbstractConsumer.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.channel.stream.impl; 2 | 3 | import com.hiriver.channel.BinlogDataSet; 4 | import com.hiriver.channel.stream.BinlogPositionStoreTrigger; 5 | import com.hiriver.channel.stream.Consumer; 6 | 7 | /** 8 | * {@link com.hiriver.channel.stream.impl.Consumer}抽象实现,仅仅适用于简单测试时继承本类实现,在实际的业务中强烈建议业务方直接实现 9 | * {@link Consumer}接口。 10 | * 11 | * @author hexiufeng 12 | * 13 | */ 14 | public abstract class AbstractConsumer implements Consumer { 15 | 16 | @Override 17 | public final void consume(final BinlogDataSet ds, final BinlogPositionStoreTrigger storeTrigger) { 18 | if(ds.getIsPositionStoreTrigger()){ 19 | storeTrigger.triggerStoreBinlogPos(); 20 | return; 21 | } 22 | if(ds.isStartTransEvent()){ 23 | return; 24 | } 25 | consumeRowData(ds); 26 | } 27 | /** 28 | * 消费使用binlog数据的抽象方法,由业务方实现.
29 | * 30 | * 注意:
31 | *
    32 | *
  • 要在本方法的实现中处理所有异常。如果没有处理,内部消费者线程会被中断。
  • 33 | *
  • 消费数据失败,需要在本方法中自行处理,比如记录日之后,跳过本条数据
  • 34 | *
35 | * 36 | * @param rowData 从binlog识别出来的数据 37 | */ 38 | protected abstract void consumeRowData(final BinlogDataSet rowData); 39 | } 40 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/main/java/com/hiriver/channel/stream/impl/AbstractTransactionRecognizer.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.channel.stream.impl; 2 | 3 | import com.hiriver.channel.stream.TransactionRecognizer; 4 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.ValidBinlogOutput; 5 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.ValidEventType; 6 | 7 | /** 8 | * {@link TransactionRecognizer} 的抽象实现 9 | * 10 | * @author hexiufeng 11 | * 12 | */ 13 | public abstract class AbstractTransactionRecognizer implements TransactionRecognizer { 14 | protected String transBinlogPos; 15 | @Override 16 | public boolean isStart(ValidBinlogOutput validOutput) { 17 | if (validOutput.getEventType() == ValidEventType.TRAN_BEGIN) { 18 | return true; 19 | } 20 | return false; 21 | } 22 | 23 | @Override 24 | public boolean isEnd(ValidBinlogOutput validOutput) { 25 | if (validOutput.getEventType() == ValidEventType.TRANS_COMMIT) { 26 | return true; 27 | } 28 | return false; 29 | } 30 | 31 | @Override 32 | public String getGTId() { 33 | return null; 34 | } 35 | 36 | @Override 37 | public String getTransBinlogPos() { 38 | return transBinlogPos; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/main/java/com/hiriver/channel/stream/impl/BinlogNameAndPosTransactionRecognizer.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.channel.stream.impl; 2 | 3 | import com.hiriver.channel.stream.TransactionRecognizer; 4 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.BinlogFileBinlogPosition; 5 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.ValidBinlogOutput; 6 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.extra.BinlogPosition; 7 | 8 | /** 9 | * binlog file + pos方式的事务识别器实现 10 | * 11 | * @author hexiufeng 12 | * 13 | */ 14 | public class BinlogNameAndPosTransactionRecognizer extends AbstractTransactionRecognizer 15 | implements TransactionRecognizer { 16 | private BinlogFileBinlogPosition position; 17 | 18 | @Override 19 | public boolean isStart(ValidBinlogOutput validOutput) { 20 | boolean start = super.isStart(validOutput); 21 | if (start) { 22 | position = new BinlogFileBinlogPosition(validOutput.getBinlogFileName(), 23 | validOutput.getEvent().getBinlogEventPos()); 24 | super.transBinlogPos = validOutput.getEventBinlogPos(); 25 | } 26 | return start; 27 | } 28 | 29 | @Override 30 | public boolean isEnd(ValidBinlogOutput validOutput) { 31 | boolean isEnd = super.isEnd(validOutput); 32 | if (isEnd) { 33 | position = new BinlogFileBinlogPosition(validOutput.getBinlogFileName(), 34 | validOutput.getEvent().getBinlogEventPos()); 35 | super.transBinlogPos = validOutput.getEventBinlogPos(); 36 | } 37 | return isEnd; 38 | } 39 | 40 | @Override 41 | public boolean tryRecognizePos(ValidBinlogOutput validOutput) { 42 | return false; 43 | } 44 | 45 | @Override 46 | public BinlogPosition getCurrentTransBeginPos() { 47 | return position; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/main/java/com/hiriver/channel/stream/impl/ChannelStreamContext.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.channel.stream.impl; 2 | 3 | import java.util.concurrent.CountDownLatch; 4 | 5 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.extra.BinlogPosition; 6 | 7 | /** 8 | * 在一个{@link com.hiriver.channel.stream.ChannelStream}过程中一些通用信息 9 | * 的存储 10 | * 11 | * @author hexiufeng 12 | * 13 | */ 14 | final class ChannelStreamContext { 15 | /** 16 | * 当与mysql链接断开后重连时的同步点位置,之所以不能直接使用{@link com.hiriver.position.store.BinlogPositionStore} 17 | * 的位置是因为{@link com.hiriver.channel.stream.ChannelBuffer}的存在,如果使用了,ChannelBuffer内的数据会被重新发送 18 | */ 19 | private BinlogPosition nextPos; 20 | /** 21 | * 从mysql接收binlog线程关闭完成通知。线程会判断shutDownTrigger == true 22 | */ 23 | final CountDownLatch shutDownProviderEvent = new CountDownLatch(1); 24 | /** 25 | * 消费线程关闭完成通知。线程会判断shutDownTrigger == true 26 | */ 27 | final CountDownLatch shutDownConsumerEvent = new CountDownLatch(1); 28 | /** 29 | * {@link com.hiriver.channel.stream.ChannelStream}关闭时通知binlog接收线程和消费线程 30 | * 关闭的信号 31 | */ 32 | volatile boolean shutDownTrigger = false; 33 | 34 | public BinlogPosition getNextPos() { 35 | return nextPos; 36 | } 37 | 38 | public void setNextPos(BinlogPosition currentPos) { 39 | this.nextPos = currentPos; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/main/java/com/hiriver/channel/stream/impl/DefaultBufferableBinlogDataSet.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.channel.stream.impl; 2 | 3 | import com.hiriver.channel.BinlogDataSet; 4 | import com.hiriver.channel.stream.BufferableBinlogDataSet; 5 | 6 | /** 7 | * 封装具体的来自Binlog Row event数据的实现 8 | * 9 | * @author hexiufeng 10 | * 11 | */ 12 | final class DefaultBufferableBinlogDataSet implements BufferableBinlogDataSet { 13 | private final BinlogDataSet ds; 14 | DefaultBufferableBinlogDataSet(BinlogDataSet ds){ 15 | this.ds = ds; 16 | } 17 | @Override 18 | public BinlogDataSet getBinlogDataSet() { 19 | return ds; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/main/java/com/hiriver/channel/stream/impl/DefaultChannelBuffer.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.channel.stream.impl; 2 | 3 | import java.util.concurrent.BlockingQueue; 4 | import java.util.concurrent.LinkedBlockingQueue; 5 | import java.util.concurrent.TimeUnit; 6 | 7 | import javax.annotation.PostConstruct; 8 | 9 | import com.hiriver.channel.stream.BufferableBinlogDataSet; 10 | import com.hiriver.channel.stream.ChannelBuffer; 11 | 12 | /** 13 | * 缺省的 {@link ChannelBuffer}实现,内部使用{@link LinkedBlockingQueue}实现,支持设定上限。 14 | * ChannelBuffer必须设定上限,否则会打爆内存,缺省上限是5000. 15 | * 16 | * @author hexiufeng 17 | * 18 | */ 19 | public class DefaultChannelBuffer implements ChannelBuffer { 20 | private int limit = 5000; 21 | private BlockingQueue queue = new LinkedBlockingQueue<>(limit) ; 22 | 23 | public int getLimit() { 24 | return limit; 25 | } 26 | public void setLimit(int limit) { 27 | this.limit = limit; 28 | } 29 | @PostConstruct 30 | public void init(){ 31 | queue = new LinkedBlockingQueue<>(limit); 32 | } 33 | @Override 34 | public boolean push(BufferableBinlogDataSet ds,long timeout, TimeUnit timeUnit) { 35 | try { 36 | return queue.offer(ds, timeout, timeUnit); 37 | } catch (InterruptedException e) { 38 | // ignore 39 | return false; 40 | } 41 | } 42 | 43 | @Override 44 | public BufferableBinlogDataSet pop(long timeout, TimeUnit timeUnit) { 45 | try { 46 | return queue.poll(timeout, timeUnit); 47 | } catch (InterruptedException e) { 48 | // ignore 49 | return null; 50 | } 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/main/java/com/hiriver/channel/stream/impl/GTIDTransactionRecognizer.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.channel.stream.impl; 2 | 3 | import java.util.Map; 4 | 5 | import com.hiriver.channel.stream.TransactionRecognizer; 6 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.GTidBinlogPosition; 7 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.GtId; 8 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.ValidBinlogOutput; 9 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.ValidEventType; 10 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.event.GTidEvent; 11 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.extra.BinlogPosition; 12 | 13 | /** 14 | * gtid方式的事务识别器。gtid方式下在事务开启后的第一事件就是GTID事件,同时GTID的不同标示者着上一个事务的结束。 但我们并不使用该特性来判断事务是否结束 15 | * 16 | * @author hexiufeng 17 | * 18 | */ 19 | public class GTIDTransactionRecognizer extends AbstractTransactionRecognizer implements TransactionRecognizer { 20 | private String gtIdString; 21 | private Map gtIdMap; 22 | 23 | @Override 24 | public boolean tryRecognizePos(ValidBinlogOutput validOutput) { 25 | String newGtId = getNewGtId(validOutput); 26 | if (newGtId != null) { 27 | gtIdString = newGtId; 28 | super.transBinlogPos = validOutput.getEventBinlogPos(); 29 | applyGtIdString(newGtId); 30 | return true; 31 | } 32 | return false; 33 | } 34 | 35 | @Override 36 | public BinlogPosition getCurrentTransBeginPos() { 37 | if (gtIdString == null) { 38 | return null; 39 | } 40 | return getFullGtIdString(); 41 | } 42 | 43 | @Override 44 | public String getGTId() { 45 | return gtIdString; 46 | } 47 | 48 | private GTidBinlogPosition getFullGtIdString() { 49 | StringBuilder sb = new StringBuilder(); 50 | for (String uuid : gtIdMap.keySet()) { 51 | sb.append(gtIdMap.get(uuid).cloneGtId().toString()); 52 | sb.append(","); 53 | } 54 | return new GTidBinlogPosition(sb.substring(0, sb.length() - 1)); 55 | } 56 | 57 | private void applyGtIdString(String gtid) { 58 | GtId gi = new GtId(gtid); 59 | if(gtIdMap.containsKey(gi.getUuid())){ 60 | gtIdMap.remove(gi.getUuid()); 61 | } 62 | gtIdMap.put(gi.getUuid(), gi); 63 | } 64 | 65 | public final void useInitPos(GTidBinlogPosition pos) { 66 | gtIdMap = pos.getGtidset().cloneGtIdMap(); 67 | } 68 | 69 | private String getNewGtId(ValidBinlogOutput validOutput) { 70 | if (validOutput.getEventType() == ValidEventType.GTID) { 71 | GTidEvent gtEvent = (GTidEvent) (validOutput.getEvent()); 72 | return gtEvent.getGTidString(); 73 | } 74 | return null; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/main/java/com/hiriver/channel/stream/impl/PersistPosBufferableBinlogDataSet.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.channel.stream.impl; 2 | 3 | import com.hiriver.channel.BinlogDataSet; 4 | import com.hiriver.channel.stream.BufferableBinlogDataSet; 5 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.extra.BinlogPosition; 6 | 7 | /** 8 | * 封装通知记录同步点数据的{@link BufferableBinlogDataSet}实现,内部记录同步点信息,方便持久化。 9 | * 10 | * @author hexiufeng 11 | * 12 | */ 13 | final class PersistPosBufferableBinlogDataSet implements BufferableBinlogDataSet { 14 | private final BinlogDataSet ds; 15 | private final BinlogPosition pos; 16 | 17 | PersistPosBufferableBinlogDataSet(BinlogDataSet ds,BinlogPosition pos){ 18 | this.ds = ds; 19 | this.pos = pos; 20 | } 21 | @Override 22 | public BinlogDataSet getBinlogDataSet() { 23 | return ds; 24 | } 25 | public BinlogPosition getPos() { 26 | return pos; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/main/java/com/hiriver/channel/stream/impl/TimestampTransactionRecognizer.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.channel.stream.impl; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.TimestampBinlogPosition; 4 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.ValidBinlogOutput; 5 | 6 | /** 7 | * created by Yang Huawei (xander.yhw@alibaba-inc.com) on 2018/8/29 23:01 8 | */ 9 | public class TimestampTransactionRecognizer extends AbstractTransactionRecognizer { 10 | 11 | private TimestampBinlogPosition position; 12 | 13 | @Override 14 | public boolean isStart(ValidBinlogOutput validOutput) { 15 | boolean start = super.isStart(validOutput); 16 | if (start) { 17 | position = new TimestampBinlogPosition(validOutput.getEvent().getOccurTime(), 18 | validOutput.getServerUuid(), validOutput.getBinlogFileName(), 19 | validOutput.getEvent().getBinlogEventPos()); 20 | super.transBinlogPos = validOutput.getEventBinlogPos(); 21 | } 22 | return start; 23 | } 24 | 25 | @Override 26 | public boolean isEnd(ValidBinlogOutput validOutput) { 27 | boolean isEnd = super.isEnd(validOutput); 28 | if (isEnd) { 29 | position = new TimestampBinlogPosition(validOutput.getEvent().getOccurTime(), 30 | validOutput.getServerUuid(), validOutput.getBinlogFileName(), 31 | validOutput.getEvent().getBinlogEventPos()); 32 | super.transBinlogPos = validOutput.getEventBinlogPos(); 33 | } 34 | return isEnd; 35 | } 36 | 37 | @Override 38 | public boolean tryRecognizePos(ValidBinlogOutput validOutput) { 39 | return false; 40 | } 41 | 42 | @Override 43 | public TimestampBinlogPosition getCurrentTransBeginPos() { 44 | return position; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/main/java/com/hiriver/position/store/BinlogPositionStore.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.position.store; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.extra.BinlogPosition; 4 | 5 | /** 6 | * 同步点存储抽象描述 7 | * 8 | * @author hexiufeng 9 | * 10 | */ 11 | public interface BinlogPositionStore { 12 | /** 13 | * 存储同步点 14 | * @param binlogPosition 同步点 15 | * @param channelId 同步点所属的数据流 16 | */ 17 | void store(BinlogPosition binlogPosition, String channelId); 18 | 19 | /** 20 | * 加载指定数据流的同步点 21 | * 22 | * @param channelId 指定的数据流 23 | * @return 同步点 24 | */ 25 | BinlogPosition load(String channelId); 26 | } 27 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/main/java/com/hiriver/position/store/impl/AbstractBinlogPositionStore.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.position.store.impl; 2 | 3 | import com.hiriver.position.store.BinlogPositionStore; 4 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.BinlogFileBinlogPosition; 5 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.GTidBinlogPosition; 6 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.TimestampBinlogPosition; 7 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.extra.BinlogPosition; 8 | import org.apache.commons.lang3.StringUtils; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | /** 13 | * 抽象的存储的同步点实现 14 | * 15 | * @author hexiufeng 16 | * 17 | */ 18 | public abstract class AbstractBinlogPositionStore implements BinlogPositionStore { 19 | private static final Logger LOG = LoggerFactory.getLogger(AbstractBinlogPositionStore.class); 20 | 21 | @Override 22 | public void store(BinlogPosition binlogPosition, String channelId) { 23 | storeImpl(binlogPosition.toBytesArray(), channelId); 24 | } 25 | 26 | @Override 27 | public BinlogPosition load(String channelId) { 28 | byte[] posBuf = loadImpl(channelId); 29 | if (posBuf == null) { 30 | LOG.info("can't load binlog pos from store in {}", channelId); 31 | return null; 32 | } 33 | String line = new String(posBuf); 34 | line = line.trim(); 35 | 36 | if(line.isEmpty()){ 37 | return null; 38 | } 39 | 40 | try{ 41 | return new GTidBinlogPosition(line); 42 | }catch(RuntimeException e){ 43 | LOG.info("loaded binlog pos [{}] from store may be binlog name+pos in {},",line, channelId); 44 | } 45 | 46 | String[] array = line.split(":"); 47 | if (array.length == 2) { 48 | return new BinlogFileBinlogPosition(array[0], Long.parseLong(array[1])); 49 | 50 | }else if (array.length==4){ 51 | return new TimestampBinlogPosition(Long.parseLong(array[0]), emptyToNull(array[1]), 52 | emptyToNull(array[2]), 53 | StringUtils.equals(array[3], "") ? null : Long.parseLong(array[3])); 54 | }else if (array.length==1){ 55 | return new TimestampBinlogPosition(Long.parseLong(array[0])); 56 | }else{ 57 | LOG.info("loaded binlog pos [{}] from store is incorrect in {},",line, channelId); 58 | return null; 59 | } 60 | 61 | } 62 | 63 | private String emptyToNull(String string) { 64 | if (StringUtils.equals(string, "")) { 65 | return null; 66 | } 67 | return string; 68 | } 69 | 70 | /** 71 | * 存储同步点 72 | * 73 | * @param posBuf 二进制化的同步点 74 | * @param channelId 指定的数据流 75 | */ 76 | protected abstract void storeImpl(byte[] posBuf, String channelId); 77 | 78 | /** 79 | * 加载二进制化的同步点 80 | * 81 | * @param channelId 指定的数据流 82 | * @return 同步点 83 | */ 84 | protected abstract byte[] loadImpl(String channelId); 85 | } 86 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/main/java/com/hiriver/position/store/impl/FileBinlogPositionStore.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.position.store.impl; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.FileNotFoundException; 6 | import java.io.FileOutputStream; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.io.OutputStream; 10 | 11 | import org.apache.commons.io.IOUtils; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import com.hiriver.position.store.BinlogPositionStore; 16 | 17 | /** 18 | * 基于文件的同步点存储实现 19 | * 20 | * @author hexiufeng 21 | * 22 | */ 23 | public class FileBinlogPositionStore extends AbstractBinlogPositionStore implements BinlogPositionStore { 24 | private static final Logger LOG = LoggerFactory.getLogger(FileBinlogPositionStore.class); 25 | private static final int MAX_BUFFER = 2048; 26 | private String filePath; 27 | 28 | public String getFilePath() { 29 | return filePath; 30 | } 31 | 32 | public void setFilePath(String filePath) { 33 | this.filePath = filePath; 34 | } 35 | 36 | private String getFileName(String channelId) { 37 | return filePath + "/" + channelId; 38 | } 39 | 40 | @Override 41 | protected void storeImpl(byte[] posBuf, String channelId) { 42 | OutputStream output = null; 43 | try { 44 | output = new FileOutputStream(getFileName(channelId)); 45 | IOUtils.write(posBuf, output); 46 | } catch (FileNotFoundException e) { 47 | LOG.error("store binlog pos error " + channelId, e); 48 | } catch (IOException e) { 49 | LOG.error("store binlog pos error " + channelId, e); 50 | } finally { 51 | if (output != null) { 52 | IOUtils.closeQuietly(output); 53 | } 54 | } 55 | 56 | } 57 | 58 | @Override 59 | protected byte[] loadImpl(String channelId) { 60 | byte[] buffer = new byte[MAX_BUFFER]; 61 | 62 | InputStream input = null; 63 | File file = new File(getFileName(channelId)); 64 | if (!file.exists()) { 65 | return null; 66 | } 67 | try { 68 | 69 | input = new FileInputStream(file); 70 | int len = IOUtils.read(input, buffer, 0, MAX_BUFFER); 71 | byte[] posBuf = new byte[len]; 72 | System.arraycopy(buffer, 0, posBuf, 0, len); 73 | return posBuf; 74 | } catch (FileNotFoundException e) { 75 | LOG.error("read binlog pos error " + channelId, e); 76 | } catch (IOException e) { 77 | LOG.error("read binlog pos error " + channelId, e); 78 | } 79 | return null; 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/main/java/com/hiriver/streamsource/DbHostInfo.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.streamsource; 2 | 3 | /** 4 | *
 5 |  * 数据库连接信息
 6 |  * 
7 | * created by Yang Huawei (xander.yhw@alibaba-inc.com) on 2018/9/3 18:05 8 | */ 9 | public interface DbHostInfo { 10 | 11 | String getHostUrl(); 12 | 13 | String getUserName(); 14 | 15 | String getPassword(); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/main/java/com/hiriver/streamsource/DbHostInfoSupplier.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.streamsource; 2 | 3 | /** 4 | * created by Yang Huawei (xander.yhw@alibaba-inc.com) on 2018/9/3 18:07 5 | */ 6 | public interface DbHostInfoSupplier { 7 | 8 | /** 9 | *
10 |      * 提供可用的数据库源信息。
11 |      * 如有多个数据源,不推荐多个候选数据源间随机选择,较佳的方式是按照优先级探活;
12 |      * 
13 | * 14 | * @return 可用的数据源 15 | */ 16 | DbHostInfo available(); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/main/java/com/hiriver/streamsource/StreamSource.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.streamsource; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.ValidBinlogOutput; 4 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.exp.ReadTimeoutExp; 5 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.extra.BinlogPosition; 6 | 7 | /** 8 | * mysql binlog 数据流源抽象描述。有两种实现:
9 | *
    10 | *
  • 纯粹的物理数据源实现
  • 11 | *
  • 抽象的基于多个物理数据的HA实现,它可以按照指定的策略自由使用其中的任何数据源
  • 12 | *
13 | * 14 | * 15 | * @author hexiufeng 16 | * 17 | */ 18 | public interface StreamSource { 19 | /** 20 | * 打开数据,并从指定的同步点同步数据 21 | * 22 | * @param binlogPos 同步点 23 | */ 24 | void openStream(BinlogPosition binlogPos); 25 | /** 26 | * 读取有效的事件信息 27 | * 28 | * @return 事件信息,可能为空 29 | * @throws ReadTimeoutExp 可能会读取超时 30 | */ 31 | ValidBinlogOutput readValidInfo() throws ReadTimeoutExp; 32 | /** 33 | * 当前数据源的host:port 34 | * 35 | * @return 36 | */ 37 | String getHostUrl(); 38 | /** 39 | * 资源释放 40 | */ 41 | void release(); 42 | /** 43 | * 是否已经打开 44 | * 45 | * @return 是否打开 46 | */ 47 | boolean isOpen(); 48 | } 49 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/main/java/com/hiriver/streamsource/StreamSourceSelector.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.streamsource; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * 在HA数据源场景下,选择数据源的策略 7 | * 8 | * @author hexiufeng 9 | * 10 | */ 11 | public interface StreamSourceSelector { 12 | /** 13 | * 从备选数据源中选定一个数据源 14 | * 15 | * @param haStreamSourceList 备选数据源 16 | * @return 选定的数据源 17 | */ 18 | StreamSource select(final List haStreamSourceList); 19 | } 20 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/main/java/com/hiriver/streamsource/impl/AbstractStreamSource.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.streamsource.impl; 2 | 3 | import com.hiriver.streamsource.DbHostInfo; 4 | import com.hiriver.streamsource.StreamSource; 5 | import com.hiriver.unbiz.mysql.lib.TransportConfig; 6 | import com.hiriver.unbiz.mysql.lib.filter.TableFilter; 7 | 8 | /** 9 | * 抽象的数据源实现 10 | * 11 | * @author hexiufeng 12 | * 13 | */ 14 | public abstract class AbstractStreamSource implements StreamSource, DbHostInfo { 15 | /** 16 | * 通信属性 17 | */ 18 | private TransportConfig transportConfig = new TransportConfig(); 19 | private String userName; 20 | private String password; 21 | private String hostUrl; 22 | /** 23 | * 从库id,hiriver是从库 24 | */ 25 | private int serverId; 26 | private int maxMaxPacketSize = 0; 27 | 28 | /** 29 | * 表过滤规则 30 | */ 31 | private TableFilter tableFilter; 32 | 33 | public TransportConfig getTransportConfig() { 34 | return transportConfig; 35 | } 36 | public void setTransportConfig(TransportConfig transportConfig) { 37 | this.transportConfig = transportConfig; 38 | } 39 | 40 | @Override 41 | public String getUserName() { 42 | return userName; 43 | } 44 | public void setUserName(String userName) { 45 | this.userName = userName; 46 | } 47 | @Override 48 | public String getPassword() { 49 | return password; 50 | } 51 | public void setPassword(String password) { 52 | this.password = password; 53 | } 54 | @Override 55 | public String getHostUrl() { 56 | return hostUrl; 57 | } 58 | public void setHostUrl(String hostUrl) { 59 | this.hostUrl = hostUrl; 60 | } 61 | public int getServerId() { 62 | return serverId; 63 | } 64 | public void setServerId(int serverId) { 65 | this.serverId = serverId; 66 | } 67 | 68 | public TableFilter getTableFilter() { 69 | return tableFilter; 70 | } 71 | public int getMaxMaxPacketSize() { 72 | return maxMaxPacketSize; 73 | } 74 | public void setMaxMaxPacketSize(int maxMaxPacketSize) { 75 | this.maxMaxPacketSize = maxMaxPacketSize; 76 | } 77 | public void setTableFilter(TableFilter tableFilter) { 78 | this.tableFilter = tableFilter; 79 | } 80 | 81 | 82 | 83 | } 84 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/main/java/com/hiriver/streamsource/impl/MysqlStreamSource.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.streamsource.impl; 2 | 3 | import com.hiriver.streamsource.StreamSource; 4 | import com.hiriver.unbiz.mysql.lib.BinlogStreamBlockingTransport; 5 | import com.hiriver.unbiz.mysql.lib.BinlogStreamBlockingTransportImpl; 6 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.ValidBinlogOutput; 7 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.exp.ReadTimeoutExp; 8 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.extra.BinlogPosition; 9 | 10 | /** 11 | * 针对物理mysql数据源的{@link StreamSource}实现。内部委托给 12 | * {@link BinlogStreamBlockingTransportImpl}实现 13 | * 14 | * @author hexiufeng 15 | * 16 | */ 17 | public class MysqlStreamSource extends AbstractStreamSource implements StreamSource { 18 | private BinlogStreamBlockingTransport transport; 19 | private boolean opened = false; 20 | 21 | 22 | /** 23 | * 初始化BinlogStreamBlockingTransportImpl 24 | */ 25 | private void initTransport() { 26 | release(); 27 | String[] array = super.getHostUrl().split(":"); 28 | int port = Integer.parseInt(array[1]); 29 | BinlogStreamBlockingTransportImpl t = new BinlogStreamBlockingTransportImpl(array[0],port,super.getUserName(),super.getPassword()); 30 | t.setServerId(super.getServerId()); 31 | t.setTransportConfig(super.getTransportConfig()); 32 | t.setTableFilter(getTableFilter()); 33 | t.setMaxMaxPacketSize(super.getMaxMaxPacketSize()); 34 | transport = t; 35 | } 36 | 37 | @Override 38 | public void openStream(BinlogPosition binlogPos) { 39 | if(opened){ 40 | return ; 41 | } 42 | initTransport(); 43 | transport.dump(binlogPos); 44 | opened = true; 45 | } 46 | 47 | @Override 48 | public ValidBinlogOutput readValidInfo() throws ReadTimeoutExp { 49 | return transport.getBinlogOutputImmediately(); 50 | } 51 | 52 | @Override 53 | public void release(){ 54 | if(transport != null){ 55 | transport.close(); 56 | transport = null; 57 | } 58 | opened = false; 59 | } 60 | 61 | @Override 62 | public boolean isOpen() { 63 | return opened; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/main/java/com/hiriver/streamsource/impl/RandomStreamSourceSelector.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.streamsource.impl; 2 | 3 | import java.util.List; 4 | 5 | import com.hiriver.streamsource.StreamSource; 6 | import com.hiriver.streamsource.StreamSourceSelector; 7 | 8 | /** 9 | * 基于随机算法的HA数据源选择策略实现。 10 | * 线程安全 11 | * 12 | * @author hexiufeng 13 | * 14 | */ 15 | public class RandomStreamSourceSelector implements StreamSourceSelector { 16 | 17 | @Override 18 | public StreamSource select(List haStreamSourceList) { 19 | int count = haStreamSourceList.size(); 20 | int index = (int) (Math.random() * count); 21 | return haStreamSourceList.get(index); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/main/java/com/hiriver/streamsource/impl/StaticDbHostInfoSupplier.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.streamsource.impl; 2 | 3 | import com.hiriver.streamsource.DbHostInfo; 4 | import com.hiriver.streamsource.DbHostInfoSupplier; 5 | 6 | /** 7 | * created by Yang Huawei (xander.yhw@alibaba-inc.com) on 2018/9/5 23:57 8 | */ 9 | public class StaticDbHostInfoSupplier implements DbHostInfoSupplier { 10 | 11 | private String hostUrl; 12 | private String userName; 13 | private String password; 14 | 15 | 16 | public String getUserName() { 17 | return userName; 18 | } 19 | 20 | public void setUserName(String userName) { 21 | this.userName = userName; 22 | } 23 | 24 | public String getPassword() { 25 | return password; 26 | } 27 | 28 | public void setPassword(String password) { 29 | this.password = password; 30 | } 31 | 32 | public String getHostUrl() { 33 | return hostUrl; 34 | } 35 | 36 | public void setHostUrl(String hostUrl) { 37 | this.hostUrl = hostUrl; 38 | } 39 | 40 | @Override 41 | public DbHostInfo available() { 42 | return new DbHostInfo() { 43 | @Override 44 | public String getHostUrl() { 45 | return hostUrl; 46 | } 47 | 48 | @Override 49 | public String getUserName() { 50 | return userName; 51 | } 52 | 53 | @Override 54 | public String getPassword() { 55 | return password; 56 | } 57 | }; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/test/java/com/hiriver/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.hiriver; 2 | 3 | import junit.framework.Test; 4 | import junit.framework.TestCase; 5 | import junit.framework.TestSuite; 6 | 7 | /** 8 | * Unit test for simple App. 9 | */ 10 | public class AppTest 11 | extends TestCase 12 | { 13 | /** 14 | * Create the test case 15 | * 16 | * @param testName name of the test case 17 | */ 18 | public AppTest( String testName ) 19 | { 20 | super( testName ); 21 | } 22 | 23 | /** 24 | * @return the suite of tests being tested 25 | */ 26 | public static Test suite() 27 | { 28 | return new TestSuite( AppTest.class ); 29 | } 30 | 31 | /** 32 | * Rigourous Test :-) 33 | */ 34 | public void testApp() 35 | { 36 | assertTrue( true ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /hiriver-modules/hiriver/src/test/java/com/hiriver/streamsource/impl/TimestampBasedStreamSourceTest.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.streamsource.impl; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | import java.util.ArrayList; 8 | import java.util.LinkedHashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | import static com.hiriver.streamsource.impl.TimestampBasedStreamSource.ExtractValidBeginTimestampFunction; 13 | import static com.hiriver.streamsource.impl.TimestampBasedStreamSource.findLastFileStartLeTimestamp; 14 | 15 | /** 16 | * created by Yang Huawei (xander.yhw@alibaba-inc.com) on 2018/10/20 13:05 17 | */ 18 | public class TimestampBasedStreamSourceTest { 19 | 20 | private ExtractValidBeginTimestampFunction extractValidBeginTimestampFunction; 21 | private List binlogFiles; 22 | 23 | @Before 24 | public void init() { 25 | final Map firstEventTimestampMap = new LinkedHashMap<>(); 26 | firstEventTimestampMap.put("mysql-bin.005591", 100L); 27 | firstEventTimestampMap.put("mysql-bin.005592", 110L); 28 | firstEventTimestampMap.put("mysql-bin.005593", 120L); 29 | firstEventTimestampMap.put("mysql-bin.005594", 130L); 30 | firstEventTimestampMap.put("mysql-bin.005595", 140L); 31 | firstEventTimestampMap.put("mysql-bin.005596", 150L); 32 | 33 | this.extractValidBeginTimestampFunction = new ExtractValidBeginTimestampFunction() { 34 | @Override 35 | public long extract(String binlogFileName) { 36 | return firstEventTimestampMap.get(binlogFileName); 37 | } 38 | }; 39 | this.binlogFiles = new ArrayList<>(firstEventTimestampMap.keySet()); 40 | 41 | } 42 | 43 | @Test 44 | public void testFindLastFileStartLeTimestampGtLastFile() { 45 | Assert.assertEquals("mysql-bin.005596", 46 | findLastFileStartLeTimestamp(151L, binlogFiles, extractValidBeginTimestampFunction)); 47 | } 48 | 49 | @Test 50 | public void testFindLastFileStartLeTimestampEqLastFile() { 51 | Assert.assertEquals("mysql-bin.005596", 52 | findLastFileStartLeTimestamp(150L, binlogFiles, extractValidBeginTimestampFunction)); 53 | } 54 | 55 | @Test 56 | public void testFindLastFileStartLeTimestampEqFirstFile() { 57 | Assert.assertEquals("mysql-bin.005591", 58 | findLastFileStartLeTimestamp(100L, binlogFiles, extractValidBeginTimestampFunction)); 59 | } 60 | 61 | @Test 62 | public void testFindLastFileStartLeTimestampLtFirstFile() { 63 | Assert.assertEquals(null, 64 | findLastFileStartLeTimestamp(99L, binlogFiles, extractValidBeginTimestampFunction)); 65 | } 66 | 67 | @Test 68 | public void testFindLastFileStartLeTimestampEqSecondFile() { 69 | Assert.assertEquals("mysql-bin.005592", 70 | findLastFileStartLeTimestamp(110L, binlogFiles, extractValidBeginTimestampFunction)); 71 | } 72 | 73 | @Test 74 | public void testFindLastFileStartLeTimestampGtFirstFileLtSecondFile() { 75 | Assert.assertEquals("mysql-bin.005591", 76 | findLastFileStartLeTimestamp(105L, binlogFiles, extractValidBeginTimestampFunction)); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | com.hiriver 7 | hiriver-modules 8 | ../ 9 | 1.2.5-SNAPSHOT 10 | 11 | mysql-proto 12 | jar 13 | 14 | mysql-proto 15 | http://maven.apache.org 16 | 17 | 18 | 19 | 20 | junit 21 | junit 22 | 23 | 24 | ch.qos.logback 25 | logback-core 26 | 27 | 28 | ch.qos.logback 29 | logback-classic 30 | 31 | 32 | org.slf4j 33 | slf4j-api 34 | 35 | 36 | org.apache.commons 37 | commons-lang3 38 | 39 | 40 | commons-io 41 | commons-io 42 | 43 | 44 | 45 | 46 | 47 | org.apache.maven.plugins 48 | maven-compiler-plugin 49 | 50 | 1.7 51 | 1.7 52 | 53 | 54 | 55 | org.apache.maven.plugins 56 | maven-source-plugin 57 | 58 | 59 | attach-sources 60 | 61 | jar 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/local/DefaultLocalEventProcessor.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.local; 2 | 3 | import com.hiriver.unbiz.mysql.lib.output.BinlogColumnValue; 4 | import com.hiriver.unbiz.mysql.lib.output.BinlogResultRow; 5 | import com.hiriver.unbiz.mysql.lib.output.RowModifyTypeEnum; 6 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.ValidBinlogOutput; 7 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.ValidEventType; 8 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.event.BaseRowEvent; 9 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.event.GTidEvent; 10 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.event.TableMapEvent; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * Created by hexiufeng on 2017/6/19. 16 | */ 17 | public class DefaultLocalEventProcessor implements LocalEventProcessor { 18 | private String gtid = ""; 19 | 20 | @Override 21 | public void processTableMapEvent(TableMapEvent event) { 22 | // do nothing 23 | } 24 | 25 | @Override 26 | public void processValidBinlogOutput(ValidBinlogOutput validBinlogOutput) { 27 | if (validBinlogOutput.getEventType() == ValidEventType.GTID) { 28 | GTidEvent gevent = (GTidEvent) validBinlogOutput.getEvent(); 29 | gtid = gevent.getGTidString(); 30 | return; 31 | } 32 | if (validBinlogOutput.getEventType() == ValidEventType.ROW) { 33 | BaseRowEvent rowEvent = (BaseRowEvent) validBinlogOutput.getEvent(); 34 | String tableName = rowEvent.getTableMapEvent().getFullTableName(); 35 | 36 | doTableRows(tableName,gtid,rowEvent); 37 | return; 38 | } 39 | if (validBinlogOutput.getEventType() == ValidEventType.TRANS_COMMIT 40 | || validBinlogOutput.getEventType() == ValidEventType.TRANS_ROLLBACK) { 41 | doTransactionEnd(gtid); 42 | gtid = ""; 43 | return; 44 | } 45 | } 46 | 47 | protected void doTableRows(final String tableName,final String gtid,final BaseRowEvent rowEvent){ 48 | List rowList = rowEvent.getRowList(); 49 | for (BinlogResultRow row : rowList) { 50 | int index = 0; 51 | if (row.getRowModifyType() == RowModifyTypeEnum.UPDATE) { 52 | System.out.println("time:" + row.getBinlogOccurTime() + ",gtid:" + gtid + ",table:" + tableName + ",update"); 53 | for (BinlogColumnValue colValue : row.getBeforeColumnValueList()) { 54 | BinlogColumnValue after = row.getAfterColumnValueList().get(index++); 55 | System.out.println(colValue.getDefinition().getColumName()); 56 | System.out.println(colValue.getValue()); 57 | System.out.println(after.getValue()); 58 | System.out.println("==========================================="); 59 | } 60 | continue; 61 | } 62 | 63 | List valueList = null; 64 | if (row.getRowModifyType() == RowModifyTypeEnum.INSERT) { 65 | System.out.println("time:" + row.getBinlogOccurTime() + ",gtid:" + gtid + ",table:" + tableName + ",insert"); 66 | valueList = row.getAfterColumnValueList(); 67 | } 68 | 69 | if (row.getRowModifyType() == RowModifyTypeEnum.DELETE) { 70 | System.out.println("time:" + row.getBinlogOccurTime() + ",gtid:" + gtid + ",table:" + tableName + ",delete"); 71 | valueList = row.getBeforeColumnValueList(); 72 | } 73 | 74 | for (BinlogColumnValue colValue : valueList) { 75 | System.out.println(colValue.getDefinition().getColumName()); 76 | System.out.println(colValue.getValue()); 77 | System.out.println("==========================================="); 78 | } 79 | } 80 | } 81 | 82 | protected void doTransactionEnd(final String gtid){ 83 | // do nothing 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/local/LocalEventProcessor.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.local; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.ValidBinlogOutput; 4 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.event.TableMapEvent; 5 | 6 | /** 7 | * Created by hexiufeng on 2017/6/19. 8 | */ 9 | public interface LocalEventProcessor { 10 | void processTableMapEvent(TableMapEvent event); 11 | void processValidBinlogOutput(ValidBinlogOutput validBinlogOutput); 12 | } 13 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/BinlogStreamBlockingTransport.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.ValidBinlogOutput; 4 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.extra.BinlogPosition; 5 | 6 | /** 7 | * 描述binlog数据流 8 | * 9 | * @author hexiufeng 10 | * 11 | */ 12 | public interface BinlogStreamBlockingTransport extends BlockingTransport { 13 | /** 14 | * 按照指定的同步点开启复制 15 | * 16 | * @param binlogPos 同步点 17 | */ 18 | void dump(BinlogPosition binlogPos); 19 | 20 | /** 21 | * 返回给上层有效的binglog event。所谓有效的binlog event参见{@link ValidBinlogOutput}。
22 | * 23 | * 注意
24 | * 该方法为堵塞模式,如果不能从mysql server读取到有效的数据,调用方线程会一直被堵塞。
25 | * 26 | * 与本方法类似但非堵塞的方法是{@link BinlogStreamBlockingTransport#getBinlogOutputImmediately()} 27 | * 28 | * @return {@link ValidBinlogOutput}对象 29 | */ 30 | ValidBinlogOutput getBinlogOutput(); 31 | 32 | /** 33 | * 返回给上层有效的事件,与{@link BinlogStreamBlockingTransport#getBinlogOutput}不同的是,它读取到 34 | * mysql binlog事件即可返回,而不论它是否是有效的事件 35 | * 36 | * @return 有效事件,如果当前不是有效事件,返回null 37 | */ 38 | ValidBinlogOutput getBinlogOutputImmediately(); 39 | } 40 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/BlockingTransport.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.Request; 4 | import com.hiriverunbiz.mysql.lib.exp.HandShakeException; 5 | import com.hiriverunbiz.mysql.lib.exp.NetworkException; 6 | import com.hiriverunbiz.mysql.lib.exp.PeerResetNetworkException; 7 | import com.hiriverunbiz.mysql.lib.exp.UnOpenedSocket; 8 | 9 | /** 10 | * 描述blocking模型的mysql连接.它是一个客户端和mysql服务器之间连接的抽象。它是非线程安全的。 11 | *

12 | * 为了和jdbc的connection区别,这里用Transport进行命名。 13 | *

14 | * 15 | * @author hexiufeng 16 | * 17 | */ 18 | public interface BlockingTransport { 19 | 20 | /** 21 | * 打开连接 22 | * 23 | * @throws NetworkException 网络异常 24 | * @throws HandShakeException 握手阶段发生异常 25 | */ 26 | void open() throws NetworkException, HandShakeException; 27 | 28 | /** 29 | * 关闭连接 30 | */ 31 | void close(); 32 | 33 | /** 34 | * 连接是否已经打开 35 | * 36 | * @return true or false 37 | */ 38 | boolean isOpen(); 39 | 40 | /** 41 | * ping msyql server,验证mysql服务是否存活 42 | * 43 | * @return true or false 44 | */ 45 | boolean ping(); 46 | 47 | /** 48 | * 从连接中读取一个有效的响应数据块。有效的响应数据块指的是不包含包头的响应数据。本方法执行两个步骤: 49 | *
    50 | *
  • 读取包头,解析读取有效数据块的长度
  • 51 | *
  • 读取指定长度的数据库
  • 52 | *
53 | * 54 | * @return 有效数据块的数组 55 | * @throws NetworkException 网络发生异常 56 | * @throws PeerResetNetworkException 底层socket被对方关闭异常 57 | * @throws UnOpenedSocket 连接问未建立 58 | */ 59 | byte[] readResponsePayload() throws NetworkException, PeerResetNetworkException, UnOpenedSocket; 60 | 61 | /** 62 | * 发送请求 63 | * 64 | * @param request 请求对象 65 | * @throws NetworkException 网络发生异常 66 | * @throws UnOpenedSocket 连接问未建立 67 | */ 68 | void writeRequest(Request request) throws NetworkException, UnOpenedSocket; 69 | } 70 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/ColumnType.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.exp.InvalidColumnType; 4 | import org.apache.commons.lang3.StringUtils; 5 | 6 | /** 7 | * mysql所支持的列的数据类型,和java的类型有所不同 8 | * 9 | * @author hexiufeng 10 | * 11 | */ 12 | public enum ColumnType { 13 | MYSQL_TYPE_DECIMAL(0x00, 2,new String[]{"decimal"}), 14 | MYSQL_TYPE_TINY(0x01, 0,new String[]{"tinyint"}), 15 | MYSQL_TYPE_SHORT(0x02, 0,new String[]{"smallint"}), 16 | MYSQL_TYPE_LONG(0x03, 0,new String[]{"int"}), 17 | MYSQL_TYPE_FLOAT(0x04, 1,new String[]{"float"}), 18 | MYSQL_TYPE_DOUBLE(0x05, 1,new String[]{"double"}), 19 | MYSQL_TYPE_NULL(0x06, 0,new String[]{}), 20 | MYSQL_TYPE_TIMESTAMP(0x07, 0,new String[]{}), 21 | MYSQL_TYPE_LONGLONG(0x08, 0,new String[]{"bigint"}), 22 | MYSQL_TYPE_INT24(0x09, 0,new String[]{"int24"}), 23 | MYSQL_TYPE_DATE(0x0a, 0,new String[]{}), 24 | MYSQL_TYPE_TIME(0x0b, 0,new String[]{}), 25 | MYSQL_TYPE_DATETIME(0x0c, 0,new String[]{}), 26 | MYSQL_TYPE_YEAR(0x0d, 0,new String[]{"year"}), 27 | MYSQL_TYPE_NEWDATE(0x0e, 0,new String[]{"date"}), 28 | MYSQL_TYPE_VARCHAR(0x0f, 2,new String[]{"verchar"}), 29 | MYSQL_TYPE_BIT(0x10, 2,new String[]{"bit"}), 30 | MYSQL_TYPE_TIMESTAMP2(0x11, 1,new String[]{"timestamp"}), 31 | MYSQL_TYPE_DATETIME2(0x12, 1,new String[]{"datetime"}), // since mysql5.6, should be 1 32 | MYSQL_TYPE_TIME2(0x13, 1,new String[]{"time"}), 33 | MYSQL_TYPE_NEWDECIMAL(0xf6, 2,new String[]{"decimal"}), 34 | MYSQL_TYPE_ENUM(0xf7, 2,new String[]{"enum"}), 35 | MYSQL_TYPE_SET(0xf8, 2,new String[]{"set"}), 36 | MYSQL_TYPE_TINY_BLOB(0xf9, 0,new String[]{"tinyblob"}), 37 | MYSQL_TYPE_MEDIUM_BLOB(0xfa, 0,new String[]{"mediumblob"}), 38 | MYSQL_TYPE_LONG_BLOB(0xfb, 0,new String[]{"longblob"}), 39 | MYSQL_TYPE_BLOB(0xfc, 1,new String[]{"blob","text"}), 40 | MYSQL_TYPE_VAR_STRING(0xfd, 2,new String[]{"varstring"}), 41 | MYSQL_TYPE_STRING(0xfe, 2,new String[]{"string"}), 42 | MYSQL_TYPE_GEOMETRY(0xff, 0,new String[]{"geometry"}); 43 | 44 | private int typeValue; 45 | private int mataLen; 46 | private String[] names; 47 | 48 | private ColumnType(int typeValue, int metaLen, String[] names) { 49 | this.typeValue = typeValue; 50 | this.mataLen = metaLen; 51 | this.names = names; 52 | } 53 | 54 | public static ColumnType ofTypeValue(int typeValue){ 55 | for(ColumnType type:ColumnType.values()){ 56 | if(type.getTypeValue() == typeValue){ 57 | return type; 58 | } 59 | } 60 | throw new InvalidColumnType("type value is :" + typeValue); 61 | } 62 | 63 | public static ColumnType ofTypeName(String name){ 64 | for(ColumnType type:ColumnType.values()){ 65 | for(String n:type.names) { 66 | if(n.equalsIgnoreCase(name)) { 67 | return type; 68 | } 69 | } 70 | // String[] mulNames = StringUtils.split(type.name,","); 71 | // for(String n : mulNames) { 72 | // if(n.equalsIgnoreCase(name)) { 73 | // return type; 74 | // } 75 | // } 76 | } 77 | // throw new InvalidColumnType("type name is :" + name); 78 | return null; 79 | } 80 | 81 | private ColumnType(int typeValue, int metaLen) { 82 | this(typeValue, metaLen, new String[]{}); 83 | } 84 | 85 | public int getTypeValue() { 86 | return typeValue; 87 | } 88 | 89 | public int getMataLen() { 90 | return mataLen; 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/ResultContentReader.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib; 2 | 3 | /** 4 | * 从socket中持续读取数据的抽象描述。 5 | *

6 | * 在解析某些响应结果时需要持续的从socket的读取后续数据,这种情况大都发生在解析某些Response的过程之中, 为了让Response不耦合socket对象,定义割爱接口进行隔离 7 | *

8 | * 9 | * @author hexiufeng 10 | * 11 | */ 12 | public interface ResultContentReader { 13 | /** 14 | * 读取后续的结果数据 15 | * 16 | * @return byte[]数据块 17 | */ 18 | byte[] readNextPacketPayload(); 19 | } 20 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/SocketReadTimeoutHanlder.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib; 2 | 3 | /** 4 | * 当从主从复制的socket流中读取数据超时时的处理抽象,缺省实现直接抛出异常 5 | * 6 | * @author hexiufeng 7 | * 8 | */ 9 | public interface SocketReadTimeoutHanlder { 10 | /** 11 | * 异常处理 12 | * 13 | * @param message 辅助消息 14 | * @param e 异常 15 | */ 16 | void handle(String message, Exception e); 17 | } 18 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/TextProtocolBlockingTransport.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.text.FieldListCommandResponse; 4 | import com.hiriver.unbiz.mysql.lib.protocol.text.TextCommandFieldListRequest; 5 | import com.hiriver.unbiz.mysql.lib.protocol.text.TextCommandQueryResponse; 6 | 7 | /** 8 | * 支持msyql Text Query协议的连接. 9 | * 10 | *

11 | * Text Query指的是执行sql语句的协议 12 | *

13 | * 14 | * @author hexiufeng 15 | * 16 | */ 17 | public interface TextProtocolBlockingTransport extends BlockingTransport { 18 | /** 19 | * 执行没有返回记录的sql命令 20 | * 21 | * @param sql sql命令 22 | * @return 影响的记录数 23 | */ 24 | int executeSQL(String sql); 25 | 26 | /** 27 | * 执行查询sql,返回结果集合 28 | * 29 | * @param sql sql命令 30 | * @return TextCommandQueryResponse 结果集合 31 | */ 32 | TextCommandQueryResponse execute(String sql); 33 | 34 | /** 35 | * 显示指的表的字段定义 36 | * 37 | * @param fieldListRequest 字段定义命令 38 | * @return FieldListCommandResponse 39 | */ 40 | FieldListCommandResponse showFieldList(TextCommandFieldListRequest fieldListRequest); 41 | } 42 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/TransportConfig.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * socket properties config: 8 | *
    9 | *
  • connect timeout
  • 10 | *
  • soTimeout
  • 11 | *
  • receiveBufferSize
  • 12 | *
  • sendBufferSize
  • 13 | *
  • keepAlive
  • 14 | *
15 | * 16 | * @author hexiufeng 17 | * 18 | */ 19 | public class TransportConfig { 20 | private int connectTimeout = 15000; 21 | private int soTimeout = 15000; 22 | private int receiveBufferSize; 23 | private int sendBufferSize; 24 | private boolean keepAlive = true; 25 | 26 | private List initSqlList = new ArrayList(); 27 | 28 | { 29 | initSqlList.add("SET AUTOCOMMIT=1"); 30 | } 31 | 32 | public int getConnectTimeout() { 33 | return connectTimeout; 34 | } 35 | 36 | public int getSoTimeout() { 37 | return soTimeout; 38 | } 39 | 40 | public void setConnectTimeout(int connectTimeout) { 41 | this.connectTimeout = connectTimeout; 42 | } 43 | 44 | public void setSoTimeout(int soTimeout) { 45 | this.soTimeout = soTimeout; 46 | } 47 | 48 | public int getRecieveBufferSize() { 49 | return receiveBufferSize; 50 | } 51 | 52 | public int getSendBufferSize() { 53 | return sendBufferSize; 54 | } 55 | 56 | public void setRecieveBufferSize(int receiveBufferSize) { 57 | this.receiveBufferSize = receiveBufferSize; 58 | } 59 | 60 | public void setSendBufferSize(int sendBufferSize) { 61 | this.sendBufferSize = sendBufferSize; 62 | } 63 | 64 | public List getInitSqlList() { 65 | return initSqlList; 66 | } 67 | 68 | public void setInitSqlList(List initSqlList) { 69 | this.initSqlList = initSqlList; 70 | } 71 | 72 | public boolean isKeepAlive() { 73 | return keepAlive; 74 | } 75 | 76 | public void setKeepAlive(boolean keepAlive) { 77 | this.keepAlive = keepAlive; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/filter/TableFilter.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.filter; 2 | 3 | /** 4 | * 根据db、table名称进行过滤的抽象描述 5 | * 6 | * @author hexiufeng 7 | * 8 | */ 9 | public interface TableFilter { 10 | /** 11 | * 过滤 12 | * 13 | * @param dbName db名称 14 | * @param tableName 表名称 15 | * @return 是否通过过滤 16 | */ 17 | boolean filter(String dbName,String tableName); 18 | } 19 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/output/BinlogColumnValue.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.output; 2 | 3 | /** 4 | * 对上层暴露数据时,对每个字段的值的描述 5 | * 6 | * @author hexiufeng 7 | * 8 | */ 9 | public class BinlogColumnValue { 10 | private final ColumnDefinition definition; 11 | private final Object value; 12 | 13 | public BinlogColumnValue(ColumnDefinition definition, Object value) { 14 | this.definition = definition; 15 | this.value = value; 16 | } 17 | 18 | public ColumnDefinition getDefinition() { 19 | return definition; 20 | } 21 | 22 | public Object getValue() { 23 | return value; 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | String strValue = value == null ? "null" : value.toString(); 29 | return definition.getColumName() + ":" + strValue; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/output/BinlogResultRow.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.output; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * 行数据的描述 7 | * 8 | * @author hexiufeng 9 | * 10 | */ 11 | public class BinlogResultRow { 12 | /** 13 | * delete、update事件有效 14 | */ 15 | private final List beforeColumnValueList; 16 | /** 17 | * insert、update事件有效 18 | */ 19 | private final List afterColumnValueList; 20 | private final RowModifyTypeEnum rowModifyType; 21 | /** 22 | * 当前数据写入数据库时的时间戳,unix 时间戳 23 | */ 24 | private final long binlogOccurTime; 25 | 26 | public BinlogResultRow(List beforeColumnValueList, List afterColumnValueList, 27 | RowModifyTypeEnum rowModifyType, long binlogOccurTime) { 28 | this.beforeColumnValueList = beforeColumnValueList; 29 | this.afterColumnValueList = afterColumnValueList; 30 | this.rowModifyType = rowModifyType; 31 | this.binlogOccurTime = binlogOccurTime; 32 | } 33 | 34 | public List getBeforeColumnValueList() { 35 | return beforeColumnValueList; 36 | } 37 | 38 | public List getAfterColumnValueList() { 39 | return afterColumnValueList; 40 | } 41 | 42 | public RowModifyTypeEnum getRowModifyType() { 43 | return rowModifyType; 44 | } 45 | 46 | public long getBinlogOccurTime() { 47 | return binlogOccurTime; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/output/ColumnDefinition.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.output; 2 | 3 | import com.hiriver.unbiz.mysql.lib.ColumnType; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * 表字段描述 10 | * 11 | * @author hexiufeng 12 | * 13 | */ 14 | public class ColumnDefinition { 15 | /** 16 | * 字段名称 17 | */ 18 | private String columName; 19 | /** 20 | * 字段类型 21 | */ 22 | private ColumnType type; 23 | /** 24 | * 字段的字符集 25 | */ 26 | private String charset; 27 | /** 28 | * 是否是无符号 29 | */ 30 | private boolean isUnsigned; 31 | /** 32 | * 是否是主键 33 | */ 34 | private boolean isPrimary; 35 | /** 36 | * 是否在unique key字段 37 | */ 38 | private boolean isUnique; 39 | /** 40 | * 是否索引字段 41 | */ 42 | private boolean isKey; 43 | 44 | private int len; 45 | 46 | private final List enumList = new ArrayList<>(); 47 | private final List setList = new ArrayList<>(); 48 | 49 | public int getLen() { 50 | return len; 51 | } 52 | 53 | public void setLen(int len) { 54 | this.len = len; 55 | } 56 | 57 | public String getColumName() { 58 | return columName; 59 | } 60 | 61 | public ColumnType getType() { 62 | return type; 63 | } 64 | 65 | public void setColumName(String columName) { 66 | this.columName = columName; 67 | } 68 | 69 | public void setType(ColumnType type) { 70 | this.type = type; 71 | } 72 | 73 | public String getCharset() { 74 | return charset; 75 | } 76 | 77 | public void setCharset(String charset) { 78 | this.charset = charset; 79 | } 80 | 81 | public boolean isUnsigned() { 82 | return isUnsigned; 83 | } 84 | 85 | public boolean isPrimary() { 86 | return isPrimary; 87 | } 88 | 89 | public boolean isUnique() { 90 | return isUnique; 91 | } 92 | 93 | public boolean isKey() { 94 | return isKey; 95 | } 96 | 97 | public void setUnsigned(boolean isUnsigned) { 98 | this.isUnsigned = isUnsigned; 99 | } 100 | 101 | public void setPrimary(boolean isPrimary) { 102 | this.isPrimary = isPrimary; 103 | } 104 | 105 | public void setUnique(boolean isUnique) { 106 | this.isUnique = isUnique; 107 | } 108 | 109 | public void setKey(boolean isKey) { 110 | this.isKey = isKey; 111 | } 112 | 113 | public List getEnumList(){ 114 | return this.enumList; 115 | } 116 | 117 | public List getSetList(){ 118 | return this.setList; 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/output/RowModifyTypeEnum.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.output; 2 | 3 | /** 4 | * row data 事件类型 5 | * 6 | * @author hexiufeng 7 | * 8 | */ 9 | public enum RowModifyTypeEnum { 10 | INSERT,UPDATE,DELETE; 11 | } 12 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/AbstractRequest.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 4 | import com.hiriver.unbiz.mysql.lib.protocol.tool.SafeByteArrayOutputStream; 5 | 6 | /** 7 | * mysql request的抽象实现 8 | * 9 | * @author hexiufeng 10 | * 11 | */ 12 | public abstract class AbstractRequest implements Request { 13 | private int sequenceId; 14 | 15 | public int getSequenceId() { 16 | return sequenceId; 17 | } 18 | 19 | public void setSequenceId(int sequenceId) { 20 | this.sequenceId = sequenceId; 21 | } 22 | 23 | @Override 24 | public byte[] toByteArray() { 25 | SafeByteArrayOutputStream out = new SafeByteArrayOutputStream(256); 26 | // packet header holder 27 | out.safeWrite(MysqlNumberUtils.write4Int(0)); 28 | fillPayload(out); 29 | byte[] buffer = out.toByteArray(); 30 | PacketHeader header = new PacketHeader(); 31 | header.setPayloadLen(buffer.length - 4); 32 | header.setSequenceId(sequenceId); 33 | 34 | System.arraycopy(header.toByteArray(), 0, buffer, 0, 4); 35 | return buffer; 36 | } 37 | 38 | /** 39 | * 填充请求主体对象 40 | * 41 | * @param out 输出的byte[] 流 42 | */ 43 | protected abstract void fillPayload(SafeByteArrayOutputStream out); 44 | } 45 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/AbstractResponse.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol; 2 | 3 | /** 4 | * 抽象的mysql client-server协议的返回包 5 | * 6 | * @author hexiufeng 7 | * 8 | */ 9 | public abstract class AbstractResponse implements Response { 10 | private boolean checkSum = false; 11 | public boolean isCheckSum() { 12 | return checkSum; 13 | } 14 | public void setCheckSum(boolean checkSum) { 15 | this.checkSum = checkSum; 16 | } 17 | @Override 18 | public void parse(byte[] buf, Position pos) { 19 | throw new RuntimeException("don't support this method."); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/CapabilityFlagConst.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol; 2 | 3 | /** 4 | * 描述与mysql交互时客户端或mysql服务端需要具备的能力。 5 | * 6 | * 参见 7 | * http://dev. 8 | * mysql.com/doc/internals/en/capability-flags.html#packet-Protocol::CapabilityFlags 9 | * 10 | * @author hexiufeng 11 | * 12 | */ 13 | public interface CapabilityFlagConst { 14 | int CLIENT_LONG_PASSWORD = 0x00000001; 15 | int CLIENT_PLUGIN_AUTH = 0x00080000; 16 | int CLIENT_SECURE_CONNECTION = 0x00008000; 17 | int CLIENT_PROTOCOL_41 = 0x00000200; 18 | int CLIENT_CONNECT_WITH_DB = 0x00000008; 19 | int CLIENT_CONNECT_ATTRS = 0x00100000; 20 | int CLIENT_TRANSACTIONS = 0x00002000; 21 | int CLIENT_LONG_FLAG = 0x00000004; 22 | int CLIENT_MULTI_RESULTS = 0x00020000; 23 | int CLIENT_FOUND_ROWS = 0x00000002; 24 | int CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA = 0x00200000; 25 | int CLIENT_LOCAL_FILES = 0x00000080; 26 | int CLIENT_INTERACTIVE = 0x00000400; 27 | int CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS = 0x00400000; 28 | } 29 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/ColumnFlagConst.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol; 2 | 3 | /** 4 | * 字段的定义信息,copy from mysql c 源码 5 | * 6 | * @author hexiufeng 7 | * 8 | */ 9 | public interface ColumnFlagConst { 10 | int NOT_NULL_FLAG = 0x01; /* Field can't be NULL */ 11 | int PRI_KEY_FLAG = 0x02; /* Field is part of a primary key */ 12 | int UNIQUE_KEY_FLAG = 0x04; /* Field is part of a unique key */ 13 | int MULTIPLE_KEY_FLAG = 0x08; /* Field is part of a key */ 14 | int BLOB_FLAG = 0x10; /* Field is a blob */ 15 | int UNSIGNED_FLAG = 0x20; /* Field is unsigned */ 16 | int ZEROFILL_FLAG = 0x40; /* Field is zerofill */ 17 | int BINARY_FLAG = 0x80; /* Field is binary */ 18 | int ENUM_FLAG = 0x0100; /* field is an enum */ 19 | int AUTO_INCREMENT_FLAG = 0x0200; /* field is a autoincrement field */ 20 | int TIMESTAMP_FLAG = 0x0400; /* Field is a timestamp */ 21 | int SET_FLAG = 0x0800; /* field is a set */ 22 | int NO_DEFAULT_VALUE_FLAG = 0x1000; /* Field doesn't have default value */ 23 | int ON_UPDATE_NOW_FLAG = 0x2000; /* Field is set to NOW on UPDATE */ 24 | int NUM_FLAG = 0x8000; /* Field is num (for clients) */ 25 | } 26 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/EOFPacket.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 4 | 5 | /** 6 | * 响应结束包。用于一个请求可能返回大量数据时,比执行一个sql查询.
7 | * 8 | * see 9 | * http://dev.mysql.com/doc/internals/en/packet 10 | * -EOF_Packet.html 11 | * 12 | * @author hexiufeng 13 | * 14 | */ 15 | public class EOFPacket extends AbstractResponse implements Response { 16 | /** 17 | * MUST be 0xfe 18 | */ 19 | private int header; 20 | private int warnings; 21 | private int statusFlags; 22 | 23 | @Override 24 | public void parse(byte[] buf) { 25 | Position pos = Position.factory(); 26 | header = MysqlNumberUtils.read1Int(buf, pos); 27 | warnings = MysqlNumberUtils.read2Int(buf, pos); 28 | statusFlags = MysqlNumberUtils.read2Int(buf, pos); 29 | } 30 | 31 | public int getHeader() { 32 | return header; 33 | } 34 | 35 | public int getWarnings() { 36 | return warnings; 37 | } 38 | 39 | public int getStatusFlags() { 40 | return statusFlags; 41 | } 42 | 43 | public void setHeader(int header) { 44 | this.header = header; 45 | } 46 | 47 | public void setWarnings(int warnings) { 48 | this.warnings = warnings; 49 | } 50 | 51 | public void setStatusFlags(int statusFlags) { 52 | this.statusFlags = statusFlags; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/ERRPacket.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | 5 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 6 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlStringUtils; 7 | 8 | /** 9 | * 错误数据包。
10 | * 11 | * see 12 | * http://dev.mysql.com/doc/internals/en/packet 13 | * -ERR_Packet.html 14 | * 15 | * @author hexiufeng 16 | * 17 | */ 18 | public class ERRPacket extends AbstractResponse implements Response { 19 | /** 20 | * MUST be 0xff 21 | */ 22 | private int header; 23 | private int errorCode; 24 | private int sqlStateMarker; 25 | private byte[] sqlState; 26 | private String errorMessage; 27 | 28 | @Override 29 | public void parse(byte[] buf) { 30 | Position pos = Position.factory(); 31 | header = MysqlNumberUtils.read1Int(buf, pos); 32 | errorCode = MysqlNumberUtils.read2Int(buf, pos); 33 | // sqlStateMarker = MysqlNumberUtils.read1Int(buf, pos); 34 | // sqlState = MysqlStringUtils.readFixString(buf, pos, 3); 35 | try { 36 | errorMessage = new String(MysqlStringUtils.readEofString(buf, pos, super.isCheckSum()), "utf-8"); 37 | } catch (UnsupportedEncodingException e) { 38 | // ignore 39 | } 40 | } 41 | 42 | public int getHeader() { 43 | return header; 44 | } 45 | 46 | public int getErrorCode() { 47 | return errorCode; 48 | } 49 | 50 | public int getSqlStateMarker() { 51 | return sqlStateMarker; 52 | } 53 | 54 | public byte[] getSqlState() { 55 | return sqlState; 56 | } 57 | 58 | public String getErrorMessage() { 59 | return errorMessage; 60 | } 61 | 62 | public void setErrorCode(int errorCode) { 63 | this.errorCode = errorCode; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/OKPacket.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 4 | 5 | /** 6 | * 正确响应数据包。
7 | * 8 | * see 9 | * http://dev.mysql.com/doc/internals/en/packet- 10 | * OK_Packet.html 11 | * 12 | * @author hexiufeng 13 | * 14 | */ 15 | public class OKPacket extends AbstractResponse implements Response { 16 | /** 17 | * MUST be 0x00 18 | */ 19 | private int header; 20 | private long affectedRows; 21 | private long lastInsertId; 22 | private int statusFlags; 23 | private int warnings; 24 | 25 | @Override 26 | public void parse(byte[] buf) { 27 | Position pos = Position.factory(); 28 | header = MysqlNumberUtils.read1Int(buf, pos); 29 | affectedRows = MysqlNumberUtils.readLencodeLong(buf, pos); 30 | lastInsertId = MysqlNumberUtils.readLencodeLong(buf, pos); 31 | statusFlags = MysqlNumberUtils.read2Int(buf, pos); 32 | warnings = MysqlNumberUtils.read2Int(buf, pos); 33 | } 34 | 35 | public int getHeader() { 36 | return header; 37 | } 38 | 39 | public long getAffectedRows() { 40 | return affectedRows; 41 | } 42 | 43 | public long getLastInsertId() { 44 | return lastInsertId; 45 | } 46 | 47 | public int getStatusFlags() { 48 | return statusFlags; 49 | } 50 | 51 | public int getWarnings() { 52 | return warnings; 53 | } 54 | 55 | public void setHeader(int header) { 56 | this.header = header; 57 | } 58 | 59 | public void setAffectedRows(long affectedRows) { 60 | this.affectedRows = affectedRows; 61 | } 62 | 63 | public void setLastInsertId(long lastInsertId) { 64 | this.lastInsertId = lastInsertId; 65 | } 66 | 67 | public void setStatusFlags(int statusFlags) { 68 | this.statusFlags = statusFlags; 69 | } 70 | 71 | public void setWarnings(int warnings) { 72 | this.warnings = warnings; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/PacketHeader.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 4 | 5 | /** 6 | * 与mysql进行交互的数据包的头,msyql的由固定4个字节组成: 7 | *
    8 | *
  • 前三个字节是payloadLen
  • 9 | *
  • 最后一个字节是sequenceId
  • 10 | *
11 | * 12 | * @author hexiufeng 13 | * 14 | */ 15 | public class PacketHeader extends AbstractResponse implements Response, Request { 16 | /** 17 | * 后续数据的字节数 18 | */ 19 | private int payloadLen; 20 | /** 21 | * 数据包的序号,从0开始,下一名开始时重置为0 22 | */ 23 | private int sequenceId; 24 | 25 | public int getPayloadLen() { 26 | return payloadLen; 27 | } 28 | 29 | public void setPayloadLen(int payloadLen) { 30 | this.payloadLen = payloadLen; 31 | } 32 | 33 | public int getSequenceId() { 34 | return sequenceId; 35 | } 36 | 37 | public void setSequenceId(int sequenceId) { 38 | this.sequenceId = sequenceId; 39 | } 40 | 41 | @Override 42 | public void parse(byte[] packetHeader) { 43 | Position pos = Position.factory(); 44 | payloadLen = MysqlNumberUtils.read3Int(packetHeader, pos); 45 | sequenceId = MysqlNumberUtils.read1Int(packetHeader, pos); 46 | } 47 | 48 | @Override 49 | public byte[] toByteArray() { 50 | byte[] buffer = MysqlNumberUtils.write4Int(payloadLen); 51 | buffer[3] = (byte) sequenceId; 52 | return buffer; 53 | } 54 | 55 | /** 56 | * header的字节数 57 | * 58 | * @return header的字节数 59 | */ 60 | public int getExpectLen() { 61 | return 4; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/Position.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol; 2 | 3 | /** 4 | * 从byte数组中解析数据时,描述当前解析到哪个字节的辅助类, 5 | * 在一包数据被持续解析时非常有用,比如解析多行数据时, 6 | * 7 | * @author hexiufeng 8 | * 9 | */ 10 | public final class Position { 11 | private int pos; 12 | 13 | private Position(int pos) { 14 | this.pos = pos; 15 | } 16 | 17 | public static Position factory() { 18 | return factory(0); 19 | } 20 | 21 | public static Position factory(int pos) { 22 | return new Position(pos); 23 | } 24 | 25 | public int getPos() { 26 | return this.pos; 27 | } 28 | 29 | public int getAndForwordPos() { 30 | return pos++; 31 | } 32 | 33 | public int getAndForwordPos(int step) { 34 | int cur = pos; 35 | pos += step; 36 | return cur; 37 | } 38 | 39 | public int forwardPos() { 40 | return ++pos; 41 | } 42 | 43 | public int forwardPos(int step) { 44 | pos += step; 45 | return pos; 46 | } 47 | 48 | public void reset() { 49 | pos = 0; 50 | } 51 | 52 | public void reset(int newPos) { 53 | pos = newPos; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/Request.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol; 2 | 3 | /** 4 | * 与mysql交互的请求.该请求对象包含请求头和请求主体数据 5 | * 6 | * @author hexiufeng 7 | * 8 | */ 9 | public interface Request { 10 | /** 11 | * 把请求对象转换为可以socket发送的byte数组 12 | * 13 | * @return byte[]数组 14 | */ 15 | byte[] toByteArray(); 16 | } 17 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/Response.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol; 2 | 3 | /** 4 | * 与mysql交互的响应对象 5 | * 6 | * @author hexiufeng 7 | * 8 | */ 9 | public interface Response { 10 | /** 11 | * 把byte[]数据转换为响应对象 12 | * 13 | * @param buf byte[]数组 14 | */ 15 | void parse(byte[] buf); 16 | 17 | /** 18 | * 解析当前buf的数据,使用pos来控制当已经处理数据的位置,在数据被多次处理时非常 19 | * 有效,比如在解析多行数据时 20 | * 21 | * @param buf 已经从mysql server读取到数据缓存 22 | * @param pos 保存当前已经处理的位置 23 | */ 24 | void parse(byte[] buf, Position pos); 25 | } 26 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binary/BitColumnTypeValueParser.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binary; 2 | 3 | import com.hiriver.unbiz.mysql.lib.output.ColumnDefinition; 4 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 5 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 6 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlStringUtils; 7 | 8 | /** 9 | * bit类型的数据解析器 10 | * 11 | * @author hexiufeng 12 | * 13 | */ 14 | public class BitColumnTypeValueParser implements ColumnTypeValueParser { 15 | 16 | @Override 17 | public Object parse(byte[] buf, Position pos, ColumnDefinition columnDef, int meta) { 18 | int nbits = ((meta >>> 8) * 8) + (meta & 0xff); 19 | int len = (nbits + 7) / 8; 20 | byte[] bitBuff = MysqlStringUtils.readFixString(buf, pos, len); 21 | StringBuilder sb = new StringBuilder(); 22 | for(int i = 0; i < bitBuff.length;i++){ 23 | int v = bitBuff[i] & 0xff; 24 | if(i == 0) { 25 | sb.append(Integer.toBinaryString(v)); 26 | continue; 27 | } 28 | String pad = "00000000" + Integer.toBinaryString(v); 29 | sb.append(pad.substring(pad.length() - 8)); 30 | } 31 | return "0b" + sb.toString(); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binary/BlobColumnTypeValueParser.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binary; 2 | 3 | import com.hiriver.unbiz.mysql.lib.CharsetMapping; 4 | import com.hiriver.unbiz.mysql.lib.output.ColumnDefinition; 5 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 6 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 7 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlStringUtils; 8 | import com.hiriver.unbiz.mysql.lib.protocol.tool.StringTool; 9 | 10 | /** 11 | * blob or text,text和 blob在底层存储是相同的,只是blob类型的字符集是binary,而text类型有utf-8或者其他字符集 12 | * 13 | * @author hexiufeng 14 | * 15 | */ 16 | public class BlobColumnTypeValueParser implements ColumnTypeValueParser { 17 | 18 | @Override 19 | public Object parse(byte[] buf, Position pos, ColumnDefinition columnDef, int meta) { 20 | int len = (int) MysqlNumberUtils.readNInt(buf, pos, meta); 21 | if (CharsetMapping.isBinary(columnDef.getCharset())) { 22 | return MysqlStringUtils.readFixString(buf, pos, len); 23 | } else { 24 | return StringTool.safeConvertBytes2String(MysqlStringUtils.readFixString(buf, pos, len), 25 | columnDef.getCharset()); 26 | } 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binary/ColumnTypeValueParser.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binary; 2 | 3 | import com.hiriver.unbiz.mysql.lib.output.ColumnDefinition; 4 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 5 | 6 | /** 7 | * binlog数据解析器,根据不同的列数据类型不同,而有不同解析方式 8 | * 9 | * @author hexiufeng 10 | * 11 | */ 12 | public interface ColumnTypeValueParser { 13 | /** 14 | * 根据binlog日志数据解析出对应的java类型数据。 15 | * 16 | * @param buf 二进制的binlog数据 17 | * @param pos 用于记录buffer中已解析数据的位置 18 | * @param columnDef 列的类型 19 | * @param meta binlog中的该类型的meta信息 20 | * @return java数据类型 21 | */ 22 | Object parse(byte[] buf, Position pos, ColumnDefinition columnDef, int meta); 23 | } 24 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binary/DateColumnTypeValueParser.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binary; 2 | 3 | import java.util.Calendar; 4 | 5 | import com.hiriver.unbiz.mysql.lib.output.ColumnDefinition; 6 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 7 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 8 | 9 | /** 10 | * date 类型的解析器 11 | * 12 | * @author hexiufeng 13 | * 14 | */ 15 | public class DateColumnTypeValueParser implements ColumnTypeValueParser { 16 | 17 | @Override 18 | public Object parse(byte[] buf, Position pos, ColumnDefinition columnDef, int meta) { 19 | int tmp = MysqlNumberUtils.read3Int(buf, pos); 20 | int day = tmp & 31; 21 | int month = (tmp >> 5 & 15); 22 | int year = tmp >> 9; 23 | Calendar cal = Calendar.getInstance(); 24 | cal.set(year, month - 1, day, 0, 0, 0); 25 | return cal.getTime(); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binary/DateTime2ColumnTypeValueParser.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binary; 2 | 3 | import java.util.Calendar; 4 | 5 | import com.hiriver.unbiz.mysql.lib.output.ColumnDefinition; 6 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 7 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 8 | 9 | /** 10 | * datetime2 类型的解析器。处理逻辑来自mysql源码 11 | * 12 | * @author hexiufeng 13 | * 14 | */ 15 | public class DateTime2ColumnTypeValueParser implements ColumnTypeValueParser { 16 | private static final long DATETIMEF_INT_OFS = 0x8000000000L; 17 | 18 | @Override 19 | public Object parse(byte[] buf, Position pos, ColumnDefinition columnDef, int meta) { 20 | long val = myDatetimePackedFromBinary(buf, pos, meta); 21 | // int secondPart = (int)(val % (1L<<24)); 22 | long ymdhms = val >>> 24; 23 | long ymd = ymdhms >>> 17; 24 | long ym = ymd >> 5; 25 | long hms = ymdhms % (1L << 17); 26 | 27 | int day = (int) (ymd % (1 << 5)); 28 | int month = (int) (ym % 13); 29 | int year = (int) (ym / 13); 30 | 31 | int second = (int) (hms % (1 << 6)); 32 | int minute = (int) ((hms >> 6) % (1 << 6)); 33 | int hour = (int) (hms >> 12); 34 | 35 | Calendar cal = Calendar.getInstance(); 36 | 37 | cal.set(year, month - 1, day, hour, minute, second); 38 | cal.set(Calendar.MILLISECOND, 0); 39 | return cal.getTime(); 40 | } 41 | 42 | private long myDatetimePackedFromBinary(byte[] buf, Position pos, int dec) { 43 | long intpart = MysqlNumberUtils.readBigEdianNInt(buf, pos, 5) - DATETIMEF_INT_OFS; 44 | long frac = 0; 45 | switch (dec) { 46 | case 0: 47 | default: 48 | return intpart << 24; 49 | case 1: 50 | case 2: 51 | frac = MysqlNumberUtils.read1Int(buf, pos) * 10000; 52 | break; 53 | case 3: 54 | case 4: 55 | frac = (MysqlNumberUtils.readBigEdianNInt(buf, pos, 2) * 100); 56 | break; 57 | case 5: 58 | case 6: 59 | frac = (MysqlNumberUtils.readBigEdianNInt(buf, pos, 3)); 60 | break; 61 | } 62 | return intpart << 24 + frac; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binary/DateTimeColumnTypeValueParser.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binary; 2 | 3 | import java.util.Calendar; 4 | 5 | import com.hiriver.unbiz.mysql.lib.output.ColumnDefinition; 6 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 7 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 8 | 9 | /** 10 | * datetime 类型解析器 11 | * 12 | * @author hexiufeng 13 | * 14 | */ 15 | public class DateTimeColumnTypeValueParser implements ColumnTypeValueParser { 16 | 17 | @Override 18 | public Object parse(byte[] buf, Position pos, ColumnDefinition columnDef, int meta) { 19 | long tmp = MysqlNumberUtils.read8Int(buf, pos); 20 | long d = tmp / 1000000; 21 | long t = tmp % 1000000; 22 | 23 | int year = (int) (d / 10000); 24 | int month = (int) ((d % 10000) / 100); 25 | int date = (int) (d % 100); 26 | 27 | int hour = (int) (t / 10000); 28 | int minute = (int) ((t % 10000) / 100); 29 | int second = (int) (t % 100); 30 | Calendar cal = Calendar.getInstance(); 31 | cal.set(year, month - 1, date, hour, minute, second); 32 | cal.set(Calendar.MILLISECOND, 0); 33 | return cal.getTime(); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binary/DecimalColumnTypeValueParser.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binary; 2 | 3 | import com.hiriver.unbiz.mysql.lib.output.ColumnDefinition; 4 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 5 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 6 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlStringUtils; 7 | import com.hiriver.unbiz.mysql.lib.protocol.tool.MysqlDecimal; 8 | 9 | import java.math.BigDecimal; 10 | 11 | /** 12 | * decimal 数据类型解析器 13 | * 14 | * 15 | * @author hexiufeng 16 | * 17 | */ 18 | public class DecimalColumnTypeValueParser implements ColumnTypeValueParser { 19 | 20 | @Override 21 | public Object parse(byte[] buf, Position pos, ColumnDefinition columnDef, int meta) { 22 | int scale = (meta >>> 8); 23 | int precision = meta & 0xff; 24 | MysqlDecimal myDecimal = new MysqlDecimal(precision,scale); 25 | byte[] decBuf = MysqlStringUtils.readFixString(buf, pos, myDecimal.getBinSize()); 26 | myDecimal.parse(decBuf); 27 | return myDecimal.toDecimal(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binary/DoubleColumnTypeValueParser.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binary; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.nio.ByteOrder; 5 | 6 | import com.hiriver.unbiz.mysql.lib.output.ColumnDefinition; 7 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 8 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlStringUtils; 9 | 10 | /** 11 | * double数据类型解析器 12 | * 13 | * @author hexiufeng 14 | * 15 | */ 16 | public class DoubleColumnTypeValueParser implements ColumnTypeValueParser { 17 | 18 | @Override 19 | public Object parse(byte[] buf, Position pos, ColumnDefinition columnDef, int meta) { 20 | byte[] array = MysqlStringUtils.readFixString(buf, pos, 8); 21 | ByteBuffer bbf = ByteBuffer.wrap(array); 22 | bbf.order(ByteOrder.LITTLE_ENDIAN); 23 | return bbf.getDouble(); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binary/EnumColumnTypeValueParser.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binary; 2 | 3 | import com.hiriver.unbiz.mysql.lib.output.ColumnDefinition; 4 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 5 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 6 | 7 | /** 8 | * mysql枚举数据类型解析器,mysql枚举在mysql binlog内部本质上是string类型。代码逻辑来自mysql源码
9 | *

10 | * mysql的枚举在底层上只存储枚举对应的索引值,而每个索引所代表的文本值存储在表结构的元数据中,每次的 11 | * 查询结果返回时做一次索引到文本的转化,这样可以降低存储空间 12 | *

13 | * 注意:枚举的值是从1开始的。 14 | * 15 | * @author hexiufeng 16 | * 17 | */ 18 | public class EnumColumnTypeValueParser implements ColumnTypeValueParser { 19 | 20 | @Override 21 | public Object parse(byte[] buf, Position pos, ColumnDefinition columnDef, int meta) { 22 | int value = 0; 23 | int flag = meta & 0xff; 24 | switch (flag) { 25 | case 1: 26 | value = MysqlNumberUtils.read1Int(buf, pos); 27 | break; 28 | case 2: 29 | value = (int) MysqlNumberUtils.readBigEdianNInt(buf, pos, 2); 30 | break; 31 | default: 32 | } 33 | return columnDef.getEnumList().get(value - 1); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binary/FloatColumnTypeValueParser.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binary; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.nio.ByteOrder; 5 | 6 | import com.hiriver.unbiz.mysql.lib.output.ColumnDefinition; 7 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 8 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlStringUtils; 9 | 10 | /** 11 | * float数据类型解析器 12 | * 13 | * @author hexiufeng 14 | * 15 | */ 16 | public class FloatColumnTypeValueParser implements ColumnTypeValueParser { 17 | 18 | @Override 19 | public Object parse(byte[] buf, Position pos, ColumnDefinition columnDef, int meta) { 20 | byte[] array = MysqlStringUtils.readFixString(buf, pos, 4); 21 | ByteBuffer bbf = ByteBuffer.wrap(array); 22 | bbf.order(ByteOrder.LITTLE_ENDIAN); 23 | return bbf.getFloat(); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binary/IntegerColumnTypeValueParser.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binary; 2 | 3 | import com.hiriver.unbiz.mysql.lib.output.ColumnDefinition; 4 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 5 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 6 | 7 | /** 8 | * 32 bit int类型解析器,对应mysql的long,mysql的长整形是bigint,如果该字段是usigned的,返回的是java Long类型 9 | * 10 | * @author hexiufeng 11 | * 12 | */ 13 | public class IntegerColumnTypeValueParser implements ColumnTypeValueParser { 14 | private final int len; 15 | 16 | public IntegerColumnTypeValueParser(int len) { 17 | this.len = len; 18 | } 19 | 20 | @Override 21 | public Object parse(byte[] buf, Position pos, ColumnDefinition columnDef, int meta) { 22 | if (columnDef.isUnsigned()) { 23 | long value = MysqlNumberUtils.readNInt(buf, pos, len); 24 | if (isShort() || isByte()) { 25 | return (int) value; 26 | } 27 | return Long.valueOf(value); 28 | } else { 29 | int value = MysqlNumberUtils.readNRawInt(buf, pos, len); 30 | if(isShort()){ 31 | return Integer.valueOf((short)value); 32 | } 33 | if(isByte()){ 34 | return Integer.valueOf((byte)value); 35 | } 36 | return Integer.valueOf(value); 37 | } 38 | } 39 | 40 | private boolean isShort() { 41 | return len == 2; 42 | } 43 | private boolean isByte() { 44 | return len == 1; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binary/LongColumnTypeValueParser.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binary; 2 | 3 | import java.math.BigInteger; 4 | import java.nio.ByteBuffer; 5 | import java.nio.ByteOrder; 6 | 7 | import com.hiriver.unbiz.mysql.lib.output.ColumnDefinition; 8 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 9 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 10 | 11 | /** 12 | * mysql bigint类型的解析器,如果该字段是unsigned,返回的是BigInteger 13 | * 14 | * 15 | * @author hexiufeng 16 | * 17 | */ 18 | public class LongColumnTypeValueParser implements ColumnTypeValueParser { 19 | 20 | @Override 21 | public Object parse(byte[] buf, Position pos, ColumnDefinition columnDef, int meta) { 22 | long value = MysqlNumberUtils.read8Int(buf, pos); 23 | if (columnDef.isUnsigned()) { 24 | ByteBuffer bbf = ByteBuffer.allocate(8); 25 | bbf.order(ByteOrder.BIG_ENDIAN); 26 | bbf.putLong(value); 27 | return new BigInteger(bbf.array()); 28 | } else { 29 | return Long.valueOf(value); 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binary/NullColumnTypeValueParser.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binary; 2 | 3 | import com.hiriver.unbiz.mysql.lib.output.ColumnDefinition; 4 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 5 | 6 | /** 7 | * null类型的数据解析器,一般没用 8 | * 9 | * @author hexiufeng 10 | * 11 | */ 12 | public class NullColumnTypeValueParser implements ColumnTypeValueParser { 13 | 14 | @Override 15 | public Object parse(byte[] buf, Position pos, ColumnDefinition columnDef, int meta) { 16 | return null; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binary/SetColumnTypeValueParser.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binary; 2 | 3 | import com.hiriver.unbiz.mysql.lib.output.ColumnDefinition; 4 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 5 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlStringUtils; 6 | 7 | /** 8 | * mysql set类型的解析器,它和枚举一样,本质上是string,一般不用
9 | *

10 | * mysql的set与枚举类似,也是在表结构的元数据中定义一组文本值,在数据存储内部值存文本的索引, 11 | * 从而降低存储空间,但与枚举不同的是一个set值可能存储多个值,比如 set定义为(a,b,c,d),但 12 | * 某条记录只存储(a,d),在mysql是使用位图索引实现的,set中每个元素索引使用位的位置代替,比如 13 | * set(a,b,c,d)对应1111,那么(a,d)就是1001 14 | * 15 | *

16 | * 17 | * @author hexiufeng 18 | * 19 | */ 20 | public class SetColumnTypeValueParser implements ColumnTypeValueParser { 21 | 22 | private static final int[] ONES = { 23 | 1, 24 | 1 << 1, 25 | 1 << 2, 26 | 1 << 3, 27 | 1 << 4, 28 | 1 << 5, 29 | 1 << 6, 30 | 1 << 7 31 | 32 | }; 33 | 34 | @Override 35 | public Object parse(byte[] buf, Position pos, ColumnDefinition columnDef, int meta) { 36 | 37 | byte[] v =MysqlStringUtils.readFixString(buf, pos, meta); 38 | StringBuilder sb = new StringBuilder(); 39 | int index = 0; 40 | for(byte b : v) { 41 | for(int i = 0;i < 8; i++){ 42 | if((b & ONES[i]) == ONES[i]) { 43 | sb.append(columnDef.getSetList().get(index * 8 + i)); 44 | sb.append(","); 45 | } 46 | } 47 | } 48 | 49 | return sb.substring(0,sb.length() - 1); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binary/StringColumnTypeValueParser.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binary; 2 | 3 | import com.hiriver.unbiz.mysql.lib.output.ColumnDefinition; 4 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 5 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 6 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlStringUtils; 7 | import com.hiriver.unbiz.mysql.lib.protocol.tool.StringTool; 8 | 9 | /** 10 | * var char, var string, string 11 | * 12 | * @author hexiufeng 13 | * 14 | */ 15 | public class StringColumnTypeValueParser implements ColumnTypeValueParser { 16 | 17 | @Override 18 | public Object parse(byte[] buf, Position pos, ColumnDefinition columnDef, int meta) { 19 | // 注意,当column type is Type_String时, 这儿的meta不是mysql的meta,已经被转换过 20 | // see BaseRowEvent#parseEachColumnOfRow 21 | // meta is the max length of the column 22 | int len = 0; 23 | if (meta < 256) { 24 | len = (int) MysqlNumberUtils.read1Int(buf, pos); 25 | } else { 26 | len = (int) MysqlNumberUtils.read2Int(buf, pos); 27 | } 28 | 29 | return StringTool.safeConvertBytes2String(MysqlStringUtils.readFixString(buf, pos, len), 30 | columnDef.getCharset()); 31 | 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binary/TimeColumnTypeValueParser.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binary; 2 | 3 | import java.sql.Time; 4 | import java.util.Calendar; 5 | 6 | import com.hiriver.unbiz.mysql.lib.output.ColumnDefinition; 7 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 8 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 9 | 10 | /** 11 | * time 类型解析器 12 | * 13 | * @author hexiufeng 14 | * 15 | */ 16 | public class TimeColumnTypeValueParser implements ColumnTypeValueParser { 17 | 18 | @Override 19 | public Object parse(byte[] buf, Position pos, ColumnDefinition columnDef, int meta) { 20 | Calendar calendar = Calendar.getInstance(); 21 | calendar.set(Calendar.MILLISECOND, 0); 22 | int tmp = MysqlNumberUtils.read3Int(buf, pos); 23 | calendar.set(0, 0, 0, tmp / 10000, (tmp % 10000) / 100, tmp % 100); 24 | return new Time(calendar.getTimeInMillis()); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binary/TimeStamp2ColumnTypeValueParser.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binary; 2 | 3 | import java.sql.Timestamp; 4 | 5 | import com.hiriver.unbiz.mysql.lib.output.ColumnDefinition; 6 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 7 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 8 | 9 | /** 10 | * timestamp2类型解析器 11 | * 12 | * @author hexiufeng 13 | * 14 | */ 15 | public class TimeStamp2ColumnTypeValueParser implements ColumnTypeValueParser { 16 | 17 | @Override 18 | public Object parse(byte[] buf, Position pos, ColumnDefinition columnDef, int meta) { 19 | long secValue = MysqlNumberUtils.readBigEdianNInt(buf, pos, 4); 20 | long usecValue = 0; 21 | switch (meta) { 22 | case 0: 23 | default: 24 | break; 25 | case 1: 26 | case 2: 27 | usecValue = MysqlNumberUtils.read1Int(buf, pos) * 10000L; 28 | break; 29 | case 3: 30 | case 4: 31 | usecValue = MysqlNumberUtils.readBigEdianNInt(buf, pos, 2) * 100; 32 | break; 33 | case 5: 34 | case 6: 35 | usecValue = MysqlNumberUtils.readBigEdianNInt(buf, pos, 3); 36 | } 37 | Timestamp tsValue = new Timestamp(secValue*1000); 38 | tsValue.setNanos((int) usecValue); 39 | return tsValue; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binary/TimeStampColumnTypeValueParser.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binary; 2 | 3 | import java.sql.Timestamp; 4 | 5 | import com.hiriver.unbiz.mysql.lib.output.ColumnDefinition; 6 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 7 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 8 | 9 | /** 10 | * timestamp类型解析器 11 | * 12 | * @author hexiufeng 13 | * 14 | */ 15 | public class TimeStampColumnTypeValueParser implements ColumnTypeValueParser { 16 | 17 | @Override 18 | public Object parse(byte[] buf, Position pos, ColumnDefinition columnDef, int meta) { 19 | long secValue = MysqlNumberUtils.readNInt(buf, pos, 4); 20 | secValue *=1000; 21 | return new Timestamp(secValue); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binary/UnsupportColumnTypeValueParser.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binary; 2 | 3 | import com.hiriver.unbiz.mysql.lib.output.ColumnDefinition; 4 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 5 | 6 | /** 7 | * 通用的描述不支持的数据类型,直接返回null 8 | * 9 | * @author hexiufeng 10 | * 11 | */ 12 | public class UnsupportColumnTypeValueParser implements ColumnTypeValueParser { 13 | 14 | @Override 15 | public Object parse(byte[] buf, Position pos, ColumnDefinition columnDef, int meta) { 16 | return null; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binary/YearColumnTypeValueParser.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binary; 2 | 3 | import com.hiriver.unbiz.mysql.lib.output.ColumnDefinition; 4 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 5 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 6 | 7 | /** 8 | * year类型解析器 9 | * 10 | * @author hexiufeng 11 | * 12 | */ 13 | public class YearColumnTypeValueParser implements ColumnTypeValueParser { 14 | 15 | @Override 16 | public Object parse(byte[] buf, Position pos, ColumnDefinition columnDef, int meta) { 17 | int value = MysqlNumberUtils.read1Int(buf, pos); 18 | return value + 1900; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/AbstractBinlogResponse.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.Response; 4 | 5 | /** 6 | * 抽象描述同步binlog的返回数据包 7 | * 8 | * @author hexiufeng 9 | * 10 | */ 11 | public abstract class AbstractBinlogResponse implements Response { 12 | 13 | @Override 14 | public void parse(byte[] buf) { 15 | throw new RuntimeException("don't support this method."); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/AbstractTableMetaProvider.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog; 2 | 3 | import com.hiriver.unbiz.mysql.lib.TextProtocolBlockingTransport; 4 | import com.hiriver.unbiz.mysql.lib.TextProtocolBlockingTransportImpl; 5 | import com.hiriver.unbiz.mysql.lib.TransportConfig; 6 | import com.hiriver.unbiz.mysql.lib.output.ColumnDefinition; 7 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.event.TableMapEvent; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | abstract class AbstractTableMetaProvider implements TableMetaProvider { 16 | protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractTableMetaProvider.class); 17 | final Map cache = new HashMap(); 18 | @Override 19 | public TableMeta getTableMeta(long tableId, TableMapEvent tableMapEvent) { 20 | String schemaName = tableMapEvent.getSchema(); 21 | String tableName = tableMapEvent.getTableName(); 22 | String fullTableName = schemaName + "." + tableName; 23 | if (cache.containsKey(fullTableName)) { 24 | TableMeta tableMeta = cache.get(fullTableName); 25 | if (tableMeta.getTableId() == tableId) { 26 | return tableMeta; 27 | } 28 | } 29 | TableMeta tableMeta = new TableMeta(tableId); 30 | TextProtocolBlockingTransport textTrans = new TextProtocolBlockingTransportImpl(getHost(), getPort(), 31 | getUserName(), getPassword(), schemaName, getTransportConfig()); 32 | 33 | textTrans.open(); 34 | try { 35 | List list = readMeta(tableName,tableMapEvent,textTrans); 36 | if(list != null && list.size() > 0) { 37 | tableMeta.getColumnMetaList().addAll(list); 38 | } 39 | } finally { 40 | textTrans.close(); 41 | } 42 | cache.put(fullTableName, tableMeta); 43 | return tableMeta; 44 | } 45 | 46 | protected abstract String getHost(); 47 | protected abstract int getPort(); 48 | protected abstract String getUserName(); 49 | protected abstract String getPassword(); 50 | protected abstract TransportConfig getTransportConfig(); 51 | 52 | protected abstract List readMeta(String tableName,TableMapEvent tableMapEvent, TextProtocolBlockingTransport textTrans); 53 | } 54 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/BinlogContext.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.event.FormatDescriptionEvent; 7 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.event.RotateEvent; 8 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.event.TableMapEvent; 9 | 10 | /** 11 | * 在binlog数据同步时,当前session的全局信息存储 12 | * 13 | * @author hexiufeng 14 | * 15 | */ 16 | public class BinlogContext { 17 | /** 18 | * 表元数据的提供者,提供表字段、类型等 19 | */ 20 | private TableMetaProvider tableMetaProvider; 21 | /** 22 | * 日志翻滚事件,用于描述当前或者将要正在发送binlog文件名称及position 23 | */ 24 | private RotateEvent rotateEvent; 25 | /** 26 | * 当前binlog的事件格式描述事件,在执行同步命令后,mysql server 首先会发送Rotate event,之后会发送本事件。 27 | * 在整个sesition中只有这一个事件,其中的eventTypeHeaderLenArray属性会被用到 28 | */ 29 | private FormatDescriptionEvent foramtDescEvent; 30 | /** 31 | * 当前事务的数据包所属表的元数据描述事件,不同的事务或者不同表的数据来临时,会有不同的 TableMapEvent发送过来,在这里缓存,用于后续数据的解析 32 | */ 33 | // private TableMapEvent tableMapEvent; 34 | 35 | private final Map tableMapEventHolder = new HashMap<>(); 36 | private final Map tableIdMapping = new HashMap<>(); 37 | 38 | 39 | 40 | public TableMetaProvider getTableMetaProvider() { 41 | return tableMetaProvider; 42 | } 43 | 44 | public FormatDescriptionEvent getForamtDescEvent() { 45 | return foramtDescEvent; 46 | } 47 | 48 | public void putCurrentTableMapEvent(TableMapEvent tableMapEvent) { 49 | TableMapEvent old = tableMapEventHolder.get(tableMapEvent.getFullTableName()); 50 | if(old != null && old.getTableId() != tableMapEvent.getTableId()){ 51 | tableIdMapping.remove(old.getTableId()); 52 | } 53 | tableMapEventHolder.put(tableMapEvent.getFullTableName(), tableMapEvent); 54 | tableIdMapping.put(tableMapEvent.getTableId(), tableMapEvent); 55 | } 56 | 57 | public TableMapEvent getTableMapEventByTableId(long tableId){ 58 | return tableIdMapping.get(tableId); 59 | } 60 | 61 | public TableMapEvent getTableMapEventByFullTableName(String fullTableName){ 62 | return tableMapEventHolder.get(fullTableName); 63 | } 64 | 65 | public void setTableMetaProvider(TableMetaProvider tableMetaProvider) { 66 | this.tableMetaProvider = tableMetaProvider; 67 | } 68 | 69 | public void setForamtDescEvent(FormatDescriptionEvent foramtDescEvent) { 70 | this.foramtDescEvent = foramtDescEvent; 71 | } 72 | 73 | 74 | 75 | public RotateEvent getRotateEvent() { 76 | return rotateEvent; 77 | } 78 | 79 | public void setRotateEvent(RotateEvent rotateEvent) { 80 | this.rotateEvent = rotateEvent; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/BinlogEvent.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.Response; 4 | 5 | /** 6 | * 描述binlog事件的接口。binlog eventheader中提供当前事件的binlog位置和事件写入时间, 7 | * 这些信息会被应用方使用,尤其是调试时,非常有用,因此Binlog事件中需要携带这些信息。 8 | * binlog pos和Rotate 时间的binlog file name可以唯一确定当前事件在binlog file中的位置 9 | * 10 | * @author hexiufeng 11 | * 12 | */ 13 | public interface BinlogEvent extends Response { 14 | /** 15 | * 获取当前事件在binlog file中的位置 16 | * 17 | * @return 当前事件在binlog file中的位置 18 | */ 19 | long getBinlogEventPos(); 20 | /** 21 | * 设置事件发生的时间 22 | * 23 | * @param occurTime 事件发生的时间 24 | */ 25 | void acceptOccurTime(long occurTime); 26 | /** 27 | * 获取当前事件发生的时间 28 | * @return 29 | */ 30 | long getOccurTime(); 31 | } 32 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/BinlogEventHeader.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 4 | import com.hiriver.unbiz.mysql.lib.protocol.Response; 5 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 6 | 7 | /** 8 | * 9 | * binlogEvent header,只支持 binlog verson 4 10 | * 11 | * @author hexiufeng 12 | * 13 | */ 14 | public class BinlogEventHeader extends AbstractBinlogResponse implements Response { 15 | private int timestamp; // 4 bytes 16 | private int eventType; // 1 bytes 17 | private int serverId; // 4 bytes mysql server id 18 | private int eventSize; // 4 bytes 19 | private int logPos; // 4 bytes for binlog version >1 20 | private int flags; // 2 bytes for binlog version >1 21 | 22 | public int getRestContentLen() { 23 | return this.eventSize - getHeaderSize(); 24 | } 25 | 26 | public int getHeaderSize() { 27 | return 19; 28 | } 29 | 30 | @Override 31 | public void parse(byte[] buf, Position pos) { 32 | this.timestamp = MysqlNumberUtils.read4Int(buf, pos); 33 | this.eventType = MysqlNumberUtils.read1Int(buf, pos); 34 | this.serverId = MysqlNumberUtils.read4Int(buf, pos); 35 | this.eventSize = MysqlNumberUtils.read4Int(buf, pos); 36 | this.logPos = MysqlNumberUtils.read4Int(buf, pos); 37 | this.flags = MysqlNumberUtils.read2Int(buf, pos); 38 | } 39 | 40 | public int getTimestamp() { 41 | return timestamp; 42 | } 43 | 44 | public int getEventType() { 45 | return eventType; 46 | } 47 | 48 | public int getServerId() { 49 | return serverId; 50 | } 51 | 52 | public int getLogPos() { 53 | return logPos; 54 | } 55 | 56 | public int getFlags() { 57 | return flags; 58 | } 59 | 60 | public void setTimestamp(int timestamp) { 61 | this.timestamp = timestamp; 62 | } 63 | 64 | public void setEventType(int eventType) { 65 | this.eventType = eventType; 66 | } 67 | 68 | public void setServerId(int serverId) { 69 | this.serverId = serverId; 70 | } 71 | 72 | public int getEventSize() { 73 | return eventSize; 74 | } 75 | 76 | public void setEventSize(int eventSize) { 77 | this.eventSize = eventSize; 78 | } 79 | 80 | public void setLogPos(int logPos) { 81 | this.logPos = logPos; 82 | } 83 | 84 | public void setFlags(int flags) { 85 | this.flags = flags; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/BinlogEventType.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog; 2 | 3 | /** 4 | * mysql binlog所支持的binlog事件,copy在mysql的c源码 5 | * 6 | * @author hexiufeng 7 | * 8 | */ 9 | public interface BinlogEventType { 10 | int UNKNOWN_EVENT = 0x00; 11 | 12 | int START_EVENT_V3 = 0x01; 13 | 14 | int QUERY_EVENT = 0x02; 15 | 16 | int STOP_EVENT = 0x03; 17 | 18 | int ROTATE_EVENT = 0x04; 19 | 20 | int INTVAR_EVENT = 0x05; 21 | 22 | int LOAD_EVENT = 0x06; 23 | 24 | int SLAVE_EVENT = 0x07; 25 | 26 | int CREATE_FILE_EVENT = 0x08; 27 | 28 | int APPEND_BLOCK_EVENT = 0x09; 29 | 30 | int EXEC_LOAD_EVENT = 0x0a; 31 | 32 | int DELETE_FILE_EVENT = 0x0b; 33 | 34 | int NEW_LOAD_EVENT = 0x0c; 35 | 36 | int RAND_EVENT = 0x0d; 37 | 38 | int USER_VAR_EVENT = 0x0e; 39 | 40 | int FORMAT_DESCRIPTION_EVENT = 0x0f; 41 | 42 | int XID_EVENT = 0x10; 43 | 44 | int BEGIN_LOAD_QUERY_EVENT = 0x11; 45 | 46 | int EXECUTE_LOAD_QUERY_EVENT = 0x12; 47 | 48 | int TABLE_MAP_EVENT = 0x13; 49 | 50 | int WRITE_ROWS_EVENTv0 = 0x14; 51 | 52 | int UPDATE_ROWS_EVENTv0 = 0x15; 53 | 54 | int DELETE_ROWS_EVENTv0 = 0x16; 55 | 56 | int WRITE_ROWS_EVENTv1 = 0x17; 57 | 58 | int UPDATE_ROWS_EVENTv1 = 0x18; 59 | 60 | int DELETE_ROWS_EVENTv1 = 0x19; 61 | 62 | int INCIDENT_EVENT = 0x1a; 63 | 64 | int HEARTBEAT_EVENT = 0x1b; 65 | 66 | int IGNORABLE_EVENT = 0x1c; 67 | 68 | int ROWS_QUERY_EVENT = 0x1d; 69 | 70 | int WRITE_ROWS_EVENTv2 = 0x1e; 71 | 72 | int UPDATE_ROWS_EVENTv2 = 0x1f; 73 | 74 | int DELETE_ROWS_EVENTv2 = 0x20; 75 | 76 | int GTID_EVENT = 0x21; 77 | 78 | int ANONYMOUS_GTID_EVENT = 0x22; 79 | 80 | int PREVIOUS_GTIDS_EVENT = 0x23; 81 | } 82 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/BinlogFileBinlogPosition.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.Request; 4 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.extra.BinlogPosition; 5 | 6 | /** 7 | * binlog file name + offset的实现,一般用于mysql5.6.9之前的版本,之后的版本请 使用{@link GTidBinlogPosition} 8 | * 9 | * @author hexiufeng 10 | * 11 | */ 12 | public class BinlogFileBinlogPosition implements BinlogPosition { 13 | private long pos; 14 | private String binlogFileName; 15 | 16 | public BinlogFileBinlogPosition() { 17 | 18 | } 19 | 20 | public BinlogFileBinlogPosition(String binlogFileName, long pos) { 21 | this.pos = pos; 22 | this.binlogFileName = binlogFileName; 23 | } 24 | 25 | @Override 26 | public Request packetDumpRequest(int serverId) { 27 | DumpRequest request = new DumpRequest(pos, serverId, binlogFileName); 28 | return request; 29 | } 30 | 31 | @Override 32 | public byte[] toBytesArray() { 33 | return toString().getBytes(); 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return binlogFileName + ":" + pos; 39 | } 40 | 41 | @Override 42 | public boolean isSame(BinlogPosition posStore) { 43 | if (!(posStore instanceof BinlogFileBinlogPosition)) { 44 | return false; 45 | } 46 | return toString().equals(posStore.toString()); 47 | } 48 | 49 | public long getPos() { 50 | return pos; 51 | } 52 | 53 | public String getBinlogFileName() { 54 | return binlogFileName; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/DumpRequest.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.AbstractRequest; 4 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 5 | import com.hiriver.unbiz.mysql.lib.protocol.tool.SafeByteArrayOutputStream; 6 | import com.hiriver.unbiz.mysql.lib.protocol.tool.StringTool; 7 | 8 | /** 9 | * 基于binlog file name + offset的dump binlog指令实现,适用于COM_BINLOG_DUMP指令 10 | * 11 | * @author hexiufeng 12 | * 13 | */ 14 | public class DumpRequest extends AbstractRequest { 15 | private final long pos; 16 | private final int serverId; 17 | private final String binlogFileName; 18 | 19 | public DumpRequest(long pos, int serverId, String binlogFileName) { 20 | this.pos = pos; 21 | this.serverId = serverId; 22 | this.binlogFileName = binlogFileName; 23 | } 24 | 25 | @Override 26 | protected void fillPayload(SafeByteArrayOutputStream out) { 27 | out.write(0x12); 28 | out.safeWrite(MysqlNumberUtils.write4Int((int) pos)); 29 | out.safeWrite(MysqlNumberUtils.writeNInt(2, 2)); 30 | out.safeWrite(MysqlNumberUtils.write4Int(serverId)); 31 | out.safeWrite(StringTool.safeConvertString2Bytes(binlogFileName)); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/GTidBinlogPosition.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog; 2 | 3 | import java.util.Map; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import com.hiriver.unbiz.mysql.lib.protocol.Request; 9 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.extra.BinlogPosition; 10 | 11 | /** 12 | * 基于gtid的同步点实现,适用于mysql5.6.9之后的版本 13 | * 14 | * @author hexiufeng 15 | * 16 | */ 17 | public class GTidBinlogPosition implements BinlogPosition { 18 | private static final Logger LOGGER = LoggerFactory.getLogger(GTidBinlogPosition.class); 19 | private GtIdSet gtidset; 20 | 21 | public GTidBinlogPosition() { 22 | } 23 | 24 | public GTidBinlogPosition(String gtIdSetString) { 25 | this.gtidset = new GtIdSet(gtIdSetString); 26 | } 27 | 28 | @Override 29 | public Request packetDumpRequest(int serverId) { 30 | if (LOGGER.isInfoEnabled()) { 31 | LOGGER.info("dump binlog use gtid set {}.", gtidset); 32 | } 33 | GTidDumpRequest request = new GTidDumpRequest(gtidset.getGtidMap(), serverId); 34 | return request; 35 | } 36 | 37 | @Override 38 | public byte[] toBytesArray() { 39 | return toString().getBytes(); 40 | } 41 | 42 | public GtIdSet getGtidset() { 43 | return gtidset; 44 | } 45 | 46 | public void setGtidset(GtIdSet gtidset) { 47 | this.gtidset = gtidset; 48 | } 49 | 50 | public GTidBinlogPosition fixConfPos() { 51 | Map gtIdMap = gtidset.cloneGtIdMap(); 52 | StringBuilder sb = new StringBuilder(); 53 | int count = 0; 54 | for (String uuid : gtIdMap.keySet()) { 55 | if (count < gtIdMap.size() - 1) { 56 | sb.append(gtIdMap.get(uuid).cloneNextGtId().toString()); 57 | } else { 58 | sb.append(gtIdMap.get(uuid).cloneGtId().toString()); 59 | } 60 | sb.append(","); 61 | count++; 62 | } 63 | return new GTidBinlogPosition(sb.substring(0, sb.length() - 1)); 64 | } 65 | 66 | @Override 67 | public String toString() { 68 | return gtidset.toString(); 69 | } 70 | 71 | @Override 72 | public boolean isSame(BinlogPosition posStore) { 73 | if (!(posStore instanceof GTidBinlogPosition)) { 74 | return false; 75 | } 76 | return toString().equals(posStore.toString()); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/GTidDumpRequest.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog; 2 | 3 | import java.util.Map; 4 | 5 | import com.hiriver.unbiz.mysql.lib.protocol.AbstractRequest; 6 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 7 | import com.hiriver.unbiz.mysql.lib.protocol.tool.GTSidTool; 8 | import com.hiriver.unbiz.mysql.lib.protocol.tool.SafeByteArrayOutputStream; 9 | 10 | /** 11 | * 基于gtid的dump指令实现,适用于COM_BINLOG_DUMP_GTID指令 12 | * 13 | * @author hexiufeng 14 | * 15 | */ 16 | public class GTidDumpRequest extends AbstractRequest { 17 | private final int serverId; 18 | private final Map gtidInfoMap; 19 | 20 | public GTidDumpRequest(Map gtidInfoMap, int serverId) { 21 | this.gtidInfoMap = gtidInfoMap; 22 | this.serverId = serverId; 23 | } 24 | 25 | @Override 26 | protected void fillPayload(SafeByteArrayOutputStream out) { 27 | out.write(0x1e); // command 28 | out.safeWrite(MysqlNumberUtils.writeNInt(0x04, 2)); // flag 29 | out.safeWrite(MysqlNumberUtils.writeNInt(serverId, 4)); // server id 30 | out.safeWrite(MysqlNumberUtils.writeNInt(4, 4)); // binlog name size 31 | out.safeWrite(MysqlNumberUtils.writeNInt(0, 4)); // binlog name 32 | out.safeWrite(MysqlNumberUtils.writeNLong(4L, 8)); // binlog_pos 33 | 34 | out.safeWrite(MysqlNumberUtils.writeNInt(calDataLen(gtidInfoMap.size()), 4)); // datalen 35 | 36 | out.safeWrite(MysqlNumberUtils.writeNLong(gtidInfoMap.size(), 8)); // n_sids 37 | 38 | for (String uuid : gtidInfoMap.keySet()) { 39 | out.safeWrite(GTSidTool.convertSidString2DumpFormatBytes(uuid)); // sid 40 | GtId gi = gtidInfoMap.get(uuid); 41 | 42 | out.safeWrite(MysqlNumberUtils.writeNLong(1L, 8)); // n_intervals 43 | out.safeWrite(MysqlNumberUtils.writeNLong(gi.getInternel().getStart(), 8)); // start 44 | out.safeWrite(MysqlNumberUtils.writeNLong(gi.getInternel().getStop()==1?2:gi.getInternel().getStop(), 8)); // stop 45 | } 46 | } 47 | 48 | private int calDataLen(int nSids) { 49 | return nSids * 40 + 8; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/GtId.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog; 2 | 3 | import java.util.UUID; 4 | 5 | /** 6 | * 单个gtid,uuid:[12-]45 7 | * 8 | * @author hexiufeng 9 | * 10 | */ 11 | public class GtId { 12 | private final String uuid; 13 | private GtIdInterval internel; 14 | 15 | public void setInternel(GtIdInterval internel) { 16 | this.internel = internel; 17 | } 18 | 19 | public GtId(String uuid, long start, long stop) { 20 | this.uuid = uuid; 21 | this.internel = new GtIdInterval(start,stop); 22 | } 23 | 24 | /** 25 | * 构造器 26 | * 27 | * @param gtidInfoString uuid:[3-]12 28 | */ 29 | public GtId(String gtidInfoString) { 30 | String[] array = gtidInfoString.split(":"); 31 | uuid = array[0]; 32 | UUID.fromString(uuid); 33 | this.internel = new GtIdInterval(array[1]); 34 | } 35 | 36 | public String getUuid() { 37 | return uuid; 38 | } 39 | 40 | 41 | public GtIdInterval getInternel() { 42 | return internel; 43 | } 44 | 45 | @Override 46 | public String toString(){ 47 | return this.uuid + ":" + internel.toShortString(); 48 | } 49 | 50 | 51 | public GtId cloneGtId(){ 52 | return new GtId(uuid,internel.getStart(),internel.getStop()); 53 | } 54 | 55 | public GtId cloneNextGtId(){ 56 | return new GtId(uuid,internel.getStart(),internel.getStop() + 1L); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/GtIdInterval.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog; 2 | 3 | /** 4 | * 描述gtid的事务段 5 | * 6 | * @author hexiufeng 7 | * 8 | */ 9 | public class GtIdInterval { 10 | private final long start; 11 | private final long stop; 12 | 13 | public GtIdInterval(String internelString) { 14 | long end = 0; 15 | if (internelString.indexOf('-') >= 0) { 16 | String[] numPosArray = internelString.split("-"); 17 | this.start = Long.parseLong(numPosArray[0]); 18 | end = Long.parseLong(numPosArray[1]); 19 | } else { 20 | this.start = 1L; 21 | end = Long.parseLong(internelString); 22 | } 23 | // if (end == 1L) { 24 | // end++; 25 | // } 26 | this.stop = end; 27 | } 28 | 29 | public GtIdInterval(long start, long stop) { 30 | this.start = start; 31 | this.stop = stop; 32 | } 33 | 34 | public long getStart() { 35 | return start; 36 | } 37 | 38 | public long getStop() { 39 | return stop; 40 | } 41 | 42 | public String toShortString() { 43 | return "" + stop; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/GtIdSet.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog; 2 | 3 | import java.util.Collections; 4 | import java.util.LinkedHashMap; 5 | import java.util.Map; 6 | 7 | /** 8 | * gtid set 描述 9 | * 10 | * @author hexiufeng 11 | * 12 | */ 13 | public class GtIdSet { 14 | private final Map gtidMap = new LinkedHashMap<>(); 15 | 16 | /** 17 | * uuid:[2-]13,uuid:[3-]19 18 | * 19 | * @param gtIdSetString gtid st string 20 | */ 21 | public GtIdSet(String gtIdSetString) { 22 | gtIdSetString = gtIdSetString.replace("\n", "").replace(" ", ""); 23 | String[] gtidArray = gtIdSetString.split(","); 24 | for (String gtid : gtidArray) { 25 | GtId gi = new GtId(gtid); 26 | if (gtidMap.containsKey(gi.getUuid())) { 27 | throw new RuntimeException("uuid must be unique."); 28 | } 29 | gtidMap.put(gi.getUuid(), gi); 30 | } 31 | } 32 | 33 | @Override 34 | public String toString() { 35 | StringBuilder sb = new StringBuilder(); 36 | for (String uuid : this.gtidMap.keySet()) { 37 | GtId gi = gtidMap.get(uuid); 38 | sb.append(gi.toString()); 39 | sb.append(","); 40 | } 41 | return sb.substring(0, sb.length() - 1); 42 | } 43 | 44 | public Map getGtidMap() { 45 | return Collections.unmodifiableMap(gtidMap); 46 | } 47 | 48 | public Map cloneGtIdMap() { 49 | Map clone = new LinkedHashMap<>(); 50 | for (String uuid : gtidMap.keySet()) { 51 | clone.put(uuid, gtidMap.get(uuid).cloneGtId()); 52 | } 53 | return clone; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/InternelColumnDefinition.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog; 2 | 3 | import com.hiriver.unbiz.mysql.lib.ColumnType; 4 | import com.hiriver.unbiz.mysql.lib.protocol.tool.GenericStringTypeChecker; 5 | 6 | /** 7 | * 内部使用的表的列定义 8 | * 9 | * @author hexiufeng 10 | * 11 | */ 12 | public class InternelColumnDefinition { 13 | private ColumnType columnType; 14 | /** 15 | * 主从复制协议中定义的每个字段的元数据,TableMapEvent会定义每一列的元数据, 16 | * 而元数据所占的长度跟类型有关,参见http://dev.mysql.com/doc/internals/en/table-map-event.html 17 | */ 18 | private int meta; 19 | private boolean isNull; 20 | 21 | /** 22 | * 该字段是否是枚举或者set 23 | */ 24 | private boolean enumOrSet; 25 | 26 | public InternelColumnDefinition(ColumnType columnType, int meta, boolean isNull) { 27 | this.columnType = columnType; 28 | this.meta = meta; 29 | this.isNull = isNull; 30 | // 枚举或set在内部也用string表示,需要区分开来 31 | if(columnType == ColumnType.MYSQL_TYPE_STRING){ 32 | int[] lenghHolder = {0}; 33 | ColumnType realType = GenericStringTypeChecker.checkRealColumnType(meta,lenghHolder); 34 | if(realType == ColumnType.MYSQL_TYPE_ENUM || realType == ColumnType.MYSQL_TYPE_SET){ 35 | enumOrSet = true; 36 | } 37 | } 38 | } 39 | 40 | public ColumnType getColumnType() { 41 | return columnType; 42 | } 43 | 44 | public int getMeta() { 45 | return meta; 46 | } 47 | 48 | public boolean isNull() { 49 | return isNull; 50 | } 51 | 52 | public boolean isEnumOrSet(){ 53 | return enumOrSet; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/RegisterRequest.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.AbstractRequest; 4 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 5 | import com.hiriver.unbiz.mysql.lib.protocol.tool.SafeByteArrayOutputStream; 6 | 7 | /** 8 | * 注册从库到主库请求指令,实现COM_REGISTER_SLAVE 9 | * 10 | * @author hexiufeng 11 | * 12 | */ 13 | public class RegisterRequest extends AbstractRequest { 14 | private final int serverId; 15 | 16 | public RegisterRequest(int serverId) { 17 | this.serverId = serverId; 18 | } 19 | 20 | @Override 21 | protected void fillPayload(SafeByteArrayOutputStream out) { 22 | out.write(0x15); 23 | out.safeWrite(MysqlNumberUtils.write4Int(serverId)); 24 | out.write(0); 25 | out.write(0); 26 | out.write(0); 27 | out.safeWrite(MysqlNumberUtils.writeNInt(0, 2)); 28 | out.safeWrite(MysqlNumberUtils.write4Int(0)); 29 | out.safeWrite(MysqlNumberUtils.write4Int(0)); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/TableMeta.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.hiriver.unbiz.mysql.lib.output.ColumnDefinition; 7 | 8 | /** 9 | * table的元数据描述,主要包含字段的定义,包括字段名称,类型、是否为主键等 10 | * 11 | * @author hexiufeng 12 | * 13 | */ 14 | public class TableMeta { 15 | private final List columnMetaList = new ArrayList(); 16 | /** 17 | * tableid,mysql主从复制中的概念,标示一个表的唯一id,确切的说是mysql server缓存中针对每一个表分配的id,同一个表的id会变化.
18 | *
    19 | *
  • 当mysql缓存刷新时,一个的表的id会发生变化
  • 20 | *
  • 当表结构修改是,tableid会发生变化
  • 21 | *
22 | */ 23 | private final long tableId; 24 | 25 | public TableMeta(long tableId) { 26 | this.tableId = tableId; 27 | } 28 | 29 | public void addColumn(ColumnDefinition meta) { 30 | this.columnMetaList.add(meta); 31 | } 32 | 33 | public List getColumnMetaList() { 34 | return columnMetaList; 35 | } 36 | 37 | public ColumnDefinition getColumnDefinition(int index) { 38 | return this.columnMetaList.get(index); 39 | } 40 | 41 | public long getTableId() { 42 | return tableId; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/TableMetaProvider.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.event.TableMapEvent; 4 | 5 | /** 6 | * 表的元数据提供者,缺省实现是从mysql server读取。 7 | * 8 | * @author hexiufeng 9 | * 10 | */ 11 | public interface TableMetaProvider { 12 | /** 13 | * 根据表的id和表名称获取表元数据 14 | * 15 | * @param tableId tableid 16 | * @param tableMapEvent tableMapEvent 17 | * @return 表的元数据 18 | */ 19 | TableMeta getTableMeta(long tableId, TableMapEvent tableMapEvent); 20 | } 21 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/TimestampBinlogPosition.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.Request; 4 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.extra.BinlogPosition; 5 | 6 | /** 7 | *
  8 |  * 基于时间戳的位点,包含时间戳、mysql server uuid、binlog文件名称、文件内偏移量 4个字段;
  9 |  * 有效格式包含如下3种
 10 |  *     1543374986
 11 |  *     1543374986:::
 12 |  *     1543374686:a20181c1-7edb-11e6-8e14-90e2bac75fc4:mysql-bin.000178:515628788
 13 |  * 即或者只有时间戳或者有全部4个字段
 14 |  * 
15 | * created by Yang Huawei (xander.yhw@alibaba-inc.com) on 2018/8/29 22:23 16 | */ 17 | public class TimestampBinlogPosition implements BinlogPosition { 18 | /** 19 | * unix时间戳,单位秒 20 | */ 21 | private final long timestamp; 22 | private final String serverUuid; 23 | private final String binlogFileName; 24 | private final Long pos; 25 | 26 | 27 | public TimestampBinlogPosition(long timestamp) { 28 | this(timestamp, null, null, null); 29 | } 30 | 31 | public TimestampBinlogPosition(long timestamp, String serverUuid, String binlogFileName, 32 | Long pos) { 33 | this.timestamp = timestamp; 34 | this.serverUuid = serverUuid; 35 | this.binlogFileName = binlogFileName; 36 | this.pos = pos; 37 | } 38 | 39 | @Override 40 | public Request packetDumpRequest(int serverId) { 41 | return new BinlogFileBinlogPosition(this.binlogFileName, this.pos).packetDumpRequest(serverId); 42 | } 43 | 44 | @Override 45 | public byte[] toBytesArray() { 46 | return toString().getBytes(); 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | return timestamp + ":" + (serverUuid == null ? "" : serverUuid) + ":" 52 | + (binlogFileName == null ? "" : binlogFileName) + ":" + (pos == null ? "" : pos); 53 | } 54 | 55 | @Override 56 | public boolean isSame(BinlogPosition pos) { 57 | return this.equals(pos); 58 | } 59 | 60 | 61 | public long getTimestamp() { 62 | return timestamp; 63 | } 64 | 65 | public String getServerUuid() { 66 | return serverUuid; 67 | } 68 | 69 | public Long getPos() { 70 | return pos; 71 | } 72 | 73 | public String getBinlogFileName() { 74 | return binlogFileName; 75 | } 76 | 77 | 78 | @Override 79 | public boolean equals(Object o) { 80 | if (this == o) 81 | return true; 82 | if (o == null || getClass() != o.getClass()) 83 | return false; 84 | 85 | TimestampBinlogPosition that = (TimestampBinlogPosition) o; 86 | 87 | if (timestamp != that.timestamp) 88 | return false; 89 | if (serverUuid != null ? !serverUuid.equals(that.serverUuid) : that.serverUuid != null) 90 | return false; 91 | if (binlogFileName != null ? !binlogFileName.equals(that.binlogFileName) 92 | : that.binlogFileName != null) 93 | return false; 94 | return pos != null ? pos.equals(that.pos) : that.pos == null; 95 | } 96 | 97 | @Override 98 | public int hashCode() { 99 | int result = (int) (timestamp ^ (timestamp >>> 32)); 100 | result = 31 * result + (serverUuid != null ? serverUuid.hashCode() : 0); 101 | result = 31 * result + (binlogFileName != null ? binlogFileName.hashCode() : 0); 102 | result = 31 * result + (pos != null ? pos.hashCode() : 0); 103 | return result; 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/ValidBinlogOutput.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.event.BaseRowEvent; 4 | 5 | /** 6 | * 描述有效的binlog 数据 event。有效的数据evetn包括:
7 | * 8 | *
    9 | *
  • Insert/update/delete row event
  • 10 | *
  • GTId event
  • 11 | *
  • XidEvent
  • 12 | *
13 | * 14 | * @author hexiufeng 15 | * 16 | */ 17 | public class ValidBinlogOutput { 18 | /** 19 | * 有效事件 20 | */ 21 | private final BinlogEvent event; 22 | /** 23 | * binlog文件名称,配合event中的pos可以确定当前事件在binlog file中的位置 24 | */ 25 | private final String binlogFileName; 26 | /** 27 | * 当前事件的类型 28 | */ 29 | private final ValidEventType eventType; 30 | 31 | private String serverUuid; 32 | 33 | public ValidBinlogOutput(BinlogEvent event,String binlogFileName,ValidEventType eventType) { 34 | this.event = event; 35 | this.binlogFileName = binlogFileName; 36 | this.eventType = eventType; 37 | } 38 | 39 | public ValidEventType getEventType() { 40 | return eventType; 41 | } 42 | 43 | public String getBinlogFileName() { 44 | return binlogFileName; 45 | } 46 | 47 | /** 48 | * 当前事件是否是行数据事件 49 | * 50 | * @return boolean 51 | */ 52 | public boolean isRowEvent() { 53 | return eventType == ValidEventType.ROW; 54 | } 55 | public BaseRowEvent getRowEvent(){ 56 | return (BaseRowEvent)event; 57 | } 58 | public BinlogEvent getEvent() { 59 | return event; 60 | } 61 | 62 | public String getEventBinlogPos(){ 63 | return binlogFileName + ":" + getEvent().getBinlogEventPos(); 64 | } 65 | 66 | public String getServerUuid() { 67 | return serverUuid; 68 | } 69 | 70 | public void setServerUuid(String serverUuid) { 71 | this.serverUuid = serverUuid; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/ValidEventType.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog; 2 | 3 | /** 4 | * 描述有效事件的类型。
5 | * 6 | *
    7 | *
  • 开始事务
  • 8 | *
  • 事务提交
  • 9 | *
  • 事务回滚
  • 10 | *
  • 描述GTID的事件
  • 11 | *
  • 行数据事件
  • 12 | *
13 | * 14 | * @author hexiufeng 15 | * 16 | */ 17 | public enum ValidEventType { 18 | TRAN_BEGIN,TRANS_COMMIT,TRANS_ROLLBACK,GTID,ROW; 19 | } 20 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/event/AbstractBinlogEvent.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog.event; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.AbstractBinlogResponse; 4 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.BinlogEvent; 5 | 6 | /** 7 | * 抽象的binlog 事件描述 8 | * 9 | * @author hexiufeng 10 | * 11 | */ 12 | public abstract class AbstractBinlogEvent extends AbstractBinlogResponse implements BinlogEvent { 13 | /** 14 | * 该事件在binlog file内部的位置 15 | */ 16 | private final long eventBinlogPos; 17 | /** 18 | * 该数据写入mysql的事件戳,一般用于检测数据同步的性能 19 | */ 20 | private long occurTime; 21 | /** 22 | * mysql日志是否支持校验 23 | */ 24 | private final boolean hasCheckSum; 25 | 26 | protected AbstractBinlogEvent(long eventBinlogPos, boolean hasCheckSum) { 27 | this.eventBinlogPos = eventBinlogPos; 28 | this.hasCheckSum = hasCheckSum; 29 | } 30 | 31 | public boolean isHasCheckSum() { 32 | return hasCheckSum; 33 | } 34 | 35 | @Override 36 | public long getBinlogEventPos() { 37 | return eventBinlogPos; 38 | } 39 | 40 | @Override 41 | public void acceptOccurTime(long occurTime) { 42 | this.occurTime = occurTime; 43 | } 44 | 45 | @Override 46 | public long getOccurTime() { 47 | return occurTime; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/event/EventFactory.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog.event; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.BinlogContext; 4 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.BinlogEvent; 5 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.BinlogEventType; 6 | 7 | /** 8 | * 根据事件类型产生事件对象的工厂 9 | * 10 | * @author hexiufeng 11 | * 12 | */ 13 | public class EventFactory { 14 | public static BinlogEvent factory(final int eventType, final long binlogEventPos, final BinlogContext context, 15 | boolean hasCheckSum) { 16 | switch (eventType) { 17 | case BinlogEventType.FORMAT_DESCRIPTION_EVENT: 18 | return new FormatDescriptionEvent(binlogEventPos, hasCheckSum); 19 | case BinlogEventType.GTID_EVENT: 20 | return new GTidEvent(binlogEventPos, hasCheckSum); 21 | case BinlogEventType.QUERY_EVENT: 22 | return new QueryEvent(binlogEventPos, hasCheckSum); 23 | case BinlogEventType.ROTATE_EVENT: 24 | return new RotateEvent(binlogEventPos, hasCheckSum); 25 | case BinlogEventType.WRITE_ROWS_EVENTv0: 26 | case BinlogEventType.UPDATE_ROWS_EVENTv0: 27 | case BinlogEventType.DELETE_ROWS_EVENTv0: 28 | return new RowEventV0(context, context.getTableMetaProvider(), eventType, 29 | binlogEventPos, hasCheckSum); 30 | case BinlogEventType.WRITE_ROWS_EVENTv1: 31 | case BinlogEventType.UPDATE_ROWS_EVENTv1: 32 | case BinlogEventType.DELETE_ROWS_EVENTv1: 33 | return new RowEventV1(context, context.getTableMetaProvider(), eventType, 34 | binlogEventPos, hasCheckSum); 35 | case BinlogEventType.WRITE_ROWS_EVENTv2: 36 | case BinlogEventType.UPDATE_ROWS_EVENTv2: 37 | case BinlogEventType.DELETE_ROWS_EVENTv2: 38 | return new RowEventV2(context, context.getTableMetaProvider(), eventType, 39 | binlogEventPos, hasCheckSum); 40 | case BinlogEventType.STOP_EVENT: 41 | return new StopEvent(binlogEventPos, hasCheckSum); 42 | case BinlogEventType.TABLE_MAP_EVENT: 43 | return new TableMapEvent(context.getForamtDescEvent(), binlogEventPos, hasCheckSum); 44 | case BinlogEventType.XID_EVENT: 45 | return new XidEvent(binlogEventPos, hasCheckSum); 46 | // case BinlogEventType.PREVIOUS_GTIDS_EVENT: 47 | // return new GTidEvent(binlogEventPos); 48 | default: 49 | return new UnkonwnEvent(binlogEventPos, hasCheckSum); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/event/FormatDescriptionEvent.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog.event; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 4 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.BinlogEvent; 5 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 6 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlStringUtils; 7 | 8 | /** 9 | * mysql binlog格式描述事件,在mysql开始dump指令后,第二个发送的事件,一次dump指令只发送一次该事件,第一个事件是Rotate event 10 | * 11 | * @author hexiufeng 12 | * 13 | */ 14 | public class FormatDescriptionEvent extends AbstractBinlogEvent implements BinlogEvent { 15 | 16 | private int binlogVersion; // 2 bytes 17 | private String mysqlSeverVersion; // 50 bytes 18 | private long createStamp; // 4 bytes, unix stamp 19 | private int eventHeaderLen; // 1 bytes, should be 19 20 | private byte[] eventTypeHeaderLenArray; // string.eof 21 | 22 | public FormatDescriptionEvent(long eventBinlogPos, boolean hasCheckSum) { 23 | super(eventBinlogPos, hasCheckSum); 24 | } 25 | 26 | @Override 27 | public void parse(byte[] buf, Position pos) { 28 | this.binlogVersion = MysqlNumberUtils.read2Int(buf, pos); 29 | this.mysqlSeverVersion = new String(MysqlStringUtils.readFixString(buf, pos, 50)); 30 | this.createStamp = MysqlNumberUtils.read4Int(buf, pos) & 0xffffffffL; 31 | this.eventHeaderLen = MysqlNumberUtils.read1Int(buf, pos); 32 | this.eventTypeHeaderLenArray = MysqlStringUtils.readEofString(buf, pos, super.isHasCheckSum()); 33 | } 34 | 35 | public int getPostHeaderLen(int eventType) { 36 | return eventTypeHeaderLenArray[eventType - 1] & 0xff; 37 | } 38 | 39 | public int getBinlogVersion() { 40 | return binlogVersion; 41 | } 42 | 43 | public String getMysqlSeverVersion() { 44 | return mysqlSeverVersion; 45 | } 46 | 47 | public long getCreateStamp() { 48 | return createStamp; 49 | } 50 | 51 | public int getEventHeaderLen() { 52 | return eventHeaderLen; 53 | } 54 | 55 | public byte[] getEventTypeHeaderLenArray() { 56 | return eventTypeHeaderLenArray; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/event/GTidEvent.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog.event; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 4 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.BinlogEvent; 5 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 6 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlStringUtils; 7 | import com.hiriver.unbiz.mysql.lib.protocol.tool.GTSidTool; 8 | 9 | /** 10 | * gtid事件,在mysql5.6.9之后添加的事件,描述该事务的唯一id,在一个事务中,第一个被发送的事件 11 | * 12 | * @author hexiufeng 13 | * 14 | */ 15 | public class GTidEvent extends AbstractBinlogEvent implements BinlogEvent { 16 | private boolean commitFlag; 17 | private byte[] sid; 18 | private long gno; 19 | 20 | public GTidEvent(long eventBinlogPos, boolean hasCheckSum) { 21 | super(eventBinlogPos, hasCheckSum); 22 | } 23 | 24 | @Override 25 | public void parse(byte[] buf, Position pos) { 26 | int commitValue = MysqlNumberUtils.read1Int(buf, pos); 27 | commitFlag = commitValue == 1; 28 | sid = MysqlStringUtils.readFixString(buf, pos, 16); 29 | gno = MysqlNumberUtils.read8Int(buf, pos); 30 | } 31 | 32 | public String getGTidString() { 33 | return GTSidTool.convertSid2UUIDString(sid) + ":" + gno; 34 | } 35 | 36 | public boolean isCommitFlag() { 37 | return commitFlag; 38 | } 39 | 40 | public byte[] getSid() { 41 | return sid; 42 | } 43 | 44 | public String getSidString() { 45 | return GTSidTool.convertSid2UUIDString(sid); 46 | } 47 | 48 | public long getGno() { 49 | return gno; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/event/QueryEvent.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog.event; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 4 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.BinlogEvent; 5 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 6 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlStringUtils; 7 | 8 | /** 9 | * 执行sql语句的事件,在binlog中一般保持开启、提交、回滚事件和ddl 10 | * 11 | * @author hexiufeng 12 | * 13 | */ 14 | public class QueryEvent extends AbstractBinlogEvent implements BinlogEvent { 15 | private int slaveProxyId; 16 | private int executionTime; 17 | private int schemaLength; 18 | private int errorCode; 19 | private int statusVarsLength; 20 | private byte[] statusVars; 21 | private String schema; 22 | private String query; 23 | 24 | public QueryEvent(long eventBinlogPos, boolean hasCheckSum) { 25 | super(eventBinlogPos, hasCheckSum); 26 | } 27 | 28 | @Override 29 | public void parse(byte[] buf, Position pos) { 30 | this.slaveProxyId = MysqlNumberUtils.read4Int(buf, pos); 31 | this.executionTime = MysqlNumberUtils.read4Int(buf, pos); 32 | this.schemaLength = MysqlNumberUtils.read1Int(buf, pos); 33 | this.errorCode = MysqlNumberUtils.read2Int(buf, pos); 34 | this.statusVarsLength = MysqlNumberUtils.read2Int(buf, pos); 35 | this.statusVars = MysqlStringUtils.readFixString(buf, pos, statusVarsLength); 36 | this.schema = new String(MysqlStringUtils.readFixString(buf, pos, schemaLength)); 37 | pos.forwardPos(); 38 | this.query = new String(MysqlStringUtils.readEofString(buf, pos, super.isHasCheckSum())); 39 | } 40 | 41 | public int getSlaveProxyId() { 42 | return slaveProxyId; 43 | } 44 | 45 | public int getExecutionTime() { 46 | return executionTime; 47 | } 48 | 49 | public int getSchemaLength() { 50 | return schemaLength; 51 | } 52 | 53 | public int getErrorCode() { 54 | return errorCode; 55 | } 56 | 57 | public int getStatusVarsLength() { 58 | return statusVarsLength; 59 | } 60 | 61 | public byte[] getStatusVars() { 62 | return statusVars; 63 | } 64 | 65 | public String getSchema() { 66 | return schema; 67 | } 68 | 69 | public String getQuery() { 70 | return query; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/event/RotateEvent.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog.event; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 4 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.BinlogEvent; 5 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 6 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlStringUtils; 7 | 8 | /** 9 | * 日志翻滚事件,在执行dump指令后,后者开启新日志文件时,都会发送该事件,它可以记录后续事件所在的binlog file name, 10 | * 在非gtid支持的场景下,非常有用,可以用于记录事件所在的位置 11 | * 12 | * @author hexiufeng 13 | * 14 | */ 15 | public class RotateEvent extends AbstractBinlogEvent implements BinlogEvent { 16 | private long position; 17 | private String nextBinlogName; 18 | 19 | public RotateEvent(long eventBinlogPos, boolean hasCheckSum) { 20 | super(eventBinlogPos, hasCheckSum); 21 | } 22 | 23 | @Override 24 | public void parse(byte[] buf, Position pos) { 25 | this.position = MysqlNumberUtils.read8Int(buf, pos); 26 | this.nextBinlogName = new String(MysqlStringUtils.readEofString(buf, pos, super.isHasCheckSum())); 27 | } 28 | 29 | public long getPosition() { 30 | return position; 31 | } 32 | 33 | public String getNextBinlogName() { 34 | return nextBinlogName; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/event/RowEventV0.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog.event; 2 | 3 | import java.util.Collections; 4 | import java.util.List; 5 | 6 | import com.hiriver.unbiz.mysql.lib.output.BinlogColumnValue; 7 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 8 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.BinlogContext; 9 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.BinlogEvent; 10 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.TableMeta; 11 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.TableMetaProvider; 12 | 13 | /** 14 | * 行事件,版本0 15 | * 16 | * @author hexiufeng 17 | * 18 | */ 19 | public class RowEventV0 extends BaseRowEvent implements BinlogEvent { 20 | 21 | public RowEventV0(BinlogContext binlogContext, TableMetaProvider tableMetaProvider, int eventType, 22 | final long eventBinlogPos, boolean hasCheckSum) { 23 | super(binlogContext, tableMetaProvider, eventType, eventBinlogPos, hasCheckSum); 24 | } 25 | 26 | @Override 27 | protected void parseVerPostHeader(byte[] buf, Position pos) { 28 | } 29 | 30 | @Override 31 | protected void parseVerBodyForUpdate(byte[] buf, Position pos) { 32 | // do nothing 33 | } 34 | 35 | @Override 36 | protected List parseVerRowForUpdate(byte[] buf, Position pos, TableMeta tableMeta) { 37 | return Collections.emptyList(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/event/RowEventV1.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog.event; 2 | 3 | import java.util.List; 4 | 5 | import com.hiriver.unbiz.mysql.lib.output.BinlogColumnValue; 6 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 7 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.BinlogContext; 8 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.BinlogEvent; 9 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.TableMeta; 10 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.TableMetaProvider; 11 | 12 | /** 13 | * 行事件,版本2,支持获取update之前的数据。在子header后有2个字节的预留,解析时需要跳过。 mysql文档中在这个地方存在错误 14 | * 15 | * @author hexiufeng 16 | * 17 | */ 18 | public class RowEventV1 extends BaseRowEvent implements BinlogEvent { 19 | private byte[] updateColumnsNotNullBitmap; 20 | 21 | public RowEventV1(BinlogContext binlogContext, TableMetaProvider tableMetaProvider, int eventType, 22 | final long eventBinlogPos, boolean hasCheckSum) { 23 | super(binlogContext, tableMetaProvider, eventType, eventBinlogPos, hasCheckSum); 24 | } 25 | 26 | @Override 27 | protected void parseVerPostHeader(byte[] buf, Position pos) { 28 | } 29 | 30 | @Override 31 | protected void parseVerBodyForUpdate(byte[] buf, Position pos) { 32 | updateColumnsNotNullBitmap = readNotNullBitmap(buf, pos); 33 | } 34 | 35 | @Override 36 | protected List parseVerRowForUpdate(byte[] buf, Position pos, TableMeta tableMeta) { 37 | return super.parseRow(updateColumnsNotNullBitmap, buf, pos, tableMeta); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/event/RowEventV2.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog.event; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 4 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.BinlogContext; 5 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.BinlogEvent; 6 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.TableMetaProvider; 7 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 8 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlStringUtils; 9 | 10 | /** 11 | * 行事件,版本2 12 | * 13 | * @author hexiufeng 14 | * 15 | */ 16 | public class RowEventV2 extends RowEventV1 implements BinlogEvent { 17 | 18 | public RowEventV2(BinlogContext binlogContext, TableMetaProvider tableMetaProvider, int eventType, 19 | final long eventBinlogPos, boolean hasCheckSum) { 20 | super(binlogContext, tableMetaProvider, eventType, eventBinlogPos, hasCheckSum); 21 | } 22 | 23 | @Override 24 | protected void parseVerPostHeader(byte[] buf, Position pos) { 25 | int len = MysqlNumberUtils.read2Int(buf, pos); 26 | MysqlStringUtils.readFixString(buf, pos, len - 2); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/event/StopEvent.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog.event; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 4 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.BinlogEvent; 5 | 6 | /** 7 | * stop event,无用 8 | * 9 | * @author hexiufeng 10 | * 11 | */ 12 | public class StopEvent extends AbstractBinlogEvent implements BinlogEvent { 13 | 14 | public StopEvent(long eventBinlogPos, boolean hasCheckSum) { 15 | super(eventBinlogPos, hasCheckSum); 16 | } 17 | 18 | @Override 19 | public void parse(byte[] buf, Position pos) { 20 | 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/event/UnkonwnEvent.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog.event; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 4 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.BinlogEvent; 5 | 6 | /** 7 | * 未知事件,直接丢弃 8 | * 9 | * @author hexiufeng 10 | * 11 | */ 12 | public class UnkonwnEvent extends AbstractBinlogEvent implements BinlogEvent { 13 | 14 | public UnkonwnEvent(long eventBinlogPos, boolean hasCheckSum) { 15 | super(eventBinlogPos, hasCheckSum); 16 | // TODO Auto-generated constructor stub 17 | } 18 | 19 | @Override 20 | public void parse(byte[] buf, Position pos) { 21 | // do nothing 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/event/XidEvent.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog.event; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 4 | import com.hiriver.unbiz.mysql.lib.protocol.binlog.BinlogEvent; 5 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 6 | 7 | /** 8 | * Xid event,标志着事务的结束 9 | * 10 | * @author hexiufeng 11 | * 12 | */ 13 | public class XidEvent extends AbstractBinlogEvent implements BinlogEvent { 14 | private long xid; 15 | 16 | public XidEvent(long eventBinlogPos, boolean hasCheckSum) { 17 | super(eventBinlogPos, hasCheckSum); 18 | } 19 | 20 | @Override 21 | public void parse(byte[] buf, Position pos) { 22 | this.xid = MysqlNumberUtils.read8Int(buf, pos); 23 | } 24 | 25 | public long getXid() { 26 | return xid; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/exp/FetalParseValueExp.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog.exp; 2 | 3 | /** 4 | * 解析columnvalue失败时抛出的异常 5 | * 6 | * @author hexiufeng 7 | * 8 | */ 9 | public class FetalParseValueExp extends RuntimeException { 10 | 11 | /** 12 | * 13 | */ 14 | private static final long serialVersionUID = 1L; 15 | 16 | public FetalParseValueExp(Exception e){ 17 | super(e); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/exp/InvalidBinlogVer.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog.exp; 2 | 3 | /** 4 | * 无效的binlog版本异常 5 | * 6 | * @author hexiufeng 7 | * 8 | */ 9 | public class InvalidBinlogVer extends RuntimeException { 10 | 11 | /** 12 | * 13 | */ 14 | private static final long serialVersionUID = 1L; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/exp/InvalidColumnType.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog.exp; 2 | 3 | /** 4 | * 无效的列类型异常,当不能识别列类型时抛出该异常 5 | * 6 | * @author hexiufeng 7 | * 8 | */ 9 | public class InvalidColumnType extends RuntimeException { 10 | 11 | /** 12 | * 13 | */ 14 | private static final long serialVersionUID = 1L; 15 | 16 | public InvalidColumnType(String message) { 17 | super(message); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/exp/ReadTimeoutExp.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog.exp; 2 | 3 | /** 4 | * 读取数据超时异常。当没有新数据或者网络抖动时可能被抛出。 5 | * 上层应用应该捕获该异常并试图重新连接mysql 6 | * 7 | * @author hexiufeng 8 | * 9 | */ 10 | public class ReadTimeoutExp extends RuntimeException { 11 | 12 | /** 13 | * 14 | */ 15 | private static final long serialVersionUID = 1L; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/exp/TableAlreadyModifyExp.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog.exp; 2 | 3 | /** 4 | * 表结构已经被修改异常,当接收到的binlog事件描述的列总数大于当前数据库 5 | * 中表的列数时,抛出该异常,表示表结构已经发生变化,将无法匹配到正确的列名 6 | * 7 | * @author hexiufeng 8 | * 9 | */ 10 | public class TableAlreadyModifyExp extends RuntimeException { 11 | 12 | /** 13 | * 14 | */ 15 | private static final long serialVersionUID = 1L; 16 | 17 | public TableAlreadyModifyExp(String message){ 18 | super(message); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/binlog/extra/BinlogPosition.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.binlog.extra; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.Request; 4 | 5 | /** 6 | * 描述mysql binlog的同步点。当一个事务结束时需要记录同步点,当重新同步时可以从该同步点继续同步。
7 | * 8 | *
    9 | *
  • mysql5.6.9之前,同步点是binlog file name + offset
  • 10 | *
  • mysql5.6.9之后,同步点可以是gtid,当从mysql从库复制数据时,当一个从库崩溃,可以自动切换到其他从库,此时 gtid特别有用,可以保证从正确的位置继续复制,但第一种方式不行
  • 11 | *
12 | * 13 | * @author hexiufeng 14 | * 15 | */ 16 | public interface BinlogPosition { 17 | /** 18 | * 转换binlog pos为dump指令 19 | * 20 | * @param serverId 从库的唯一id,当前系统逻辑上就是一个从库 21 | * @return dump指令请求 22 | */ 23 | Request packetDumpRequest(int serverId); 24 | 25 | /** 26 | * 转换成可以存储的二进制流。缺省实现是调用当前对象的toString()方法,然后转换成byte数组 27 | * 28 | * @return byte 数组 29 | */ 30 | byte[] toBytesArray(); 31 | 32 | /** 33 | * 判断两个 pos是否相同 34 | * 35 | * @param pos 所比较的位置 36 | * @return 是否相同 37 | */ 38 | boolean isSame(BinlogPosition pos); 39 | } 40 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/datautils/MysqlStringUtils.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.datautils; 2 | 3 | import java.util.Arrays; 4 | 5 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 6 | import com.hiriverunbiz.mysql.lib.exp.InvalidMysqlDataException; 7 | 8 | /** 9 | * 处理mysql string类型的数据工具,mysql string本质上是byte[]. 10 | * 11 | * ses 12 | * http://dev.mysql.com/doc/internals/en/string.html 13 | * 14 | * @author hexiufeng 15 | * 16 | */ 17 | public class MysqlStringUtils { 18 | private MysqlStringUtils() { 19 | } 20 | 21 | /** 22 | * 读取null结尾的string 23 | * 24 | * @param buf 25 | * @param off 26 | * @return 27 | */ 28 | public static byte[] readNulString(byte[] buf, Position off) { 29 | int end = -1; 30 | int start = off.getPos(); 31 | for (int i = start; i < buf.length; i++) { 32 | if (buf[i] == 0) { 33 | end = i; 34 | break; 35 | } 36 | } 37 | if (end == -1) { 38 | throw new InvalidMysqlDataException("invalid null string"); 39 | } 40 | off.forwardPos(end + 1 - start); 41 | return Arrays.copyOfRange(buf, start, end); 42 | } 43 | 44 | /** 45 | * 读取指定长度的string 46 | * 47 | * @param buf 48 | * @param off 49 | * @param len 50 | * @return 51 | */ 52 | public static byte[] readFixString(byte[] buf, Position off, int len) { 53 | int start = off.getPos(); 54 | off.forwardPos(len); 55 | return Arrays.copyOfRange(buf, start, start + len); 56 | } 57 | 58 | /** 59 | * 从当前位置读取到最后的string 60 | * 61 | * @param buf 62 | * @param off 63 | * @return 64 | */ 65 | public static byte[] readEofString(byte[] buf, Position off) { 66 | return readEofString(buf, off, false); 67 | } 68 | 69 | /** 70 | * 从当前位置读取到最后的string,不包含最好4个字节的校验码 71 | * 72 | * @param buf 73 | * @param off 74 | * @param hasCheckSum 75 | * @return 76 | */ 77 | public static byte[] readEofString(byte[] buf, Position off, boolean hasCheckSum) { 78 | int len = buf.length - off.getPos(); 79 | if (hasCheckSum) { 80 | len -= 4; 81 | } 82 | return readFixString(buf, off, len); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/text/AbstractTextCommandRequest.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.text; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.AbstractRequest; 4 | 5 | /** 6 | * 抽象的文本协议的 request命令 7 | * 8 | * @author hexiufeng 9 | * 10 | */ 11 | public abstract class AbstractTextCommandRequest extends AbstractRequest { 12 | protected final int command; 13 | 14 | protected AbstractTextCommandRequest(int command) { 15 | this.command = command; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/text/ColumnValue.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.text; 2 | 3 | import com.hiriver.unbiz.mysql.lib.output.ColumnDefinition; 4 | 5 | /** 6 | * 描述列的值 7 | * 8 | * @author hexiufeng 9 | * 10 | */ 11 | public class ColumnValue { 12 | private final ColumnValueProvider provider; 13 | private final ColumnDefinition definition; 14 | 15 | public ColumnValue(ColumnValueProvider provider, ColumnDefinition definition) { 16 | this.provider = provider; 17 | this.definition = definition; 18 | provider.useCharset(definition.getCharset()); 19 | } 20 | 21 | public boolean isNull() { 22 | return provider.isNull(); 23 | } 24 | 25 | public String getValueAsString() { 26 | return provider.getValueAsString(); 27 | } 28 | 29 | public Integer getValueAsInt() { 30 | return provider.getValueAsInt(); 31 | } 32 | 33 | public Long getValueAsLong() { 34 | return provider.getValueAsLong(); 35 | } 36 | 37 | public String getCharset() { 38 | return definition.getCharset(); 39 | } 40 | 41 | public String getColumnName(){ 42 | return definition.getColumName(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/text/ColumnValueProvider.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.text; 2 | 3 | 4 | /** 5 | * 解析列值的提供者抽象。用于解析每列的值 6 | * 7 | * @author hexiufeng 8 | * 9 | */ 10 | public interface ColumnValueProvider { 11 | /** 12 | * 解析成string类型 13 | * 14 | * @return string类型 15 | */ 16 | String getValueAsString(); 17 | 18 | /** 19 | * 解析成Integer值 20 | * 21 | * @return Integer值 22 | */ 23 | Integer getValueAsInt(); 24 | 25 | /** 26 | * 解析成long 27 | * 28 | * @return long值 29 | */ 30 | Long getValueAsLong(); 31 | 32 | /** 33 | * 是否是空值 34 | * 35 | * @return boolean 36 | */ 37 | boolean isNull(); 38 | 39 | /** 40 | * 当数据类型是string时,应该使用哪种charset来解析 41 | * 42 | * @param charset 43 | */ 44 | void useCharset(String charset); 45 | } 46 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/text/FieldListCommandResponse.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.text; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.hiriver.unbiz.mysql.lib.ResultContentReader; 7 | import com.hiriver.unbiz.mysql.lib.protocol.AbstractResponse; 8 | import com.hiriver.unbiz.mysql.lib.protocol.Response; 9 | import com.hiriver.unbiz.mysql.lib.protocol.tool.PacketTool; 10 | 11 | /** 12 | * 描述COM_FIELD_LIST指令的返回结果,返回表的列定义 13 | * 14 | * @author hexiufeng 15 | * 16 | */ 17 | public class FieldListCommandResponse extends AbstractResponse implements Response { 18 | private List columnList = new ArrayList(32); 19 | 20 | private final ResultContentReader resultContextReader; 21 | 22 | public FieldListCommandResponse(ResultContentReader resultContextReader) { 23 | this.resultContextReader = resultContextReader; 24 | } 25 | 26 | @Override 27 | public void parse(byte[] buf) { 28 | ColumnDefinitionResponse colDef = new ColumnDefinitionResponse(false); 29 | colDef.parse(buf); 30 | columnList.add(colDef); 31 | byte[] nextBuffer = resultContextReader.readNextPacketPayload(); 32 | while (!PacketTool.isEofPacket(nextBuffer)) { 33 | ColumnDefinitionResponse nextColDef = new ColumnDefinitionResponse(false); 34 | nextColDef.parse(nextBuffer); 35 | columnList.add(nextColDef); 36 | nextBuffer = resultContextReader.readNextPacketPayload(); 37 | } 38 | } 39 | 40 | public List getColumnList() { 41 | return columnList; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/text/PingRequest.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.text; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.Request; 4 | import com.hiriver.unbiz.mysql.lib.protocol.tool.SafeByteArrayOutputStream; 5 | 6 | /** 7 | * Ping command request 8 | * 9 | * @author hexiufeng 10 | * 11 | */ 12 | public class PingRequest extends AbstractTextCommandRequest implements Request { 13 | 14 | public PingRequest() { 15 | super(0x0e); 16 | } 17 | 18 | @Override 19 | protected void fillPayload(SafeByteArrayOutputStream out) { 20 | out.write(super.command); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/text/ResultsetRowResponse.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.text; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.hiriver.unbiz.mysql.lib.protocol.AbstractResponse; 7 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 8 | import com.hiriver.unbiz.mysql.lib.protocol.Response; 9 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 10 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlStringUtils; 11 | 12 | /** 13 | * COM_QUERY指令返回结果中行数据部分描述 14 | * 15 | * @author hexiufeng 16 | * 17 | */ 18 | public class ResultsetRowResponse extends AbstractResponse implements Response { 19 | private List valueList; 20 | private final List columnList; 21 | 22 | public List getValueList() { 23 | return valueList; 24 | } 25 | 26 | public ResultsetRowResponse(List columnList) { 27 | this.columnList = columnList; 28 | valueList = new ArrayList(columnList.size()); 29 | } 30 | 31 | @Override 32 | public void parse(byte[] buf) { 33 | Position pos = Position.factory(); 34 | for (int i = 0; i < columnList.size(); i++) { 35 | valueList.add(parseColumn(buf, i,pos)); 36 | } 37 | } 38 | 39 | /** 40 | * 解析每一列的数据 41 | * 42 | * @param buf 二进制数据 43 | * @param column 列的index 44 | * @return 列值 45 | */ 46 | private ColumnValue parseColumn(byte[] buf, int column,Position pos) { 47 | 48 | if ((buf[pos.getPos()] & 0xff) == 0xfb) { 49 | pos.forwardPos(); 50 | return null; 51 | } else { 52 | int lenc = (int) MysqlNumberUtils.readLencodeLong(buf, pos); 53 | byte[] tb = MysqlStringUtils.readFixString(buf, pos, lenc); 54 | return new ColumnValue(new TextColumnValueProvider(tb), columnList.get(column).toColumnDefinition()); 55 | } 56 | 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/text/TextColumnValueProvider.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.text; 2 | 3 | import com.hiriver.unbiz.mysql.lib.CharsetMapping; 4 | import com.hiriver.unbiz.mysql.lib.protocol.tool.StringTool; 5 | 6 | /** 7 | * 支持文本协议的列数据解析提供者。{@link ColumnValueProvider}实现 8 | * 9 | * @author hexiufeng 10 | * 11 | */ 12 | class TextColumnValueProvider implements ColumnValueProvider { 13 | private final byte[] binValue; 14 | private String charset = "UTF-8"; 15 | private boolean isConvert = false; 16 | private String convertedString; 17 | 18 | public TextColumnValueProvider(byte[] binValue) { 19 | this.binValue = binValue; 20 | } 21 | 22 | @Override 23 | public String getValueAsString() { 24 | if (!isConvert) { 25 | if (binValue == null) { 26 | convertedString = null; 27 | } 28 | if (CharsetMapping.isBinary(charset)) { 29 | convertedString = StringTool.safeConvertBytes2String(binValue); 30 | } else { 31 | convertedString = StringTool.safeConvertBytes2String(binValue, charset); 32 | } 33 | isConvert = true; 34 | } 35 | 36 | return convertedString; 37 | } 38 | 39 | @Override 40 | public Integer getValueAsInt() { 41 | if (isNull()) { 42 | return null; 43 | } 44 | return Integer.parseInt(getValueAsString()); 45 | } 46 | 47 | @Override 48 | public Long getValueAsLong() { 49 | if (isNull()) { 50 | return null; 51 | } 52 | return Long.parseLong(getValueAsString()); 53 | } 54 | 55 | @Override 56 | public void useCharset(String charset) { 57 | this.charset = charset; 58 | } 59 | 60 | @Override 61 | public boolean isNull() { 62 | return binValue == null; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/text/TextCommandFieldListRequest.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.text; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.tool.SafeByteArrayOutputStream; 4 | import com.hiriver.unbiz.mysql.lib.protocol.tool.StringTool; 5 | 6 | /** 7 | * COM_FIELD_LIST指令实现,用于获取表的列定义 8 | * 9 | * @author hexiufeng 10 | * 11 | */ 12 | public class TextCommandFieldListRequest extends AbstractTextCommandRequest { 13 | private String table; // string.nul 14 | private String fieldWildcard; // string.eof 15 | 16 | public TextCommandFieldListRequest() { 17 | super(0x04); 18 | } 19 | 20 | public TextCommandFieldListRequest(String table) { 21 | super(0x04); 22 | this.table = table; 23 | } 24 | 25 | @Override 26 | protected void fillPayload(SafeByteArrayOutputStream out) { 27 | out.write(super.command); 28 | out.safeWrite(StringTool.safeConvertString2Bytes(table)); 29 | out.write(0); 30 | if (fieldWildcard != null && !fieldWildcard.isEmpty()) { 31 | out.safeWrite(StringTool.safeConvertString2Bytes(fieldWildcard)); 32 | } 33 | } 34 | 35 | public String getTable() { 36 | return table; 37 | } 38 | 39 | public String getFieldWildcard() { 40 | return fieldWildcard; 41 | } 42 | 43 | public void setTable(String table) { 44 | this.table = table; 45 | } 46 | 47 | public void setFieldWildcard(String fieldWildcard) { 48 | this.fieldWildcard = fieldWildcard; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/text/TextCommandQueryRequest.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.text; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.tool.SafeByteArrayOutputStream; 4 | import com.hiriver.unbiz.mysql.lib.protocol.tool.StringTool; 5 | 6 | /** 7 | * COM_QUERY指令实现,用于执行sql 8 | * 9 | * @author hexiufeng 10 | * 11 | */ 12 | public class TextCommandQueryRequest extends AbstractTextCommandRequest { 13 | private String query; 14 | 15 | public TextCommandQueryRequest() { 16 | super(0x03); 17 | } 18 | 19 | public TextCommandQueryRequest(String query) { 20 | super(0x03); 21 | this.query = query; 22 | } 23 | 24 | @Override 25 | protected void fillPayload(SafeByteArrayOutputStream out) { 26 | out.write(super.command); 27 | out.safeWrite(StringTool.safeConvertString2Bytes(query)); 28 | } 29 | 30 | public String getQuery() { 31 | return query; 32 | } 33 | 34 | public void setQuery(String query) { 35 | this.query = query; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/text/TextCommandQueryResponse.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.text; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.hiriver.unbiz.mysql.lib.ResultContentReader; 7 | import com.hiriver.unbiz.mysql.lib.protocol.AbstractResponse; 8 | import com.hiriver.unbiz.mysql.lib.protocol.EOFPacket; 9 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 10 | import com.hiriver.unbiz.mysql.lib.protocol.Response; 11 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 12 | import com.hiriver.unbiz.mysql.lib.protocol.tool.PacketTool; 13 | 14 | /** 15 | * COM_QUERY指令的返回结果 16 | * 17 | * @author hexiufeng 18 | * 19 | */ 20 | public class TextCommandQueryResponse extends AbstractResponse implements Response { 21 | private int columnCount; 22 | private List columnList = new ArrayList(32); 23 | private List rowList = new ArrayList(); 24 | 25 | private final ResultContentReader resultContextReader; 26 | 27 | public TextCommandQueryResponse(ResultContentReader resultContextReader) { 28 | this.resultContextReader = resultContextReader; 29 | } 30 | 31 | @Override 32 | public void parse(byte[] buf) { 33 | Position pos = Position.factory(); 34 | columnCount = (int) MysqlNumberUtils.readLencodeLong(buf, pos); 35 | readColumnDefinition(columnCount); 36 | 37 | byte[] rowBuffer = resultContextReader.readNextPacketPayload(); 38 | while (!PacketTool.isEofPacket(rowBuffer, 0)) { 39 | // pasre row 40 | ResultsetRowResponse row = new ResultsetRowResponse(columnList); 41 | row.parse(rowBuffer); 42 | rowList.add(row); 43 | rowBuffer = resultContextReader.readNextPacketPayload(); 44 | } 45 | } 46 | 47 | private void readColumnDefinition(int count) { 48 | for (int i = 0; i < count; i++) { 49 | ColumnDefinitionResponse colDef = new ColumnDefinitionResponse(true); 50 | colDef.parse(resultContextReader.readNextPacketPayload()); 51 | 52 | columnList.add(colDef); 53 | } 54 | // parse eof 55 | EOFPacket eof = new EOFPacket(); 56 | eof.parse(resultContextReader.readNextPacketPayload()); 57 | } 58 | 59 | public List getColumnList() { 60 | return columnList; 61 | } 62 | 63 | public List getRowList() { 64 | return rowList; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/tool/GTSidTool.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.tool; 2 | 3 | /** 4 | * GTID工具,用于把byte数组转换成mysql uuid strig,或者把 5 | * uuid string转换成byte数据 6 | * 7 | * @author hexiufeng 8 | * 9 | */ 10 | public class GTSidTool { 11 | private GTSidTool() { 12 | 13 | } 14 | 15 | private final static char[] HEX_DICT = 16 | { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; 17 | 18 | /** 19 | * 把byte数组转换成mysql的uuid 20 | * 21 | * @param sid byte数组 22 | * @return uuid string 23 | */ 24 | public static String convertSid2UUIDString(byte[] sid) { 25 | StringBuilder sb = new StringBuilder(36); 26 | for (int i = 0; i < 16; i++) { 27 | if (i == 4 || i == 6 || i == 8 || i == 10) { 28 | sb.append('-'); 29 | } 30 | int value = sid[i] & 0xff; 31 | sb.append(HEX_DICT[value >> 4]); 32 | sb.append(HEX_DICT[value & 0xf]); 33 | } 34 | return sb.toString(); 35 | } 36 | 37 | /** 38 | * 用于把mysql uuid转换成byte数组 39 | * 40 | * @param uuidString uuid string 41 | * @return byte数组 42 | */ 43 | public static byte[] convertSidString2DumpFormatBytes(String uuidString) { 44 | String value = uuidString.replaceAll("-", ""); 45 | byte[] buffer = new byte[16]; 46 | 47 | for (int i = 0; i < 16; i++) { 48 | String hex = value.substring(2 * i, 2 * i + 2); 49 | int intValue = Integer.parseInt(hex, 16); 50 | buffer[i] = (byte) intValue; 51 | } 52 | return buffer; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/tool/GenericStringTypeChecker.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.tool; 2 | 3 | import com.hiriver.unbiz.mysql.lib.ColumnType; 4 | 5 | /** 6 | * Created by hexiufeng on 2017/5/28. 7 | */ 8 | public class GenericStringTypeChecker { 9 | private GenericStringTypeChecker(){ 10 | 11 | } 12 | 13 | /** 14 | * 针对于char类型所做的特殊处理,主要是重新修正length,参见log_event.cc#log_event_print_value 15 | * 16 | * @param meta column meta value 17 | * @param lengthHolder length占位符 18 | * @return 19 | */ 20 | public static ColumnType checkRealColumnType(int meta, int[] lengthHolder) { 21 | // from log_event.cc 22 | if (meta >= 256) { 23 | // column type 24 | int byte0 = meta >>> 8; 25 | // length 26 | int byte1 = meta & 0xff; 27 | 28 | if ((byte0 & 0x30) != 0x30) { 29 | /* a long CHAR() field: see #37426 */ 30 | lengthHolder[0] = byte1 | (((byte0 & 0x30) ^ 0x30) << 4); 31 | 32 | return ColumnType.ofTypeValue(byte0 | 0x30); 33 | } 34 | 35 | lengthHolder[0] = meta & 0xff; 36 | return ColumnType.ofTypeValue(byte0); 37 | 38 | } 39 | lengthHolder[0] = meta; 40 | return ColumnType.MYSQL_TYPE_STRING; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/tool/PacketTool.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.tool; 2 | 3 | /** 4 | * 判断common packet类型的工具 5 | * 6 | * @author hexiufeng 7 | * 8 | */ 9 | public class PacketTool { 10 | private PacketTool() { 11 | } 12 | 13 | /** 14 | * 读取到的二进制数据是否是OK包数据 15 | * 16 | * @param buf byte数组 17 | * @return 是否是OK包 18 | */ 19 | public static boolean isOkPackete(byte[] buf) { 20 | return (buf[0] & 0xff) == 0; 21 | } 22 | 23 | /** 24 | * 读取到的二进制数据是否是ERR包数据 25 | * 26 | * @param buf byte数组 27 | * @return 是否是ERR包 28 | * 29 | */ 30 | public static boolean isErrPackete(byte[] buf) { 31 | return (buf[0] & 0xff) == 0xff; 32 | } 33 | 34 | /** 35 | * 读取到的二进制数据是否是EOF包数据 36 | * 37 | * @param buf byte数组 38 | * @return 是否是EOF包 39 | * 40 | */ 41 | public static boolean isEofPacket(byte[] buf) { 42 | return buf.length < 9 && (buf[0] & 0xfe) == 0xfe; 43 | } 44 | 45 | /** 46 | * 读取到的二进制数据中从指定的位置算起是否是EOF包数据 47 | * 48 | * @param buf byte数组 49 | * @param pos 开始位置 50 | * @return 是否是EOF包 51 | */ 52 | public static boolean isEofPacket(byte[] buf, int pos) { 53 | return buf.length - pos < 9 && (buf[pos] & 0xfe) == 0xfe; 54 | } 55 | 56 | /** 57 | * 读取到的二进制数据中从指定的位置算起是否是切换权限验证包数据 58 | * 59 | * @param buf byte数组 60 | * @return 是否是切换权限验证包 61 | */ 62 | public static boolean isChangeAuthPackete(byte[] buf) { 63 | return (buf[0] & 0xff) == 0xfe; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/tool/PassSecure.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.tool; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.security.MessageDigest; 5 | import java.security.NoSuchAlgorithmException; 6 | 7 | /** 8 | * 9 | * mysql密码加密实现工具 10 | * 11 | * @author hexiufeng 12 | * 13 | */ 14 | public class PassSecure { 15 | 16 | private PassSecure() { 17 | } 18 | 19 | /** 20 | * 针对mysql "Secure Password Authentication"方式的实现,mysql支持5中密码验证方式., 其中"Secure Password Authentication" 21 | * 是比较安全的一种,又分为插件支持和非插件支持,我们实现 非插件支持的Authentication::Native41方式. 22 | * 23 | * @see http://dev.mysql.com/doc/ 24 | * internals/en/authentication-method.html 25 | * @see 27 | * http://dev.mysql.com/doc/internals/en/secure-password-authentication.html#packet-Authentication::Native41 28 | * 29 | * 30 | * @param password 密码 31 | * @param authRandom 来自mysql server的加密种子 32 | * @return 最终加密后的密码 33 | */ 34 | public static byte[] nativeMysqlSecure(String password, byte[] authRandom) { 35 | byte[] passBytes; 36 | try { 37 | passBytes = password.getBytes("utf-8"); 38 | } catch (UnsupportedEncodingException e) { 39 | throw new IllegalArgumentException(e); 40 | } 41 | 42 | MessageDigest md; 43 | try { 44 | md = MessageDigest.getInstance("SHA-1"); 45 | } catch (NoSuchAlgorithmException e) { 46 | throw new IllegalArgumentException(e); 47 | } 48 | 49 | return scramble411(passBytes, authRandom, md); 50 | } 51 | 52 | /** 53 | * 按照mysql的规范对密码进行加密编码。规则如下:
54 | *

55 | * SHA1( password ) XOR SHA1( "20-bytes random data from server" SHA1( SHA1( password ) ) ) 56 | *

57 | * 58 | * 注意:
59 | *

60 | * 本代码实现摘自mysql jdbc驱动的实现 61 | *

62 | * 63 | * @param pass 密码 64 | * @param seed 来在mysql server的动态加密种子,每次连接都不同,由mysql server产出 65 | * @param md sha-1算法实现实例 66 | * @return 加密后的密码 67 | */ 68 | private static final byte[] scramble411(byte[] pass, byte[] seed, MessageDigest md) { 69 | byte[] passwordHashStage1 = md.digest(pass); 70 | md.reset(); 71 | 72 | byte[] passwordHashStage2 = md.digest(passwordHashStage1); 73 | md.reset(); 74 | 75 | md.update(seed); 76 | md.update(passwordHashStage2); 77 | 78 | byte[] toBeXord = md.digest(); 79 | 80 | int numToXor = toBeXord.length; 81 | 82 | for (int i = 0; i < numToXor; i++) { 83 | toBeXord[i] = (byte) (toBeXord[i] ^ passwordHashStage1[i]); 84 | } 85 | 86 | return toBeXord; 87 | 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/tool/SafeByteArrayOutputStream.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.tool; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | 6 | /** 7 | * 继承{@link ByteArrayOutputStream},输出数据时不抛出异常。 8 | * 用于打包Request 9 | * 10 | * @author hexiufeng 11 | * 12 | */ 13 | public class SafeByteArrayOutputStream extends ByteArrayOutputStream { 14 | public SafeByteArrayOutputStream(int size) { 15 | super(size); 16 | } 17 | 18 | /** 19 | * 输出byte数组 20 | * 21 | * @param buff byte数组 22 | */ 23 | public void safeWrite(byte[] buff) { 24 | try { 25 | super.write(buff); 26 | } catch (IOException e) { 27 | // ignore 28 | } 29 | } 30 | 31 | /** 32 | * 重新设置某个位置的值 33 | * 34 | * @param pos 指定的位置 35 | * @param value byte数据值 36 | */ 37 | public void setPosValue(int pos, byte value) { 38 | super.buf[pos] = value; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriver/unbiz/mysql/lib/protocol/tool/StringTool.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.lib.protocol.tool; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | 5 | /** 6 | * 字符串处理工具 7 | * 8 | * @author hexiufeng 9 | * 10 | */ 11 | public class StringTool { 12 | private StringTool() { 13 | } 14 | 15 | /** 16 | * 转换string为utf8 数组,不抛出异常 17 | * 18 | * @param str 要转换的字符串 19 | * @return utf8数组 20 | */ 21 | public static byte[] safeConvertString2Bytes(String str) { 22 | try { 23 | return str.getBytes("utf-8"); 24 | 25 | } catch (UnsupportedEncodingException e) { 26 | throw new RuntimeException(e); 27 | } 28 | } 29 | 30 | /** 31 | * 把指定的数组转换成string,数组是utf8编码的 32 | * 33 | * @param buff utf8编码的数组 34 | * @return 字符串 35 | */ 36 | public static String safeConvertBytes2String(byte[] buff) { 37 | try { 38 | return new String(buff, "utf-8"); 39 | 40 | } catch (UnsupportedEncodingException e) { 41 | throw new RuntimeException(e); 42 | } 43 | } 44 | 45 | /** 46 | * 按照指定的字符集转换byte数组为string 47 | * 48 | * @param buff 指定字符集编码的数组 49 | * @param charsetName 字符集 50 | * @return 转换的字符串 51 | */ 52 | public static String safeConvertBytes2String(byte[] buff, String charsetName) { 53 | try { 54 | return new String(buff, charsetName); 55 | 56 | } catch (UnsupportedEncodingException e) { 57 | throw new RuntimeException(e); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriverunbiz/mysql/lib/exp/BaseExecuteException.java: -------------------------------------------------------------------------------- 1 | package com.hiriverunbiz.mysql.lib.exp; 2 | 3 | /** 4 | * 与mysql成功连接后,执行交互命令时发生的异常,它只是描述交互异常,但不包括网络异常s。 5 | * 6 | * @author hexiufeng 7 | * 8 | */ 9 | public class BaseExecuteException extends RuntimeException { 10 | 11 | /** 12 | * 13 | */ 14 | private static final long serialVersionUID = 1L; 15 | 16 | public BaseExecuteException(String message) { 17 | super(message); 18 | } 19 | 20 | public BaseExecuteException(Exception e) { 21 | super(e); 22 | } 23 | 24 | public BaseExecuteException(String message, Exception e) { 25 | super(message, e); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriverunbiz/mysql/lib/exp/HandShakeException.java: -------------------------------------------------------------------------------- 1 | package com.hiriverunbiz.mysql.lib.exp; 2 | 3 | /** 4 | * 与mysql进行连接握手时导致的异常. 5 | * 6 | * @author hexiufeng 7 | * 8 | */ 9 | public class HandShakeException extends RuntimeException { 10 | 11 | /** 12 | * 13 | */ 14 | private static final long serialVersionUID = 1L; 15 | 16 | public HandShakeException(String message) { 17 | super(message); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriverunbiz/mysql/lib/exp/InvalidMysqlDataException.java: -------------------------------------------------------------------------------- 1 | package com.hiriverunbiz.mysql.lib.exp; 2 | 3 | /** 4 | * 无效的mysql数据异常,一般发生在解析响应数据时。 5 | * 6 | * @author hexiufeng 7 | * 8 | */ 9 | public class InvalidMysqlDataException extends BaseExecuteException { 10 | 11 | /** 12 | * 13 | */ 14 | private static final long serialVersionUID = 1L; 15 | 16 | public InvalidMysqlDataException(String message) { 17 | super(message); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriverunbiz/mysql/lib/exp/NetworkException.java: -------------------------------------------------------------------------------- 1 | package com.hiriverunbiz.mysql.lib.exp; 2 | 3 | /** 4 | * 底层socket错误,可能网络中断导致 5 | * 6 | * @author hexiufeng 7 | * 8 | */ 9 | public class NetworkException extends RuntimeException { 10 | 11 | /** 12 | * 13 | */ 14 | private static final long serialVersionUID = 1L; 15 | 16 | public NetworkException(String message) { 17 | super(message); 18 | } 19 | 20 | public NetworkException(Exception e) { 21 | super(e); 22 | } 23 | 24 | public NetworkException(String message, Exception e) { 25 | super(message, e); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriverunbiz/mysql/lib/exp/NotExpectPayloadException.java: -------------------------------------------------------------------------------- 1 | package com.hiriverunbiz.mysql.lib.exp; 2 | 3 | /** 4 | * binlog事件中不合法的有效负载异常 5 | * 6 | * @author hexiufeng 7 | * 8 | */ 9 | public class NotExpectPayloadException extends BaseExecuteException { 10 | 11 | /** 12 | * 13 | */ 14 | private static final long serialVersionUID = 1L; 15 | 16 | public NotExpectPayloadException(String message) { 17 | super(message); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriverunbiz/mysql/lib/exp/PeerResetNetworkException.java: -------------------------------------------------------------------------------- 1 | package com.hiriverunbiz.mysql.lib.exp; 2 | 3 | /** 4 | * 底层socket被对方关闭异常。 5 | * 6 | * @author hexiufeng 7 | * 8 | */ 9 | public class PeerResetNetworkException extends BaseExecuteException { 10 | 11 | /** 12 | * 13 | */ 14 | private static final long serialVersionUID = 1L; 15 | 16 | public PeerResetNetworkException(String message) { 17 | super(message); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/main/java/com/hiriverunbiz/mysql/lib/exp/UnOpenedSocket.java: -------------------------------------------------------------------------------- 1 | package com.hiriverunbiz.mysql.lib.exp; 2 | 3 | /** 4 | * socket还没有别打开异常。一般是socket还没有被打开就执行读写操作引起的。 5 | * 6 | * @author hexiufeng 7 | * 8 | */ 9 | public class UnOpenedSocket extends RuntimeException { 10 | 11 | /** 12 | * 13 | */ 14 | private static final long serialVersionUID = 1L; 15 | 16 | public UnOpenedSocket(String message) { 17 | super(message); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/test/java/com/hiriver/unbiz/msyql/TestNumberUtils.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.msyql; 2 | 3 | import org.junit.Test; 4 | 5 | import com.hiriver.unbiz.mysql.lib.protocol.Position; 6 | import com.hiriver.unbiz.mysql.lib.protocol.datautils.MysqlNumberUtils; 7 | 8 | public class TestNumberUtils { 9 | @Test 10 | public void test3() { 11 | byte[] bin = "123".getBytes(); 12 | byte[] buf = { (byte) 0xfe, 0x29 }; 13 | Position off = Position.factory(); 14 | int value = (int) MysqlNumberUtils.readNInt(buf, off, 2); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /hiriver-modules/mysql-proto/src/test/java/com/hiriver/unbiz/mysql/tool/DecimalTestCase.java: -------------------------------------------------------------------------------- 1 | package com.hiriver.unbiz.mysql.tool; 2 | 3 | import com.hiriver.unbiz.mysql.lib.protocol.tool.MysqlDecimal; 4 | 5 | import org.junit.Test; 6 | 7 | /** 8 | * Created by hexiufeng on 2017/5/11. 9 | */ 10 | public class DecimalTestCase { 11 | @Test 12 | public void testDecimal(){ 13 | int precision=18; 14 | int scale = 4; 15 | byte[] buf = {(byte)0x80,0x0,0x1,0xD,(byte)0xFB,0x38,(byte)0xD2,0x4,(byte)0xD2}; 16 | // byte[] buf = {127, -1, -2, -14, 4, -57, 45, -5, 45}; 17 | MysqlDecimal myDecimal = new MysqlDecimal(precision,scale); 18 | myDecimal.parse(buf); 19 | System.out.println(myDecimal.toDecimal()); 20 | } 21 | } 22 | --------------------------------------------------------------------------------