本文記錄springboot2集成shardingsphere4實現業務層讀寫分離,其余相關涉及組件 :mybatis-plus,hikari, postgresql , logback ,p6spy
1. pom文件引入shardingsphere4依賴
<dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>sharding-jdbc-spring-boot-starter</artifactId> <version>4.0.1</version> </dependency>
2.shardingsphere4相關配置項
1 spring: 2 shardingsphere: 3 datasource: 4 names: master,slave0,slave1 5 master: 6 type: com.zaxxer.hikari.HikariDataSource 7 driver-class-name: com.p6spy.engine.spy.P6SpyDriver 8 # 連接池配置項 9 jdbc-url: jdbc:p6spy:postgresql://a:5432/bb 10 username: postgres 11 password: postgres 12 autoCommit: false 13 maximum-pool-size: 10 14 validation-timeout: 5_000 15 connection-timeout: 30_000 16 idle-timeout: 600_000 17 max-lifetime: 1_800_000 18 slave0: 19 type: com.zaxxer.hikari.HikariDataSource 20 driver-class-name: com.p6spy.engine.spy.P6SpyDriver 21 jdbc-url: jdbc:p6spy:postgresql://b:5432/bb 22 username: postgres 23 password: postgres 24 autoCommit: false 25 maximum-pool-size: 10 26 validation-timeout: 5_000 27 connection-timeout: 30_000 28 idle-timeout: 600_000 29 max-lifetime: 1_800_000 30 slave1: 31 type: com.zaxxer.hikari.HikariDataSource 32 driver-class-name: com.p6spy.engine.spy.P6SpyDriver 33 jdbc-url: jdbc:p6spy:postgresql://c:5432/bb 34 username: postgres 35 password: postgres 36 autoCommit: false 37 maximum-pool-size: 10 38 validation-timeout: 5_000 39 connection-timeout: 30_000 40 idle-timeout: 600_000 41 max-lifetime: 1_800_000 42 # 讀寫分離配置項 43 masterslave: 44 load-balance-algorithm-type: round_robin # random 45 master-data-source-name: master 46 slave-data-source-names: slave0,slave1 47 name: ms 48 props: 49 sql: 50 show: true
3.p6spy相關配置項 (resources下創建spy.properties文件,內容如下)
1 #開啟模塊sql記錄和長時sql記錄 2 module.log=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory 3 logMessageFormat=com.p6spy.engine.spy.appender.CustomLineFormat 4 #自定義sql輸出格式 5 customLogMessageFormat=%(currentTime) | TIME\uFF1A %(executionTime) ms | SQL\uFF1A %(sql) 6 #日志輸出方式 7 appender=com.p6spy.engine.spy.appender.Slf4JLogger 8 excludecategories=info,debug,result,resultset 9 deregisterdrivers=true 10 dateformat=yyyy-MM-dd HH:mm:ss 11 driverlist=org.postgresql.Driver 12 #開啟長時sql記錄 13 outagedetection=true 14 #觸發長時記錄時限 15 outagedetectioninterval=2
4.logback相關配置(resource文件夾下創建logback-spring.xml內容如下)
1 <?xml version="1.0" encoding="UTF-8"?> 2 <configuration> 3 4 <!--日志格式應用spring boot默認的格式,也可以自己更改--> 5 <include resource="org/springframework/boot/logging/logback/defaults.xml"/> 6 7 <!--定義日志存放的位置,默認存放在項目啟動的相對路徑的目錄--> 8 <springProperty scope="context" name="LOG_PATH" source="log.path" defaultValue="log"/> 9 <springProperty scope="context" name="ZONE" source="quick.zone" defaultValue="default"/> 10 <springProperty scope="context" name="APP_NAME" source="spring.application.name" defaultValue="app"/> 11 <springProperty scope="context" name="LOGSTASH_URL" source="logstash.url" defaultValue="localhost:4560"/> 12 13 <!-- ****************************************************************************************** --> 14 <!-- ****************************** 開發環境日志 ************************************ --> 15 <!-- ****************************************************************************************** --> 16 <springProfile name="local"> 17 <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 18 <encoder> 19 <pattern>${CONSOLE_LOG_PATTERN}</pattern> 20 <charset>utf-8</charset> 21 </encoder> 22 </appender> 23 24 <!-- 日志記錄器,日期滾動記錄 --> 25 <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> 26 27 <!-- 正在記錄的日志文件的路徑及文件名 --> 28 <file>${LOG_PATH}/${ZONE}-${APP_NAME}-local.log</file> 29 30 <!-- 日志記錄器的滾動策略,按日期,按大小記錄 --> 31 <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> 32 33 <!-- 歸檔的日志文件的路徑,%d{yyyy-MM-dd}指定日期格式,%i指定索引 --> 34 <fileNamePattern>${LOG_PATH}/all/${ZONE}-${APP_NAME}-%d{yyyy-MM-dd}.%i-local.log</fileNamePattern> 35 36 <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> 37 <maxFileSize>10MB</maxFileSize> 38 </timeBasedFileNamingAndTriggeringPolicy> 39 </rollingPolicy> 40 41 <!-- 追加方式記錄日志 --> 42 <append>true</append> 43 44 <!-- 日志文件的格式 --> 45 <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> 46 <pattern>${FILE_LOG_PATTERN}</pattern> 47 <charset>utf-8</charset> 48 </encoder> 49 </appender> 50 51 <!--默認所有的包以warn--> 52 <root level="warn"> 53 <appender-ref ref="STDOUT"/> 54 <appender-ref ref="FILE"/> 55 </root> 56 57 <!--ShardingSphere打印sql用--> 58 <logger name="ShardingSphere-SQL" level="info"/> 59 <!--p6spy打印sql--> 60 <logger name="p6spy" level="info"/> 61 <!--屏蔽getRowIdLifetime未實作警告--> 62 <logger name="com.zaxxer.hikari.pool.ProxyConnection" level="error"/> 63 64 <!--各個服務的包在本地執行的時候,打開debug模式--> 65 <logger name="com.myproject" level="debug" additivity="false"> 66 <appender-ref ref="STDOUT"/> 67 <appender-ref ref="FILE"/> 68 </logger> 69 </springProfile> 70 71 <!-- ********************************************************************************************** --> 72 <!-- **** 放到服務器上不管在什么環境都只在文件記錄日志,控制台(catalina.out)打印logback捕獲不到的日志 **** --> 73 <!-- ********************************************************************************************** --> 74 <springProfile name="!local"> 75 76 <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 77 <encoder> 78 <pattern>${CONSOLE_LOG_PATTERN}</pattern> 79 <charset>utf-8</charset> 80 </encoder> 81 </appender> 82 83 <!-- 日志記錄器,日期滾動記錄 --> 84 <appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender"> 85 86 <!-- 正在記錄的日志文件的路徑及文件名 --> 87 <file>${LOG_PATH}/quick/${ZONE}-${APP_NAME}-log-error.log</file> 88 89 <!-- 日志記錄器的滾動策略,按日期,按大小記錄 --> 90 <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> 91 92 <!-- 歸檔的日志文件的路徑,%d{yyyy-MM-dd}指定日期格式,%i指定索引 --> 93 <fileNamePattern>${LOG_PATH}/quick/${ZONE}-${APP_NAME}-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern> 94 95 <!-- 除按日志記錄之外,還配置了日志文件不能超過10M,若超過10M,日志文件會以索引0開始, 96 命名日志文件,例如log-error-2013-12-21.0.log --> 97 <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> 98 <maxFileSize>10MB</maxFileSize> 99 </timeBasedFileNamingAndTriggeringPolicy> 100 </rollingPolicy> 101 102 <!-- 追加方式記錄日志 --> 103 <append>true</append> 104 105 <!-- 日志文件的格式 --> 106 <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> 107 <pattern>${FILE_LOG_PATTERN}</pattern> 108 <charset>utf-8</charset> 109 </encoder> 110 111 <!-- 此日志文件只記錄error級別的 --> 112 <filter class="ch.qos.logback.classic.filter.LevelFilter"> 113 <level>error</level> 114 <onMatch>ACCEPT</onMatch> 115 <onMismatch>DENY</onMismatch> 116 </filter> 117 </appender> 118 119 <!-- 日志記錄器,日期滾動記錄 --> 120 <appender name="FILE_ALL" class="ch.qos.logback.core.rolling.RollingFileAppender"> 121 122 <!-- 正在記錄的日志文件的路徑及文件名 --> 123 <file>${LOG_PATH}/quick/${ZONE}-${APP_NAME}-log-all.log</file> 124 125 <!-- 日志記錄器的滾動策略,按日期,按大小記錄 --> 126 <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> 127 128 <!-- 歸檔的日志文件的路徑,%d{yyyy-MM-dd}指定日期格式,%i指定索引 --> 129 <fileNamePattern>${LOG_PATH}/quick/${ZONE}-${APP_NAME}-all-%d{yyyy-MM-dd}.%i.log</fileNamePattern> 130 131 <!-- 除按日志記錄之外,還配置了日志文件不能超過10M,若超過10M,日志文件會以索引0開始, 132 命名日志文件,例如log-error-2013-12-21.0.log --> 133 <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> 134 <maxFileSize>10MB</maxFileSize> 135 </timeBasedFileNamingAndTriggeringPolicy> 136 </rollingPolicy> 137 138 <!-- 追加方式記錄日志 --> 139 <append>true</append> 140 141 <!-- 日志文件的格式 --> 142 <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> 143 <pattern>${FILE_LOG_PATTERN}</pattern> 144 <charset>utf-8</charset> 145 </encoder> 146 </appender> 147 148 <!-- 日志記錄器,日期滾動記錄 紀錄TAM日志--> 149 <appender name="FILE_TAM" class="ch.qos.logback.core.rolling.RollingFileAppender"> 150 151 <!-- 正在記錄的日志文件的路徑及文件名 --> 152 <file>${LOG_PATH}/quick/${ZONE}-${APP_NAME}-log-tam.log</file> 153 154 <!-- 日志記錄器的滾動策略,按日期,按大小記錄 --> 155 <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> 156 157 <!-- 歸檔的日志文件的路徑,%d{yyyy-MM-dd}指定日期格式,%i指定索引 --> 158 <fileNamePattern>${LOG_PATH}/quick/${ZONE}-${APP_NAME}-tam-%d{yyyy-MM-dd}.%i.log</fileNamePattern> 159 160 <!-- 除按日志記錄之外,還配置了日志文件不能超過10M,若超過10M,日志文件會以索引0開始, 161 命名日志文件,例如log-error-2013-12-21.0.log --> 162 <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> 163 <maxFileSize>10MB</maxFileSize> 164 </timeBasedFileNamingAndTriggeringPolicy> 165 </rollingPolicy> 166 167 <!-- 追加方式記錄日志 --> 168 <append>true</append> 169 170 <!-- 日志文件的格式 --> 171 <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> 172 <pattern>${FILE_LOG_PATTERN}</pattern> 173 <charset>utf-8</charset> 174 </encoder> 175 <!-- 此日志文件只記錄error級別的 --> 176 <filter class="ch.qos.logback.classic.filter.LevelFilter"> 177 <level>error</level> 178 <onMatch>ACCEPT</onMatch> 179 <onMismatch>DENY</onMismatch> 180 </filter> 181 </appender> 182 183 <!-- 日志記錄器,日期滾動記錄 紀錄AUDITLOG日志--> 184 <appender name="FILE_AUDIT" class="ch.qos.logback.core.rolling.RollingFileAppender"> 185 186 <!-- 正在記錄的日志文件的路徑及文件名 --> 187 <file>${LOG_PATH}/auditlog/${ZONE}-${APP_NAME}-log-auditLog.log</file> 188 189 <!-- 日志記錄器的滾動策略,按日期,按大小記錄 --> 190 <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> 191 192 <!-- 歸檔的日志文件的路徑,%d{yyyy-MM-dd}指定日期格式,%i指定索引 --> 193 <fileNamePattern>${LOG_PATH}/auditlog/${ZONE}-${APP_NAME}-auditlog-%d{yyyy-MM-dd}.%i.log</fileNamePattern> 194 195 <!-- 除按日志記錄之外,還配置了日志文件不能超過10M,若超過10M,日志文件會以索引0開始, 196 命名日志文件,例如log-error-2013-12-21.0.log --> 197 <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> 198 <maxFileSize>10MB</maxFileSize> 199 </timeBasedFileNamingAndTriggeringPolicy> 200 </rollingPolicy> 201 202 <!-- 追加方式記錄日志 --> 203 <append>true</append> 204 205 <!-- 日志文件的格式 --> 206 <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> 207 <pattern>${FILE_LOG_PATTERN}</pattern> 208 <charset>utf-8</charset> 209 </encoder> 210 <!-- 此日志文件只記錄info級別的 --> 211 <filter class="ch.qos.logback.classic.filter.LevelFilter"> 212 <level>info</level> 213 <onMatch>ACCEPT</onMatch> 214 <onMismatch>DENY</onMismatch> 215 </filter> 216 </appender> 217 218 <appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender"> 219 <destination>${LOGSTASH_URL}</destination> 220 <encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder" /> 221 </appender> 222 223 224 <!--記錄到文件時,記錄兩類一類是error日志,一個是所有日志--> 225 <root level="warn"> 226 <appender-ref ref="STDOUT"/> 227 <appender-ref ref="FILE_ERROR"/> 228 <appender-ref ref="FILE_ALL"/> 229 </root> 230 231 <logger name="com.myproject" level="info"/> 232 <!--ShardingSphere打印sql用--> 233 <logger name="ShardingSphere-SQL" level="info"/> 234 <!--p6spy打印sql--> 235 <logger name="p6spy" level="info"/> 236 <!--屏蔽getRowIdLifetime未實作警告--> 237 <logger name="com.zaxxer.hikari.pool.ProxyConnection" level="error"/> 238 </springProfile> 239 240 </configuration>
5.集成結果
通過以上日志觀察發現已經實現了業務層面讀寫分離和讀庫的輪詢負載策略
通過以上日志發現已經實現了線上環境的完整sql記錄,至此已達到預期效果,集成完畢
6.問題總結
1.從數據層方面shardingsphere4並沒有實現主從庫的數據同步,在其他方案的數據同步過程中主從數據不一致導致的數據差異是否滿足應用場景還需具體評估.
2.本實例數據庫采用postgres,目前最新版本的postgres數據庫驅動也沒有實現getRowIdLifetime()方法,shardingsphere4緩存數據源元數據的時候會去調用該方法,
驅動程序會拋出一個方法未實作的異常從下面代碼可見框架捕獲后不做處理並緩存該項為UNSUPPORTED,但是因為實例集成hikari連接池,hikar的ProxyConnection類會拋出一個一個警告如下圖二,看起來像是項目啟動報錯,但實際並不影響,程序運行,因此在輸出日志中將項屏蔽,具體配置在上面logback中
警告:
3.logback的配置中root節點默認開啟warm級別日志,要想支撐 shardingsphere 和p6spy記錄sql需要單獨配置相應節點,開放對應日志級別,因 shardingsphere 打印出的不完整sql,除了查看數據庫路由外並沒有太大用處,而本項目當前並不關心數據庫路由,因此將對應配置項設為false,並集成p6spy完成完整的sql記錄
4.配置項中的數據庫url連接項名稱視具體連接池可能會有不同,本實例用連接池該名為jdbc-url,有些連接池叫url
7.部分源碼分析
該主鍵的入口類,實現了EnvironmentAware接口,在DataSoureAutoConfiguration之前被執行對dataSource經常了包裝處理用於支撐該框架的功能,下圖為datasource實例化過程,該方法的第一個入參dataSourceClassName為配置項的type所以對連接池的配置項需要和type放在同一級,如文章開始配置項所示.
本實例只關心主從分離,其他源碼部分相關類:
以上,為全部內容,后期發現問題再做補充.