Druid DataSource初始化流程

开篇

  • DruidDataSourceFactory通过createDataSource方法创建DruidDataSource对象并通过config方法设置DruidDataSource的属性。
  • DruidDataSource通过父类DruidAbstractDataSource创建ReentrantLock lock作为DruidDataSource的锁,Condition notEmpty 作为DruidDataSource的非空信号,Condition empty作为DruidDataSource的空信号。

初始化流程

  • Druid所有链接都是CreateConnectionThread创建出来的
  • Druid在初始化的时候,会初始化三个内容:定时调度的DestoryTask、一直运行的CreateConnectionThread、一直运行的DestoryConnectionThread, DestoryConnectionThread是用来逐出空闲线程的。
  • init在Datasource初始化的时候并不会执行,是在第一次获取getConnection执行的。

源码分析

public class DruidDataSourceFactory implements ObjectFactory {

    public final static String PROP_DEFAULTAUTOCOMMIT                   = "defaultAutoCommit";
    public final static String PROP_DEFAULTREADONLY                     = "defaultReadOnly";
    public final static String PROP_DEFAULTTRANSACTIONISOLATION         = "defaultTransactionIsolation";
    public final static String PROP_DEFAULTCATALOG                      = "defaultCatalog";
    public final static String PROP_DRIVERCLASSNAME                     = "driverClassName";
    public final static String PROP_MAXACTIVE                           = "maxActive";
    public final static String PROP_MAXIDLE                             = "maxIdle";
    public final static String PROP_MINIDLE                             = "minIdle";
    public final static String PROP_INITIALSIZE                         = "initialSize";
    public final static String PROP_MAXWAIT                             = "maxWait";
    public final static String PROP_TESTONBORROW                        = "testOnBorrow";
    public final static String PROP_TESTONRETURN                        = "testOnReturn";
    public final static String PROP_TIMEBETWEENEVICTIONRUNSMILLIS       = "timeBetweenEvictionRunsMillis";
    public final static String PROP_NUMTESTSPEREVICTIONRUN              = "numTestsPerEvictionRun";
    public final static String PROP_MINEVICTABLEIDLETIMEMILLIS          = "minEvictableIdleTimeMillis";
    public final static String PROP_PHY_TIMEOUT_MILLIS                  = "phyTimeoutMillis";
    public final static String PROP_TESTWHILEIDLE                       = "testWhileIdle";
    public final static String PROP_PASSWORD                            = "password";
    public final static String PROP_URL                                 = "url";
    public final static String PROP_USERNAME                            = "username";
    public final static String PROP_VALIDATIONQUERY                     = "validationQuery";
    public final static String PROP_VALIDATIONQUERY_TIMEOUT             = "validationQueryTimeout";
    public final static String PROP_INITCONNECTIONSQLS                  = "initConnectionSqls";
    public final static String PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED = "accessToUnderlyingConnectionAllowed";
    public final static String PROP_REMOVEABANDONED                     = "removeAbandoned";
    public final static String PROP_REMOVEABANDONEDTIMEOUT              = "removeAbandonedTimeout";
    public final static String PROP_LOGABANDONED                        = "logAbandoned";
    public final static String PROP_POOLPREPAREDSTATEMENTS              = "poolPreparedStatements";
    public final static String PROP_MAXOPENPREPAREDSTATEMENTS           = "maxOpenPreparedStatements";
    public final static String PROP_CONNECTIONPROPERTIES                = "connectionProperties";
    public final static String PROP_FILTERS                             = "filters";
    public final static String PROP_EXCEPTION_SORTER                    = "exceptionSorter";
    public final static String PROP_EXCEPTION_SORTER_CLASS_NAME         = "exception-sorter-class-name";
    public final static String PROP_NAME                                = "name";
    public final static String PROP_INIT                                = "init";

    public static DataSource createDataSource(Map properties) throws Exception {
        // 创建DruidDataSource对象
        DruidDataSource dataSource = new DruidDataSource();
        // 设置DruidDataSource的属性
        config(dataSource, properties);
        return dataSource;
    }

    @SuppressWarnings({"deprecation", "rawtypes"})
    public static void config(DruidDataSource dataSource, Map<?, ?> properties) throws SQLException {
        String value = null;

        value = (String) properties.get(PROP_DEFAULTAUTOCOMMIT);
        if (value != null) {
            dataSource.setDefaultAutoCommit(Boolean.valueOf(value).booleanValue());
        }

        value = (String) properties.get(PROP_DEFAULTREADONLY);
        if (value != null) {
            dataSource.setDefaultReadOnly(Boolean.valueOf(value).booleanValue());
        }

        value = (String) properties.get(PROP_DEFAULTTRANSACTIONISOLATION);
        if (value != null) {
            int level = UNKNOWN_TRANSACTIONISOLATION;
            if ("NONE".equalsIgnoreCase(value)) {
                level = Connection.TRANSACTION_NONE;
            } else if ("READ_COMMITTED".equalsIgnoreCase(value)) {
                level = Connection.TRANSACTION_READ_COMMITTED;
            } else if ("READ_UNCOMMITTED".equalsIgnoreCase(value)) {
                level = Connection.TRANSACTION_READ_UNCOMMITTED;
            } else if ("REPEATABLE_READ".equalsIgnoreCase(value)) {
                level = Connection.TRANSACTION_REPEATABLE_READ;
            } else if ("SERIALIZABLE".equalsIgnoreCase(value)) {
                level = Connection.TRANSACTION_SERIALIZABLE;
            } else {
                try {
                    level = Integer.parseInt(value);
                } catch (NumberFormatException e) {
                    LOG.error("Could not parse defaultTransactionIsolation: " + value);
                    LOG.error("WARNING: defaultTransactionIsolation not set");
                    LOG.error("using default value of database driver");
                    level = UNKNOWN_TRANSACTIONISOLATION;
                }
            }
            dataSource.setDefaultTransactionIsolation(level);
        }

        value = (String) properties.get(PROP_DEFAULTCATALOG);
        if (value != null) {
            dataSource.setDefaultCatalog(value);
        }

        value = (String) properties.get(PROP_DRIVERCLASSNAME);
        if (value != null) {
            dataSource.setDriverClassName(value);
        }

        value = (String) properties.get(PROP_MAXACTIVE);
        if (value != null) {
            dataSource.setMaxActive(Integer.parseInt(value));
        }

        value = (String) properties.get(PROP_MAXIDLE);
        if (value != null) {
            dataSource.setMaxIdle(Integer.parseInt(value));
        }

        value = (String) properties.get(PROP_MINIDLE);
        if (value != null) {
            dataSource.setMinIdle(Integer.parseInt(value));
        }

        value = (String) properties.get(PROP_INITIALSIZE);
        if (value != null) {
            dataSource.setInitialSize(Integer.parseInt(value));
        }

        value = (String) properties.get(PROP_MAXWAIT);
        if (value != null) {
            dataSource.setMaxWait(Long.parseLong(value));
        }

        value = (String) properties.get(PROP_TESTONBORROW);
        if (value != null) {
            dataSource.setTestOnBorrow(Boolean.valueOf(value).booleanValue());
        }

        value = (String) properties.get(PROP_TESTONRETURN);
        if (value != null) {
            dataSource.setTestOnReturn(Boolean.valueOf(value).booleanValue());
        }

        value = (String) properties.get(PROP_TIMEBETWEENEVICTIONRUNSMILLIS);
        if (value != null) {
            dataSource.setTimeBetweenEvictionRunsMillis(Long.parseLong(value));
        }

        value = (String) properties.get(PROP_NUMTESTSPEREVICTIONRUN);
        if (value != null) {
            dataSource.setNumTestsPerEvictionRun(Integer.parseInt(value));
        }

        value = (String) properties.get(PROP_MINEVICTABLEIDLETIMEMILLIS);
        if (value != null) {
            dataSource.setMinEvictableIdleTimeMillis(Long.parseLong(value));
        }

        value = (String) properties.get(PROP_PHY_TIMEOUT_MILLIS);
        if (value != null) {
            dataSource.setPhyTimeoutMillis(Long.parseLong(value));
        }

        value = (String) properties.get(PROP_TESTWHILEIDLE);
        if (value != null) {
            dataSource.setTestWhileIdle(Boolean.valueOf(value).booleanValue());
        }

        value = (String) properties.get(PROP_PASSWORD);
        if (value != null) {
            dataSource.setPassword(value);
        }

        value = (String) properties.get(PROP_URL);
        if (value != null) {
            dataSource.setUrl(value);
        }

        value = (String) properties.get(PROP_USERNAME);
        if (value != null) {
            dataSource.setUsername(value);
        }

        value = (String) properties.get(PROP_VALIDATIONQUERY);
        if (value != null) {
            dataSource.setValidationQuery(value);
        }

        value = (String) properties.get(PROP_VALIDATIONQUERY_TIMEOUT);
        if (value != null) {
            dataSource.setValidationQueryTimeout(Integer.parseInt(value));
        }

        value = (String) properties.get(PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED);
        if (value != null) {
            dataSource.setAccessToUnderlyingConnectionAllowed(Boolean.valueOf(value).booleanValue());
        }

        value = (String) properties.get(PROP_REMOVEABANDONED);
        if (value != null) {
            dataSource.setRemoveAbandoned(Boolean.valueOf(value).booleanValue());
        }

        value = (String) properties.get(PROP_REMOVEABANDONEDTIMEOUT);
        if (value != null) {
            dataSource.setRemoveAbandonedTimeout(Integer.parseInt(value));
        }

        value = (String) properties.get(PROP_LOGABANDONED);
        if (value != null) {
            dataSource.setLogAbandoned(Boolean.valueOf(value).booleanValue());
        }

        value = (String) properties.get(PROP_POOLPREPAREDSTATEMENTS);
        if (value != null) {
            boolean poolPreparedStatements = Boolean.valueOf(value).booleanValue();
            dataSource.setPoolPreparedStatements(poolPreparedStatements);

            if (poolPreparedStatements) {
                value = (String) properties.get(PROP_MAXOPENPREPAREDSTATEMENTS);
                if (value != null) {
                    dataSource.setMaxOpenPreparedStatements(Integer.parseInt(value));
                }
            }
        }

        value = (String) properties.get(PROP_FILTERS);
        if (value != null) {
            dataSource.setFilters(value);
        }

        value = (String) properties.get(PROP_EXCEPTION_SORTER);
        if (value != null) {
            dataSource.setExceptionSorter(value);
        }

        value = (String) properties.get(PROP_EXCEPTION_SORTER_CLASS_NAME);
        if (value != null) {
            dataSource.setExceptionSorter(value);
        }

        value = (String) properties.get(PROP_INITCONNECTIONSQLS);
        if (value != null) {
            StringTokenizer tokenizer = new StringTokenizer(value, ";");
            dataSource.setConnectionInitSqls(Collections.list(tokenizer));
        }

        value = (String) properties.get(PROP_CONNECTIONPROPERTIES);
        if (value != null) {
            dataSource.setConnectionProperties(value);
        }

        {
            Properties dataSourceProperties = null;
            for (Map.Entry entry : properties.entrySet()) {
                String entryKey = (String) entry.getKey();
                if (entryKey.startsWith("druid.")) {
                    if (dataSourceProperties == null) {
                        dataSourceProperties = new Properties();
                    }

                    String entryValue = (String) entry.getValue();
                    dataSourceProperties.put(entryKey, entryValue);
                }
            }
            if (dataSourceProperties != null) {
                dataSource.configFromPropety(dataSourceProperties);
            }
        }

        // 执行DruidDataSource的 init 操作
        value = (String) properties.get(PROP_INIT);
        if ("true".equals(value)) {
            dataSource.init();
        }
    }
}
  • DruidDataSourceFactory通过createDataSource创建DruidDataSource。
  • DruidDataSourceFactory通过config进行参数设置,最后执行DruidDataSource的init方法进行初始化。
public class DruidDataSource extends DruidAbstractDataSource implements DruidDataSourceMBean, ManagedDataSource, Referenceable, Closeable, Cloneable, ConnectionPoolDataSource, MBeanRegistration {

    private final static Log                 LOG                       = LogFactory.getLog(DruidDataSource.class);
    private static final long                serialVersionUID          = 1L;
    // stats
    private volatile long                    recycleErrorCount         = 0L;
    private long                             connectCount              = 0L;
    private long                             closeCount                = 0L;
    private volatile long                    connectErrorCount         = 0L;
    private long                             recycleCount              = 0L;
    private long                             removeAbandonedCount      = 0L;
    private long                             notEmptyWaitCount         = 0L;
    private long                             notEmptySignalCount       = 0L;
    private long                             notEmptyWaitNanos         = 0L;
    private int                              keepAliveCheckCount       = 0;
    private int                              activePeak                = 0;
    private long                             activePeakTime            = 0;
    private int                              poolingPeak               = 0;
    private long                             poolingPeakTime           = 0;
    // store
    private volatile DruidConnectionHolder[] connections;
    private int                              poolingCount              = 0;
    private int                              activeCount               = 0;
    private volatile long                    discardCount              = 0;
    private int                              notEmptyWaitThreadCount   = 0;
    private int                              notEmptyWaitThreadPeak    = 0;
    //
    private DruidConnectionHolder[]          evictConnections;
    private DruidConnectionHolder[]          keepAliveConnections;

    // threads
    private volatile ScheduledFuture<?>      destroySchedulerFuture;
    private DestroyTask                      destroyTask;
    private volatile Future<?>               createSchedulerFuture;
    private CreateConnectionThread           createConnectionThread;
    private DestroyConnectionThread          destroyConnectionThread;
    private LogStatsThread                   logStatsThread;
    private int                              createTaskCount;
    private volatile long                    createTaskIdSeed          = 1L;
    private long[]                           createTasks;
    private final CountDownLatch             initedLatch               = new CountDownLatch(2);
    private volatile boolean                 enable                    = true;
    private boolean                          resetStatEnable           = true;
    private volatile long                    resetCount                = 0L;
    private String                           initStackTrace;
    private volatile boolean                 closing                   = false;
    private volatile boolean                 closed                    = false;
    private long                             closeTimeMillis           = -1L;
    protected JdbcDataSourceStat             dataSourceStat;
    private boolean                          useGlobalDataSourceStat   = false;
    private boolean                          mbeanRegistered           = false;
    public static ThreadLocal<Long>          waitNanosLocal            = new ThreadLocal<Long>();
    private boolean                          logDifferentThread        = true;
    private volatile boolean                 keepAlive                 = false;
    private boolean                          asyncInit                 = false;
    protected boolean                        killWhenSocketReadTimeout = false;
    private static List<Filter>              autoFilters               = null;
    private boolean                          loadSpifilterSkip         = false;
    private volatile DataSourceDisableException disableException       = null;
    protected static final AtomicLongFieldUpdater<DruidDataSource> recycleErrorCountUpdater
            = AtomicLongFieldUpdater.newUpdater(DruidDataSource.class, "recycleErrorCount");
    protected static final AtomicLongFieldUpdater<DruidDataSource> connectErrorCountUpdater
            = AtomicLongFieldUpdater.newUpdater(DruidDataSource.class, "connectErrorCount");
    protected static final AtomicLongFieldUpdater<DruidDataSource> resetCountUpdater
            = AtomicLongFieldUpdater.newUpdater(DruidDataSource.class, "resetCount");
    protected static final AtomicLongFieldUpdater<DruidDataSource> createTaskIdSeedUpdater
            = AtomicLongFieldUpdater.newUpdater(DruidDataSource.class, "createTaskIdSeed");

    public DruidDataSource(){
        this(false);
    }

    public DruidDataSource(boolean fairLock){
        // 父类DruidAbstractDataSource的构造函数初始化锁
        super(fairLock);
        // 通过System.getProperties()设置配置信息
        configFromPropety(System.getProperties());
    }

    public void configFromPropety(Properties properties) {
        {
            String property = properties.getProperty("druid.name");
            if (property != null) {
                this.setName(property);
            }
        }
        {
            String property = properties.getProperty("druid.url");
            if (property != null) {
                this.setUrl(property);
            }
        }
        {
            String property = properties.getProperty("druid.username");
            if (property != null) {
                this.setUsername(property);
            }
        }
        {
            String property = properties.getProperty("druid.password");
            if (property != null) {
                this.setPassword(property);
            }
        }
        {
            Boolean value = getBoolean(properties, "druid.testWhileIdle");
            if (value != null) {
                this.testWhileIdle = value;
            }
        }
        {
            Boolean value = getBoolean(properties, "druid.testOnBorrow");
            if (value != null) {
                this.testOnBorrow = value;
            }
        }
        {
            String property = properties.getProperty("druid.validationQuery");
            if (property != null && property.length() > 0) {
                this.setValidationQuery(property);
            }
        }
        {
            Boolean value = getBoolean(properties, "druid.useGlobalDataSourceStat");
            if (value != null) {
                this.setUseGlobalDataSourceStat(value);
            }
        }
        {
            Boolean value = getBoolean(properties, "druid.useGloalDataSourceStat"); // compatible for early versions
            if (value != null) {
                this.setUseGlobalDataSourceStat(value);
            }
        }
        {
            Boolean value = getBoolean(properties, "druid.asyncInit"); // compatible for early versions
            if (value != null) {
                this.setAsyncInit(value);
            }
        }
        {
            String property = properties.getProperty("druid.filters");

            if (property != null && property.length() > 0) {
                try {
                    this.setFilters(property);
                } catch (SQLException e) {
                    LOG.error("setFilters error", e);
                }
            }
        }
        {
            String property = properties.getProperty(Constants.DRUID_TIME_BETWEEN_LOG_STATS_MILLIS);
            if (property != null && property.length() > 0) {
                try {
                    long value = Long.parseLong(property);
                    this.setTimeBetweenLogStatsMillis(value);
                } catch (NumberFormatException e) {
                    LOG.error("illegal property '" + Constants.DRUID_TIME_BETWEEN_LOG_STATS_MILLIS + "'", e);
                }
            }
        }
        {
            String property = properties.getProperty(Constants.DRUID_STAT_SQL_MAX_SIZE);
            if (property != null && property.length() > 0) {
                try {
                    int value = Integer.parseInt(property);
                    if (dataSourceStat != null) {
                        dataSourceStat.setMaxSqlSize(value);
                    }
                } catch (NumberFormatException e) {
                    LOG.error("illegal property '" + Constants.DRUID_STAT_SQL_MAX_SIZE + "'", e);
                }
            }
        }
        {
            Boolean value = getBoolean(properties, "druid.clearFiltersEnable");
            if (value != null) {
                this.setClearFiltersEnable(value);
            }
        }
        {
            Boolean value = getBoolean(properties, "druid.resetStatEnable");
            if (value != null) {
                this.setResetStatEnable(value);
            }
        }
        {
            String property = properties.getProperty("druid.notFullTimeoutRetryCount");
            if (property != null && property.length() > 0) {
                try {
                    int value = Integer.parseInt(property);
                    this.setNotFullTimeoutRetryCount(value);
                } catch (NumberFormatException e) {
                    LOG.error("illegal property 'druid.notFullTimeoutRetryCount'", e);
                }
            }
        }
        {
            String property = properties.getProperty("druid.timeBetweenEvictionRunsMillis");
            if (property != null && property.length() > 0) {
                try {
                    long value = Long.parseLong(property);
                    this.setTimeBetweenEvictionRunsMillis(value);
                } catch (NumberFormatException e) {
                    LOG.error("illegal property 'druid.timeBetweenEvictionRunsMillis'", e);
                }
            }
        }
        {
            String property = properties.getProperty("druid.maxWaitThreadCount");
            if (property != null && property.length() > 0) {
                try {
                    int value = Integer.parseInt(property);
                    this.setMaxWaitThreadCount(value);
                } catch (NumberFormatException e) {
                    LOG.error("illegal property 'druid.maxWaitThreadCount'", e);
                }
            }
        }
        {
            String property = properties.getProperty("druid.maxWait");
            if (property != null && property.length() > 0) {
                try {
                    int value = Integer.parseInt(property);
                    this.setMaxWait(value);
                } catch (NumberFormatException e) {
                    LOG.error("illegal property 'druid.maxWait'", e);
                }
            }
        }
        {
            Boolean value = getBoolean(properties, "druid.failFast");
            if (value != null) {
                this.setFailFast(value);
            }
        }
        {
            String property = properties.getProperty("druid.phyTimeoutMillis");
            if (property != null && property.length() > 0) {
                try {
                    long value = Long.parseLong(property);
                    this.setPhyTimeoutMillis(value);
                } catch (NumberFormatException e) {
                    LOG.error("illegal property 'druid.phyTimeoutMillis'", e);
                }
            }
        }
        {
            String property = properties.getProperty("druid.phyMaxUseCount");
            if (property != null && property.length() > 0) {
                try {
                    long value = Long.parseLong(property);
                    this.setPhyMaxUseCount(value);
                } catch (NumberFormatException e) {
                    LOG.error("illegal property 'druid.phyMaxUseCount'", e);
                }
            }
        }
        {
            String property = properties.getProperty("druid.minEvictableIdleTimeMillis");
            if (property != null && property.length() > 0) {
                try {
                    long value = Long.parseLong(property);
                    this.setMinEvictableIdleTimeMillis(value);
                } catch (NumberFormatException e) {
                    LOG.error("illegal property 'druid.minEvictableIdleTimeMillis'", e);
                }
            }
        }
        {
            String property = properties.getProperty("druid.maxEvictableIdleTimeMillis");
            if (property != null && property.length() > 0) {
                try {
                    long value = Long.parseLong(property);
                    this.setMaxEvictableIdleTimeMillis(value);
                } catch (NumberFormatException e) {
                    LOG.error("illegal property 'druid.maxEvictableIdleTimeMillis'", e);
                }
            }
        }
        {
            Boolean value = getBoolean(properties, "druid.keepAlive");
            if (value != null) {
                this.setKeepAlive(value);
            }
        }
        {
            String property = properties.getProperty("druid.keepAliveBetweenTimeMillis");
            if (property != null && property.length() > 0) {
                try {
                    long value = Long.parseLong(property);
                    this.setKeepAliveBetweenTimeMillis(value);
                } catch (NumberFormatException e) {
                    LOG.error("illegal property 'druid.keepAliveBetweenTimeMillis'", e);
                }
            }
        }
        {
            Boolean value = getBoolean(properties, "druid.poolPreparedStatements");
            if (value != null) {
                this.setPoolPreparedStatements0(value);
            }
        }
        {
            Boolean value = getBoolean(properties, "druid.initVariants");
            if (value != null) {
                this.setInitVariants(value);
            }
        }
        {
            Boolean value = getBoolean(properties, "druid.initGlobalVariants");
            if (value != null) {
                this.setInitGlobalVariants(value);
            }
        }
        {
            Boolean value = getBoolean(properties, "druid.useUnfairLock");
            if (value != null) {
                this.setUseUnfairLock(value);
            }
        }
        {
            String property = properties.getProperty("druid.driverClassName");
            if (property != null) {
                this.setDriverClassName(property);
            }
        }
        {
            String property = properties.getProperty("druid.initialSize");
            if (property != null && property.length() > 0) {
                try {
                    int value = Integer.parseInt(property);
                    this.setInitialSize(value);
                } catch (NumberFormatException e) {
                    LOG.error("illegal property 'druid.initialSize'", e);
                }
            }
        }
        {
            String property = properties.getProperty("druid.minIdle");
            if (property != null && property.length() > 0) {
                try {
                    int value = Integer.parseInt(property);
                    this.setMinIdle(value);
                } catch (NumberFormatException e) {
                    LOG.error("illegal property 'druid.minIdle'", e);
                }
            }
        }
        {
            String property = properties.getProperty("druid.maxActive");
            if (property != null && property.length() > 0) {
                try {
                    int value = Integer.parseInt(property);
                    this.setMaxActive(value);
                } catch (NumberFormatException e) {
                    LOG.error("illegal property 'druid.maxActive'", e);
                }
            }
        }
        {
            Boolean value = getBoolean(properties, "druid.killWhenSocketReadTimeout");
            if (value != null) {
                setKillWhenSocketReadTimeout(value);
            }
        }
        {
            String property = properties.getProperty("druid.connectProperties");
            if (property != null) {
                this.setConnectionProperties(property);
            }
        }
        {
            String property = properties.getProperty("druid.maxPoolPreparedStatementPerConnectionSize");
            if (property != null && property.length() > 0) {
                try {
                    int value = Integer.parseInt(property);
                    this.setMaxPoolPreparedStatementPerConnectionSize(value);
                } catch (NumberFormatException e) {
                    LOG.error("illegal property 'druid.maxPoolPreparedStatementPerConnectionSize'", e);
                }
            }
        }
        {
            String property = properties.getProperty("druid.initConnectionSqls");
            if (property != null && property.length() > 0) {
                try {
                    StringTokenizer tokenizer = new StringTokenizer(property, ";");
                    setConnectionInitSqls(Collections.list(tokenizer));
                } catch (NumberFormatException e) {
                    LOG.error("illegal property 'druid.initConnectionSqls'", e);
                }
            }
        }
        {
            String property = System.getProperty("druid.load.spifilter.skip");
            if (property != null && !"false".equals(property)) {
                loadSpifilterSkip = true;
            }
        }
    }
}
  • DruidDataSource的构造函数通过父类DruidAbstractDataSource初始化锁。
  • DruidDataSource的构造函数通过configFromPropety初始化配置信息。
public abstract class DruidAbstractDataSource extends WrapperAdapter implements DruidAbstractDataSourceMBean, DataSource, DataSourceProxy, Serializable {

    private static final long                          serialVersionUID                          = 1L;
    private final static Log                           LOG                                       = LogFactory.getLog(DruidAbstractDataSource.class);
    public final static int                            DEFAULT_INITIAL_SIZE                      = 0;
    public final static int                            DEFAULT_MAX_ACTIVE_SIZE                   = 8;
    public final static int                            DEFAULT_MAX_IDLE                          = 8;
    public final static int                            DEFAULT_MIN_IDLE                          = 0;
    public final static int                            DEFAULT_MAX_WAIT                          = -1;
    public final static String                         DEFAULT_VALIDATION_QUERY                  = null;                                                //
    public final static boolean                        DEFAULT_TEST_ON_BORROW                    = false;
    public final static boolean                        DEFAULT_TEST_ON_RETURN                    = false;
    public final static boolean                        DEFAULT_WHILE_IDLE                        = true;
    public static final long                           DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS = 60 * 1000L;
    public static final long                           DEFAULT_TIME_BETWEEN_CONNECT_ERROR_MILLIS = 500;
    public static final int                            DEFAULT_NUM_TESTS_PER_EVICTION_RUN        = 3;
    public static final long                           DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS    = 1000L * 60L * 30L;
    public static final long                           DEFAULT_MAX_EVICTABLE_IDLE_TIME_MILLIS    = 1000L * 60L * 60L * 7;
    public static final long                           DEFAULT_PHY_TIMEOUT_MILLIS                = -1;
    protected volatile boolean                         defaultAutoCommit                         = true;
    protected volatile Boolean                         defaultReadOnly;
    protected volatile Integer                         defaultTransactionIsolation;
    protected volatile String                          defaultCatalog                            = null;
    protected String                                   name;
    protected volatile String                          username;
    protected volatile String                          password;
    protected volatile String                          jdbcUrl;
    protected volatile String                          driverClass;
    protected volatile ClassLoader                     driverClassLoader;
    protected volatile Properties                      connectProperties                         = new Properties();
    protected volatile PasswordCallback                passwordCallback;
    protected volatile NameCallback                    userCallback;
    protected volatile int                             initialSize                               = DEFAULT_INITIAL_SIZE;
    protected volatile int                             maxActive                                 = DEFAULT_MAX_ACTIVE_SIZE;
    protected volatile int                             minIdle                                   = DEFAULT_MIN_IDLE;
    protected volatile int                             maxIdle                                   = DEFAULT_MAX_IDLE;
    protected volatile long                            maxWait                                   = DEFAULT_MAX_WAIT;
    protected int                                      notFullTimeoutRetryCount                  = 0;
    protected volatile String                          validationQuery                           = DEFAULT_VALIDATION_QUERY;
    protected volatile int                             validationQueryTimeout                    = -1;
    protected volatile boolean                         testOnBorrow                              = DEFAULT_TEST_ON_BORROW;
    protected volatile boolean                         testOnReturn                              = DEFAULT_TEST_ON_RETURN;
    protected volatile boolean                         testWhileIdle                             = DEFAULT_WHILE_IDLE;
    protected volatile boolean                         poolPreparedStatements                    = false;
    protected volatile boolean                         sharePreparedStatements                   = false;
    protected volatile int                             maxPoolPreparedStatementPerConnectionSize = 10;
    protected volatile boolean                         inited                                    = false;
    protected volatile boolean                         initExceptionThrow                        = true;
    protected PrintWriter                              logWriter                                 = new PrintWriter(System.out);
    protected List<Filter>                             filters                                   = new CopyOnWriteArrayList<Filter>();
    private boolean                                    clearFiltersEnable                        = true;
    protected volatile ExceptionSorter                 exceptionSorter                           = null;
    protected Driver                                   driver;
    protected volatile int                             queryTimeout;
    protected volatile int                             transactionQueryTimeout;
    protected long                                     createTimespan;
    protected volatile int                             maxWaitThreadCount                        = -1;
    protected volatile boolean                         accessToUnderlyingConnectionAllowed       = true;
    protected volatile long                            timeBetweenEvictionRunsMillis             = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
    protected volatile int                             numTestsPerEvictionRun                    = DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
    protected volatile long                            minEvictableIdleTimeMillis                = DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
    protected volatile long                            maxEvictableIdleTimeMillis                = DEFAULT_MAX_EVICTABLE_IDLE_TIME_MILLIS;
    protected volatile long                            keepAliveBetweenTimeMillis                = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS * 2;
    protected volatile long                            phyTimeoutMillis                          = DEFAULT_PHY_TIMEOUT_MILLIS;
    protected volatile long                            phyMaxUseCount                            = -1;
    protected volatile boolean                         removeAbandoned;
    protected volatile long                            removeAbandonedTimeoutMillis              = 300 * 1000;
    protected volatile boolean                         logAbandoned;
    protected volatile int                             maxOpenPreparedStatements                 = -1;
    protected volatile List<String>                    connectionInitSqls;
    protected volatile String                          dbType;
    protected volatile long                            timeBetweenConnectErrorMillis             = DEFAULT_TIME_BETWEEN_CONNECT_ERROR_MILLIS;
    protected volatile ValidConnectionChecker          validConnectionChecker                    = null;
    protected final Map<DruidPooledConnection, Object> activeConnections                         = new IdentityHashMap<DruidPooledConnection, Object>();
    protected final static Object                      PRESENT                                   = new Object();
    protected long                                     id;
    protected int                                      connectionErrorRetryAttempts              = 1;
    protected boolean                                  breakAfterAcquireFailure                  = false;
    protected long                                     transactionThresholdMillis                = 0L;
    protected final Date                               createdTime                               = new Date();
    protected Date                                     initedTime;
    protected volatile long                            errorCount                                = 0L;
    protected volatile long                            dupCloseCount                             = 0L;
    protected volatile long                            startTransactionCount                     = 0L;
    protected volatile long                            commitCount                               = 0L;
    protected volatile long                            rollbackCount                             = 0L;
    protected volatile long                            cachedPreparedStatementHitCount           = 0L;
    protected volatile long                            preparedStatementCount                    = 0L;
    protected volatile long                            closedPreparedStatementCount              = 0L;
    protected volatile long                            cachedPreparedStatementCount              = 0L;
    protected volatile long                            cachedPreparedStatementDeleteCount        = 0L;
    protected volatile long                            cachedPreparedStatementMissCount          = 0L;
    final static AtomicLongFieldUpdater<DruidAbstractDataSource> errorCountUpdater                         = AtomicLongFieldUpdater.newUpdater(DruidAbstractDataSource.class, "errorCount");
    final static AtomicLongFieldUpdater<DruidAbstractDataSource> dupCloseCountUpdater                      = AtomicLongFieldUpdater.newUpdater(DruidAbstractDataSource.class, "dupCloseCount");
    final static AtomicLongFieldUpdater<DruidAbstractDataSource> startTransactionCountUpdater              = AtomicLongFieldUpdater.newUpdater(DruidAbstractDataSource.class, "startTransactionCount");
    final static AtomicLongFieldUpdater<DruidAbstractDataSource> commitCountUpdater                        = AtomicLongFieldUpdater.newUpdater(DruidAbstractDataSource.class, "commitCount");
    final static AtomicLongFieldUpdater<DruidAbstractDataSource> rollbackCountUpdater                      = AtomicLongFieldUpdater.newUpdater(DruidAbstractDataSource.class, "rollbackCount");
    final static AtomicLongFieldUpdater<DruidAbstractDataSource> cachedPreparedStatementHitCountUpdater    = AtomicLongFieldUpdater.newUpdater(DruidAbstractDataSource.class, "cachedPreparedStatementHitCount");
    final static AtomicLongFieldUpdater<DruidAbstractDataSource> preparedStatementCountUpdater             = AtomicLongFieldUpdater.newUpdater(DruidAbstractDataSource.class, "preparedStatementCount");
    final static AtomicLongFieldUpdater<DruidAbstractDataSource> closedPreparedStatementCountUpdater       = AtomicLongFieldUpdater.newUpdater(DruidAbstractDataSource.class, "closedPreparedStatementCount");
    final static AtomicLongFieldUpdater<DruidAbstractDataSource> cachedPreparedStatementCountUpdater       = AtomicLongFieldUpdater.newUpdater(DruidAbstractDataSource.class, "cachedPreparedStatementCount");
    final static AtomicLongFieldUpdater<DruidAbstractDataSource> cachedPreparedStatementDeleteCountUpdater = AtomicLongFieldUpdater.newUpdater(DruidAbstractDataSource.class, "cachedPreparedStatementDeleteCount");
    final static AtomicLongFieldUpdater<DruidAbstractDataSource> cachedPreparedStatementMissCountUpdater   = AtomicLongFieldUpdater.newUpdater(DruidAbstractDataSource.class, "cachedPreparedStatementMissCount");
    protected final Histogram                          transactionHistogram                      = new Histogram(1,
                                                                                                                 10,
                                                                                                                 100,
                                                                                                                 1000,
                                                                                                                 10 * 1000,
                                                                                                                 100 * 1000);
    private boolean                                    dupCloseLogEnable                         = false;
    private ObjectName                                 objectName;
    protected volatile long                            executeCount                              = 0L;
    protected volatile long                            executeQueryCount                         = 0L;
    protected volatile long                            executeUpdateCount                        = 0L;
    protected volatile long                            executeBatchCount                         = 0L;
    final static AtomicLongFieldUpdater<DruidAbstractDataSource> executeQueryCountUpdater = AtomicLongFieldUpdater.newUpdater(DruidAbstractDataSource.class, "executeQueryCount");
    final static AtomicLongFieldUpdater<DruidAbstractDataSource> executeUpdateCountUpdater = AtomicLongFieldUpdater.newUpdater(DruidAbstractDataSource.class, "executeUpdateCount");
    final static AtomicLongFieldUpdater<DruidAbstractDataSource> executeBatchCountUpdater = AtomicLongFieldUpdater.newUpdater(DruidAbstractDataSource.class, "executeBatchCount");
    final static AtomicLongFieldUpdater<DruidAbstractDataSource> executeCountUpdater = AtomicLongFieldUpdater.newUpdater(DruidAbstractDataSource.class, "executeCount");
    protected volatile Throwable                       createError;
    protected volatile Throwable                       lastError;
    protected volatile long                            lastErrorTimeMillis;
    protected volatile Throwable                       lastCreateError;
    protected volatile long                            lastCreateErrorTimeMillis;
    protected volatile long                            lastCreateStartTimeMillis;
    protected boolean                                  isOracle                                  = false;
    protected boolean                                  isMySql                                   = false;
    protected boolean                                  useOracleImplicitCache                    = true;
    protected ReentrantLock                            lock;
    protected Condition                                notEmpty;
    protected Condition                                empty;
    protected ReentrantLock                            activeConnectionLock                      = new ReentrantLock();
    protected volatile int                             createErrorCount                          = 0;
    protected volatile int                             creatingCount                             = 0;
    protected volatile int                             directCreateCount                         = 0;
    protected volatile long                            createCount                               = 0L;
    protected volatile long                            destroyCount                              = 0L;
    protected volatile long                            createStartNanos                          = 0L;
    final static AtomicIntegerFieldUpdater<DruidAbstractDataSource> createErrorCountUpdater      = AtomicIntegerFieldUpdater.newUpdater(DruidAbstractDataSource.class, "createErrorCount");
    final static AtomicIntegerFieldUpdater<DruidAbstractDataSource> creatingCountUpdater         = AtomicIntegerFieldUpdater.newUpdater(DruidAbstractDataSource.class, "creatingCount");
    final static AtomicIntegerFieldUpdater<DruidAbstractDataSource> directCreateCountUpdater     = AtomicIntegerFieldUpdater.newUpdater(DruidAbstractDataSource.class, "directCreateCount");
    final static AtomicLongFieldUpdater<DruidAbstractDataSource>    createCountUpdater           = AtomicLongFieldUpdater.newUpdater(DruidAbstractDataSource.class, "createCount");
    final static AtomicLongFieldUpdater<DruidAbstractDataSource>    destroyCountUpdater          = AtomicLongFieldUpdater.newUpdater(DruidAbstractDataSource.class, "destroyCount");
    final static AtomicLongFieldUpdater<DruidAbstractDataSource> createStartNanosUpdater         = AtomicLongFieldUpdater.newUpdater(DruidAbstractDataSource.class, "createStartNanos");
    private Boolean                                    useUnfairLock                             = null;
    private boolean                                    useLocalSessionState                      = true;
    protected long                                     timeBetweenLogStatsMillis;
    protected DruidDataSourceStatLogger                statLogger                                = new DruidDataSourceStatLoggerImpl();
    private boolean                                    asyncCloseConnectionEnable                = false;
    protected int                                      maxCreateTaskCount                        = 3;
    protected boolean                                  failFast                                  = false;
    protected volatile int                             failContinuous                            = 0;
    protected volatile long                            failContinuousTimeMillis                  = 0L;
    protected ScheduledExecutorService                 destroyScheduler;
    protected ScheduledExecutorService                 createScheduler;
    final static AtomicLongFieldUpdater<DruidAbstractDataSource> failContinuousTimeMillisUpdater = AtomicLongFieldUpdater.newUpdater(DruidAbstractDataSource.class, "failContinuousTimeMillis");
    final static AtomicIntegerFieldUpdater<DruidAbstractDataSource> failContinuousUpdater        = AtomicIntegerFieldUpdater.newUpdater(DruidAbstractDataSource.class, "failContinuous");
    protected boolean                                  initVariants                              = false;
    protected boolean                                  initGlobalVariants                        = false;
    protected volatile boolean                         onFatalError                              = false;
    protected volatile int                             onFatalErrorMaxActive                     = 0;
    protected volatile int                             fatalErrorCount                           = 0;
    protected volatile int                             fatalErrorCountLastShrink                 = 0;
    protected volatile long                            lastFatalErrorTimeMillis                  = 0;
    protected volatile String                          lastFatalErrorSql                         = null;
    protected volatile Throwable                       lastFatalError                            = null;

    public DruidAbstractDataSource(boolean lockFair){
        lock = new ReentrantLock(lockFair);

        notEmpty = lock.newCondition();
        empty = lock.newCondition();
    }
}
  • ReentrantLock lock:创建Connection的线程、销毁的线程、获取连接的线程,需要获得重入锁,才可以对内部数据进行操作,当然只是在需要的地方加锁。
  • Condition notEmpty:如果获取连接的线程发现连接池空了,一方面会唤醒empty,另外一方面自己会调用notEmpty.await()进入等待,由CreateConnectionThread唤醒,或者其他线程释放连接时唤醒。
  • Condition empty:CreateConnectionThread只有在连接池不够用的情况下才会创建,否则调用empty.await()挂起线程。如果获取连接的线程发现连接全部被拿走了,则会调用empty.signal()唤醒CreateConnectionThread创建连接,同时也会调用notEmpty.await()进入等待。
public class DruidDataSource extends DruidAbstractDataSource implements DruidDataSourceMBean, ManagedDataSource, Referenceable, Closeable, Cloneable, ConnectionPoolDataSource, MBeanRegistration {

    public void init() throws SQLException {
        // 由volatile修饰的inited,每次获取链接也会调用 init()方法
        if (inited) {
            return;
        }

        // bug fixed for dead lock, for issue #2980
        DruidDriver.getInstance();

        final ReentrantLock lock = this.lock;
        try {
            lock.lockInterruptibly();
        } catch (InterruptedException e) {
            throw new SQLException("interrupt", e);
        }

        boolean init = false;
        try {
            if (inited) {
                return;
            }

            initStackTrace = Utils.toString(Thread.currentThread().getStackTrace());

            this.id = DruidDriver.createDataSourceId();
            if (this.id > 1) {
                long delta = (this.id - 1) * 100000;
                this.connectionIdSeedUpdater.addAndGet(this, delta);
                this.statementIdSeedUpdater.addAndGet(this, delta);
                this.resultSetIdSeedUpdater.addAndGet(this, delta);
                this.transactionIdSeedUpdater.addAndGet(this, delta);
            }

            if (this.jdbcUrl != null) {
                this.jdbcUrl = this.jdbcUrl.trim();
                initFromWrapDriverUrl();
            }
            // 初始化filters对象
            for (Filter filter : filters) {
                filter.init(this);
            }

            if (this.dbType == null || this.dbType.length() == 0) {
                this.dbType = JdbcUtils.getDbType(jdbcUrl, null);
            }

            if (JdbcConstants.MYSQL.equals(this.dbType)
                    || JdbcConstants.MARIADB.equals(this.dbType)
                    || JdbcConstants.ALIYUN_ADS.equals(this.dbType)) {
                boolean cacheServerConfigurationSet = false;
                if (this.connectProperties.containsKey("cacheServerConfiguration")) {
                    cacheServerConfigurationSet = true;
                } else if (this.jdbcUrl.indexOf("cacheServerConfiguration") != -1) {
                    cacheServerConfigurationSet = true;
                }
                if (cacheServerConfigurationSet) {
                    this.connectProperties.put("cacheServerConfiguration", "true");
                }
            }
            
            // 省略参数检查的代码

            if (this.driverClass != null) {
                this.driverClass = driverClass.trim();
            }

            initFromSPIServiceLoader();

            if (this.driver == null) {
                if (this.driverClass == null || this.driverClass.isEmpty()) {
                    this.driverClass = JdbcUtils.getDriverClassName(this.jdbcUrl);
                }

                if (MockDriver.class.getName().equals(driverClass)) {
                    driver = MockDriver.instance;
                } else {
                    if (jdbcUrl == null && (driverClass == null || driverClass.length() == 0)) {
                        throw new SQLException("url not set");
                    }
                    driver = JdbcUtils.createDriver(driverClassLoader, driverClass);
                }
            } else {
                if (this.driverClass == null) {
                    this.driverClass = driver.getClass().getName();
                }
            }

            initCheck();

            initExceptionSorter();
            initValidConnectionChecker();
            validationQueryCheck();

            if (isUseGlobalDataSourceStat()) {
                dataSourceStat = JdbcDataSourceStat.getGlobal();
                if (dataSourceStat == null) {
                    dataSourceStat = new JdbcDataSourceStat("Global", "Global", this.dbType);
                    JdbcDataSourceStat.setGlobal(dataSourceStat);
                }
                if (dataSourceStat.getDbType() == null) {
                    dataSourceStat.setDbType(this.dbType);
                }
            } else {
                dataSourceStat = new JdbcDataSourceStat(this.name, this.jdbcUrl, this.dbType, this.connectProperties);
            }
            dataSourceStat.setResetStatEnable(this.resetStatEnable);

            connections = new DruidConnectionHolder[maxActive];
            evictConnections = new DruidConnectionHolder[maxActive];
            keepAliveConnections = new DruidConnectionHolder[maxActive];

            SQLException connectError = null;

            if (createScheduler != null && asyncInit) {
                for (int i = 0; i < initialSize; ++i) {
                    submitCreateTask(true);
                }
            } else if (!asyncInit) {
                // 初始化连接数
                while (poolingCount < initialSize) {
                    try {
                        // 创建连接
                        PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection();
                        DruidConnectionHolder holder = new DruidConnectionHolder(this, pyConnectInfo);
                        connections[poolingCount++] = holder;
                    } catch (SQLException ex) {
                        LOG.error("init datasource error, url: " + this.getUrl(), ex);
                        if (initExceptionThrow) {
                            connectError = ex;
                            break;
                        } else {
                            Thread.sleep(3000);
                        }
                    }
                }

                if (poolingCount > 0) {
                    poolingPeak = poolingCount;
                    poolingPeakTime = System.currentTimeMillis();
                }
            }
            // 创建各类线程,包括连接创建线程和连接销毁线程
            createAndLogThread();
            createAndStartCreatorThread();
            createAndStartDestroyThread();
            
            // 等待线程启动完成
            initedLatch.await();
            init = true;

            initedTime = new Date();
            registerMbean();

            if (keepAlive) {
                // async fill to minIdle
                if (createScheduler != null) {
                    for (int i = 0; i < minIdle; ++i) {
                        submitCreateTask(true);
                    }
                } else {
                    this.emptySignal();
                }
            }

        } catch (SQLException e) {
            throw e;
        } finally {
            inited = true;
            lock.unlock();
            }
        }
    }
}
  • 加载数据库驱动Driver
  • 根据不同的数据库,实例化ExceptionSorter,主要的api就是isExceptionFatal(SQLException e),用于判断是否是Fatal级别的异常。
  • 初始化连接检测器,不同数据库的实现不一样,比如mysql是调用pingInternal检测连接是否OK。ValidConnectionChecker在获取连接、回收连接的时候会用到
  • 初始化JdbcDataSourceStat,主要目的是做监控。
  • 初始化connections、evictConnections、keepAliveConnections数组,分别用于存放可被获取的连接池、待清理的连接池、存活的连接池,数组的大小都是maxActive。
  • 初始化initialSize个Connection。
  • 开启LogStatsThread线程,用于定期打印DruidDataSource的一些数据,默认是不开启的,需要开启的话只需要设置timeBetweenLogStatsMillis指定打印的时间周期,log步骤需要获取主锁,建议时间不要设得太短。
  • 创建CreateConnectionThread线程,druid内部默认使用一个线程异步地创建连接,当然可以指定createScheduler线程池,开启多个线程创建连接,但是请把keepAlive设为true,否则不会开启异步线程创建连接。
  • 创建DestroyConnectionThread线程,定期扫描连接池内过期的连接,如果想对连接池外面正在使用的连接也进行清理的话,需要指定removeAbandoned为true,清理线程会判断连接是否正在使用,是否超过了清理时间而进行清理。

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容