Druid的使用
項(xiàng)目準(zhǔn)備接入SQL監(jiān)控等功能,就選擇了Druid捏鱼。因?yàn)轫?xiàng)目有點(diǎn)特殊搬葬,需要做一些簡(jiǎn)單的二次開(kāi)發(fā)幔翰。
Druid地址:https://github.com/alibaba/druid
說(shuō)下項(xiàng)目現(xiàn)狀和所做的修改
項(xiàng)目是分布式時(shí)的干毅,并且有多個(gè)模塊蓬网,但是并沒(méi)有使用注冊(cè)中心瘸味。
? ? ? ? ?這種情況下切端,就不太適合使用druid-monitor型将。因?yàn)閐ruid-monitor是單機(jī)監(jiān)控寂祥,分布式的情況下登錄各個(gè)應(yīng)用看監(jiān)控頁(yè)面太繁瑣了。所以采用了druid-admin.而druid-admin只支持注冊(cè)中心的方式七兜,所以做了如下修改
1.? 先更新POM中fastjson到最新版丸凭。因?yàn)閒astjson出BUG太頻繁了,應(yīng)該也因此流失了一些用戶腕铸。所以惜犀,先升級(jí)最新版,有備無(wú)患狠裹。
2.? 沒(méi)有注冊(cè)中心向拆,我們就把數(shù)據(jù)配置到對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)里面去就好了。
此處applications配置為你的應(yīng)用名字酪耳。
定義ClientServices替換DiscoveryClient
ClientServices里面主要是public List<String> getServices() 和 public List<ServiceInstance> getInstances(String service) 兩個(gè)方法浓恳,根據(jù)自己的配置刹缝,能讓這兩個(gè)方法返回正確的數(shù)據(jù)就行了。比如颈将,我采用的方式是根據(jù)不同環(huán)境梢夯,讀取不同的properties文件,然后再解析對(duì)應(yīng)的JSON晴圾。對(duì)springboot開(kāi)發(fā)者颂砸,是很簡(jiǎn)單的事情。
定義ClientServiceInstance作為servicedata.data的數(shù)據(jù)模型死姚。?
以上人乓,druid-admin就改造完事了。
3.? ?對(duì)監(jiān)控的需求都毒,主要是監(jiān)控慢SQL色罚、輸出SQL執(zhí)行日志(輸出update, insert,delete語(yǔ)句和參數(shù),select 沒(méi)必要輸出)我使用了druid-spring-boot-starter,然后又做的相關(guān)配置账劲。當(dāng)然也可以不使用druid-spring-boot-starter戳护,starter里面的代碼并不多,邏輯也很簡(jiǎn)單瀑焦,但是如果不使用腌且,我就需要把里面的代碼基本再重寫一份,沒(méi)必要重寫啊榛瓮。
主要有如下配置:
配置基本數(shù)據(jù)庫(kù)地址:
spring.datasource.druid.url=${jdbc.url}
spring.datasource.druid.username=${jdbc.user.name}
spring.datasource.druid.password=${jdbc.password}
#配置各種參數(shù):
# datasouce ??
spring.datasource.druid.initial-size=10
spring.datasource.druid.max-active=50
spring.datasource.druid.min-idle=5
spring.datasource.druid.validation-query=SELECT 1
spring.datasource.druid.test-on-borrow=false
spring.datasource.druid.test-on-return=false
spring.datasource.druid.test-while-idle=true
spring.datasource.druid.log-abandoned=true
spring.datasource.druid.remove-abandoned=true
spring.datasource.druid.remove-abandoned-timeout=120
#把統(tǒng)計(jì)信息錄入日志铺董,暫不啟用
#spring.datasource.druid.time-between-log-stats-millis=3600000
#druid SQL
監(jiān)控spring.datasource.druid.filter.stat.enabled=true
#慢SQL輸出到日志,默認(rèn)是ERROR級(jí)別的spring.datasource.druid.filter.stat.log-slow-sql=true
spring.datasource.druid.filter.stat.slow-sql-millis=3000
#URL 請(qǐng)求監(jiān)控spring.datasource.druid.web-stat-filter.enabled=true
#spring.datasource.druid.web-stat-filter.url-pattern=/*
#spring.datasource.druid.web-stat-filter.exclusions=*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/
#監(jiān)控頁(yè)面? druid monitor
spring.datasource.druid.stat-view-servlet.enabled=true
spring.datasource.druid.stat-view-servlet.allow=admin的IP地址
#? 因?yàn)槭褂胐ruid-dmin禀晓,所以不配置用戶名密碼
#spring.datasource.druid.stat-view-servlet.login-username=admin
#spring.datasource.druid.stat-view-servlet.login-password=admin
#slf4j
柄粹。本項(xiàng)目為logback,所以使用了slf4j.? 默認(rèn)不啟用druid-spring-boot-starter里面的配置,因?yàn)槲易隽硕伍_(kāi)發(fā)
spring.datasource.druid.filter.slf4j.enabled=false
#SQL防火墻匆绣,開(kāi)不開(kāi)都行驻右,根據(jù)需求吧。
spring.datasource.druid.filter.wall.enabled=true
啟用自定義的slf4jLogFilter
@Bean("slf4jLogFilter")
@Primary
@ConditionalOnMissingBean
public Slf4jLogFilter configSlf4jLogFilter(){
??? MySlf4jLogFilter slf4jLogFilter =new MySlf4jLogFilter ();
??? return slf4jLogFilter;
}
自定義MySlf4jLogFilter 崎淳。上面說(shuō)了堪夭,我需要只日志輸出update,
insert,delete語(yǔ)句和參數(shù),并且要求盡量少的日志拣凹,并且輸入應(yīng)用的鏈路ID:traceid森爽。組件本身不滿足需求,做了簡(jiǎn)單的二次開(kāi)發(fā)嚣镜。
@Configuration
public class MySlf4jLogFilter extends Slf4jLogFilter? implements InitializingBean{
@Override
protected void statementExecuteUpdateAfter(StatementProxy statement, String sql,int updateCount) {
if (isStatementExecuteUpdateAfterLogEnabled() && isStatementLogEnabled()) {
????????statement.setLastExecuteTimeNano();
??????????? double nanos = statement.getLastExecuteTimeNano();
??????????? double millis = nanos /(1000 *1000);
??????????? int parametersSize = statement.getParametersSize();
??????????? List<Object> parameters =new ArrayList<Object>(parametersSize);
??????????? for (int i =0; i < parametersSize; ++i) {
????????????????JdbcParameter jdbcParam = statement.getParameter(i);
????? ??????????parameters.add(jdbcParam !=null? jdbcParam.getValue():null);
??????????? }
????????DbType dbType =DbType.of(statement.getConnectionProxy().getDirectDataSource().getDbType());
? ? ? ?String formattedSql = SQLUtils.format(sql, dbType, parameters, getStatementSqlFormatOption());
? ? ? ?statementLog("{conn-" + statement.getConnectionProxy().getId() +", " + stmtId(statement)+"} update executed. effort " + updateCount +". " + millis +" millis. " + formattedSql);
??????? }
??? }
protected String stmtId(StatementProxy statement) {
????????StringBuffer buf =new StringBuffer();
??????? if (statementinstanceof CallableStatementProxy) {
????????????buf.append("cstmt-");
??????? } else if (statementinstanceof PreparedStatementProxy) {
????????????buf.append("pstmt-");
??????? } else {
? ? ????????buf.append("stmt-");
??????? }
????????buf.append(statement.getId());
??????? buf.append("}{traceId="+RequestContext.getRequestId());
??????? return buf.toString();
??? }
????@Override
????public void afterPropertiesSet() {
????????setConnectionConnectBeforeLogEnabled(false);
??????? setConnectionConnectAfterLogEnabled(false);
??????? setConnectionCommitAfterLogEnabled(false);
??????? setConnectionRollbackAfterLogEnabled(false);
??????? setConnectionCloseAfterLogEnabled(false);
??????? setStatementCreateAfterLogEnabled(false);
??????? setStatementPrepareAfterLogEnabled(false);
??????? setStatementPrepareCallAfterLogEnabled(false);
??????? setStatementExecuteAfterLogEnabled(false);
??????? setStatementExecuteQueryAfterLogEnabled(false);
??????? setStatementExecuteUpdateAfterLogEnabled(true);
??????? setStatementExecuteBatchAfterLogEnabled(true);
??????? setStatementExecutableSqlLogEnable(false);
??????? setStatementCloseAfterLogEnabled(false);
??????? setStatementParameterClearLogEnable(false);
??????? setStatementParameterSetLogEnabled(false);
??????? setResultSetNextAfterLogEnabled(false);
??????? setResultSetOpenAfterLogEnabled(false);
??????? setResultSetCloseAfterLogEnabled(false);
??????? setDataSourceLogEnabled(false);
??????? setConnectionLogEnabled(false);
??????? setConnectionLogErrorEnabled(true);
??????? setStatementLogEnabled(true);
????? ??setStatementLogErrorEnabled(true);
??????? setResultSetLogEnabled(false);
??????? setResultSetLogErrorEnabled(true);
??????? SQLUtils.FormatOption statementSqlFormatOption???????????? =new SQLUtils.FormatOption(false,false);
??????? setStatementSqlFormatOption(statementSqlFormatOption);
??? }
afterPropertiesSet是各種配置參數(shù)爬迟,參數(shù)望文取義即可,挺清晰的菊匿。如上配置的是我的需求付呕。
要輸出日志计福,還需要在logback里面配置logger:
<logger name="druid.sql.Connection" level="ERROR">
?? <appender-ref ref="DruidErrorFile" />
</logger>
<logger name="druid.sql.Statement" level="DUBUG">
?? <appender-ref ref="DruidDebugFile" />
</logger>
<logger name="druid.sql.DataSource" level="ERROR">
?? <appender-ref ref="DruidErrorFile" />
</logger>
<logger name="druid.sql.ResultSet" level="ERROR">
?? <appender-ref ref="DruidErrorFile" />
</logger>
DruidErrorFile,DruidDebugFile就沒(méi)什么好說(shuō)的了。正常配置即可徽职。
以上象颖,完活。