手写Mybatis:第6章-数据源池化技术实现

文章目录

  • 一、目标:数据源池化技术实现
  • 二、设计:数据源池化技术实现
  • 三、实现:数据源池化技术实现
    • 3.1 工程结构
    • 3.2 数据源池化技术关系图
    • 3.3 无池化链接实现
    • 3.4 有池化链接实现
      • 3.4.1 有连接的数据源
      • 3.4.2 池化链接的代理
      • 3.4.3 池状态定义
      • 3.4.4 pushConnection 回收链接
      • 3.4.5 popConnection 获取链接
    • 3.5 数据源工厂
      • 3.5.1 无池化工厂
      • 3.5.2 有池化工厂
    • 3.6 新增类型别名注册器
  • 四、测试:数据源池化技术实现
    • 4.1 配置数据源
      • 4.1.1 无池化:UNPOOLED
      • 4.1.2 有池化:POOLED
    • 4.2 单元测试
      • 4.2.1 基础测试
      • 4.2.2 无池化测试结果:UNPOOLED
      • 4.2.3 有池化测试结果:POOLED
    • 4.3 连接池验证
  • 五、总结:数据源池化技术实现

一、目标:数据源池化技术实现

💡 Mybatis 中自带的数据源实现?

  • 无池化 UnpooledDataSource 实现。
  • 有池化 pooledDataSource 实现,池化配置属性的理解:最大活跃连接数、空闲连接数、检测时长等。

二、设计:数据源池化技术实现

💡 池化技术理解为亨元模式的具体实现方案:对一些需要较高创建成本且高频使用的资源,需要进行缓存或者也称预热处理。

  • 池化技术:把一些资源存放到一个预热池子中,需要用的时候从池子中获取,使用完毕在进行使用。
  • 通过池化可以非常有效的控制资源的使用成本,包括:资源数量、空闲时长、获取方式等进行统一控制和管理

在这里插入图片描述

  • 通过提供统一的数据池中心,存放数据源连接,并根据配置按照请求获取连接的操作,创建连接池的数据源连接数量。
    • 包括:最大空闲连接和最大活跃连接,都随着创建过程被控制。
  • 此外由于控制了连接池中连接的数量,所以当外部从连接池获取连接时,如果连接已满则会进行循环等待。
    • 案例:使用DB连接池,如果一个 SQL 操作引起了慢查询,则会导致整个服务进入瘫痪的阶段,各个和数据库相关的接口调用,都不能获得到连接,接口查询 TP99 徒然提高。
  • 那连接池可以配置的很大吗?
    • 不可以. 因为连接池要和数据所分配的连接池对应上,避免应用配置连接池超过数据库所提供的连接池数量,否则会出现 夯住不能分配链接 的问题,导致数据库拖垮从而引起连锁反应.

三、实现:数据源池化技术实现

3.1 工程结构

mybatis-step-05
|-src|-main|	|-java|		|-com.lino.mybatis|			|-binding|			|	|-MapperMethod.java|			|	|-MapperProxy.java|			|	|-MapperProxyFactory.java|			|	|-MapperRegistry.java|			|-builder|			|	|-xml|			|	|	|-XMLConfigBuilder.java|			|	|-BaseBuilder.java|			|-datasource|			|	|-druid|			|	|	|-DruidDataSourceFacroty.java|			|	|-pooled|			|	|	|-PooledConnection.java|			|	|	|-PooledDataSource.java|			|	|	|-PooledDataSourceFacroty.java|			|	|	|-PoolState.java|			|	|-unpooled|			|	|	|-UnpooledDataSource.java|			|	|	|-UnpooledDataSourceFacroty.java|			|	|-DataSourceFactory.java|			|-io|			|	|-Resources.java|			|-mapping|			|	|-BoundSql.java|			|	|-Environment.java|			|	|-MappedStatement.java|			|	|-ParameterMapping.java|			|	|-SqlCommandType.java|			|-session|			|	|-defaults|			|	|	|-DefaultSqlSession.java|			|	|	|-DefaultSqlSessionFactory.java|			|	|-Configuration.java|			|	|-SqlSession.java|			|	|-SqlSessionFactory.java|			|	|-SqlSessionFactoryBuilder.java|			|	|-TransactionIsolationLevel.java|			|-transaction|			|	|-jdbc|			|	|	|-JdbcTransaction.java|			|	|	|-JdbcTransactionFactory.java|			|	|-Transaction.java|			|	|-TransactionFactory.java|			|-type|			|	|-JdbcType.java|			|	|-TypeAliasRegistry.java|-test|-java|	|-com.lino.mybatis.test|	|-dao|	|	|-IUserDao.java|	|-po|	|	|-User.java|	|-ApiTest.java|-resources|-mapper|	|-User_Mapper.xml|-mybatis-config-datasource.xml

3.2 数据源池化技术关系图

在这里插入图片描述

  • Mybatis 数据源的实现中,包括两部分:
    • 无池化的 UnpooledDataSource 实现类。
    • 有池化的 PooledDataSource 实现类,对无池化的 UnpooledDataSource 进行扩展处理。把创建出来的链接保存到内存中,记录为空闲链接和活跃链接,在不同的阶段使用。
  • PooledConnection 是对链接的代理操作,通过 invoke 方法的反射调用,对关闭的链接进行回收处理,并使用 notifyAll 通知正在等待链接的用户进行抢链接。
  • DataSourceFactory 数据源工厂接口的实现,由无池化工厂实现后,有池化工厂继承的方式进行处理。

3.3 无池化链接实现

UnpooledDataSource.java

package com.lino.mybatis.datasource.unpooled;import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.*;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;/*** @description: 无池化数据源实现*/
public class UnpooledDataSource implements DataSource {/*** 类加载器*/private ClassLoader driverClassLoader;/*** 驱动配置,也可以扩展属性信息:driver.encoding=UTF8*/private Properties driverProperties;/*** 驱动注册器*/private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap<>();/*** 驱动*/private String driver;/*** DB连接地址*/private String url;/*** 账号*/private String username;/*** 密码*/private String password;/*** 是否自动提交*/private Boolean autoCommit;/*** 事务隔离级别*/private Integer defaultTransactionIsolationLevel;static {Enumeration<Driver> drivers = DriverManager.getDrivers();while (drivers.hasMoreElements()) {Driver driver = drivers.nextElement();registeredDrivers.put(driver.getClass().getName(), driver);}}/*** 驱动代理*/private static class DriverProxy implements Driver {private Driver driver;DriverProxy(Driver driver) {this.driver = driver;}@Overridepublic Connection connect(String url, Properties info) throws SQLException {return this.driver.connect(url, info);}@Overridepublic boolean acceptsURL(String url) throws SQLException {return this.driver.acceptsURL(url);}@Overridepublic DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {return this.driver.getPropertyInfo(url, info);}@Overridepublic int getMajorVersion() {return this.driver.getMajorVersion();}@Overridepublic int getMinorVersion() {return this.driver.getMinorVersion();}@Overridepublic boolean jdbcCompliant() {return this.driver.jdbcCompliant();}@Overridepublic Logger getParentLogger() throws SQLFeatureNotSupportedException {return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);}}/*** 初始化驱动* 资料:https://www.kfu.com/~nsayer/Java/dyn-jdbc.html** @throws SQLException SQL异常*/private synchronized void initializerDriver() throws SQLException {if (!registeredDrivers.containsKey(driver)) {try {Class<?> driverType = Class.forName(driver, true, driverClassLoader);Driver driverInstance = (Driver) driverType.newInstance();DriverManager.registerDriver(new DriverProxy(driverInstance));registeredDrivers.put(driver, driverInstance);} catch (Exception e) {throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);}}}private Connection doGetConnection(String username, String password) throws SQLException {Properties props = new Properties();if (driverProperties != null) {props.putAll(driverProperties);}if (username != null) {props.setProperty("user", username);}if (password != null) {props.setProperty("password", password);}return doGetConnection(props);}private Connection doGetConnection(Properties properties) throws SQLException {initializerDriver();Connection connection = DriverManager.getConnection(url, properties);if (autoCommit != null && autoCommit != connection.getAutoCommit()) {connection.setAutoCommit(autoCommit);}if (defaultTransactionIsolationLevel != null) {connection.setTransactionIsolation(defaultTransactionIsolationLevel);}return connection;}@Overridepublic Connection getConnection() throws SQLException {return doGetConnection(username, password);}@Overridepublic Connection getConnection(String username, String password) throws SQLException {return doGetConnection(username, password);}@Overridepublic <T> T unwrap(Class<T> iface) throws SQLException {throw new SQLException(getClass().getName() + "is not a wrapper.");}@Overridepublic boolean isWrapperFor(Class<?> iface) throws SQLException {return false;}@Overridepublic PrintWriter getLogWriter() throws SQLException {return DriverManager.getLogWriter();}@Overridepublic void setLogWriter(PrintWriter out) throws SQLException {DriverManager.setLogWriter(out);}@Overridepublic void setLoginTimeout(int seconds) throws SQLException {DriverManager.setLoginTimeout(seconds);}@Overridepublic int getLoginTimeout() throws SQLException {return DriverManager.getLoginTimeout();}@Overridepublic Logger getParentLogger() throws SQLFeatureNotSupportedException {return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);}public ClassLoader getDriverClassLoader() {return driverClassLoader;}public void setDriverClassLoader(ClassLoader driverClassLoader) {this.driverClassLoader = driverClassLoader;}public Properties getDriverProperties() {return driverProperties;}public void setDriverProperties(Properties driverProperties) {this.driverProperties = driverProperties;}public static Map<String, Driver> getRegisteredDrivers() {return registeredDrivers;}public static void setRegisteredDrivers(Map<String, Driver> registeredDrivers) {UnpooledDataSource.registeredDrivers = registeredDrivers;}public String getDriver() {return driver;}public void setDriver(String driver) {this.driver = driver;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public Boolean getAutoCommit() {return autoCommit;}public void setAutoCommit(Boolean autoCommit) {this.autoCommit = autoCommit;}public Integer getDefaultTransactionIsolationLevel() {return defaultTransactionIsolationLevel;}public void setDefaultTransactionIsolationLevel(Integer defaultTransactionIsolationLevel) {this.defaultTransactionIsolationLevel = defaultTransactionIsolationLevel;}
}
  • 无池化的数据源连接实现:核心在于 initializerDriver 初始化驱动中使用 Class.forNamenewInstance 的方式创建了数据源连接操作。
  • 在创建完成链接之后,把链接存放到驱动注册器中,方便后续使用中可以直接获取链接,避免重复创建所带来的资源消耗。

3.4 有池化链接实现

💡 有池化的数据源链接,核心在于对无池化链接的包装,同时提供了相应的池化技术实现。

  • 包括:pushConnection、popConnection、forceCloseAll、pingConnection 的操作处理。
  • 当用户想要获取链接时,则会从连接池中获取链接,同时判断是否有空闲链接、最大活跃链接多少,以及是否需要等待处理或是最终抛出异常。

3.4.1 有连接的数据源

PooledDataSource.java

package com.lino.mybatis.datasource.pooled;import com.lino.mybatis.datasource.unpooled.UnpooledDataSource;
import org.slf4j.LoggerFactory;
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.sql.*;
import java.util.logging.Logger;/*** @description: 有连接池的数据源*/
public class PooledDataSource implements DataSource {private org.slf4j.Logger logger = LoggerFactory.getLogger(PooledDataSource.class);/*** 池状态*/private final PoolState state = new PoolState(this);/*** 无池化数据源*/private final UnpooledDataSource dataSource;/*** 活跃连接数*/protected int poolMaximumActiveConnections = 10;/*** 空闲连接数*/protected int poolMaximumIdleConnections = 5;/*** 在被强制返回之前,池中连接被检查的时间*/protected int poolMaximumCheckoutTime = 20000;/*** 这是给连接池一个打印日志状态机会的低层次设置,还有重新尝试获得连接,这些情况下往往需要很长时间,为了避免连接池没有配置时静默失败*/protected int poolTimeToWait = 20000;/*** 发送到数据的侦测查询,用来验证连接是否正常工作,并且准备接受请求。* 默认是 “NO PING QUERY SET”,这回引起许多数据库驱动连接由一个错误信息而导致失败*/protected String poolPingQuery = "NO PING QUERY SET";/*** 开启或禁用侦测查询*/protected boolean poolPingEnabled = false;/*** 用来配置 poolPingQuery 多长时间被用一次*/protected int poolPingConnectionsNotUsedFor = 0;private int expectedConnectionTypeCode;public PooledDataSource() {this.dataSource = new UnpooledDataSource();}protected void pushConnection(PooledConnection connection) throws SQLException {synchronized (state) {state.activeConnections.remove(connection);// 判断连接是否有效if (connection.isValid()) {// 如果空闲连接小于设定数量,也就是太少时if (state.idleConnections.size() < poolMaximumIdleConnections && connection.getConnectionTypeCode() == expectedConnectionTypeCode) {state.accumulatedCheckoutTime += connection.getCheckOutTime();if (!connection.getRealConnection().getAutoCommit()) {connection.getRealConnection().rollback();}// 实例化一个新的DB连接,加入到idle列表PooledConnection newConnection = new PooledConnection(connection.getRealConnection(), this);state.idleConnections.add(newConnection);newConnection.setCreatedTimestamp(connection.getCreatedTimestamp());newConnection.setLastUsedTimestamp(connection.getLastUsedTimestamp());connection.invalidate();logger.info("Returned connection " + newConnection.getRealHashCode() + " to pool.");// 通知其他线程可以来抢DB连接了state.notifyAll();}// 否则,空闲连接还比较充足else {state.accumulatedCheckoutTime += connection.getCheckOutTime();if (!connection.getRealConnection().getAutoCommit()) {connection.getRealConnection().rollback();}// 将connection关闭connection.getRealConnection().close();logger.info("Closed connection " + connection.getRealHashCode() + ".");connection.invalidate();}} else {logger.info("A bad connection (" + connection.getRealHashCode() + ") attempted to return to the pool, discarding connection.");state.badConnectionCount++;}}}private PooledConnection popConnection(String username, String password) throws SQLException {boolean countteWait = false;PooledConnection conn = null;long t = System.currentTimeMillis();int localBadConnectionCount = 0;while (conn == null) {synchronized (state) {// 如果有空闲连接:返回第一个if (!state.idleConnections.isEmpty()) {conn = state.idleConnections.remove(0);logger.info("Check out connention " + conn.getRealHashCode() + " form pool.");}// 如果无空闲连接:创建新的连接else {// 活跃连接数不足if (state.activeConnections.size() < poolMaximumActiveConnections) {conn = new PooledConnection(dataSource.getConnection(), this);logger.info("Created connention " + conn.getRealHashCode() + ".");}// 活跃连接数已满else {// 取得活跃连接列表的第一个,也就是最老的一个连接PooledConnection oldestActiveConnection = state.activeConnections.get(0);long longestCheckoutTime = oldestActiveConnection.getCheckOutTime();// 如果checkout时间过长,则这个连接标记为过期if (longestCheckoutTime > poolMaximumCheckoutTime) {state.claimedOverdueConnectionCount++;state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;state.accumulatedCheckoutTime += longestCheckoutTime;state.activeConnections.remove(oldestActiveConnection);if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {oldestActiveConnection.getRealConnection().rollback();}// 删掉最老的连接,然后重新实例化一个新的连接conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);oldestActiveConnection.invalidate();logger.info("Claimed overdue connention " + conn.getRealHashCode() + ".");}// 如果checkout超时时间不够长,则等待else {try {if (!countteWait) {state.hadToWaitCount++;countteWait = true;}logger.info("Waiting as long as " + poolTimeToWait + " millisecond for connection.");long wt = System.currentTimeMillis();state.wait(poolTimeToWait);state.accumulatedWaitTime += System.currentTimeMillis() - wt;} catch (InterruptedException e) {break;}}}}// 获得到连接if (conn != null) {if (conn.isValid()) {if (!conn.getRealConnection().getAutoCommit()) {conn.getRealConnection().rollback();}conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));// 记录checkout时间conn.setCheckOutTimestamp(System.currentTimeMillis());conn.setLastUsedTimestamp(System.currentTimeMillis());state.activeConnections.add(conn);state.requestCount++;state.accumulatedCheckoutTime += System.currentTimeMillis() - t;} else {logger.info("A bad connection (" + conn.getRealHashCode() + ") was returned form the pool, getting another connection.");// 如果没拿到,统计信息:失败连接 +1state.badConnectionCount++;localBadConnectionCount++;conn = null;// 失败次数较多:抛异常if (localBadConnectionCount > (poolMaximumIdleConnections + 3)) {logger.debug("PooledDataSource: Could not get a good connection to the database.");throw new SQLException("PooledDataSource: Could not get a good connection to the database.");}}}}}if (conn == null) {logger.debug("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");throw new SQLException("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");}return conn;}public void forceCloseAll() {synchronized (state) {expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());// 关闭活跃连接for (int i = state.activeConnections.size(); i > 0; i--) {try {PooledConnection conn = state.activeConnections.remove(i - 1);conn.invalidate();Connection realConn = conn.getRealConnection();if (!realConn.getAutoCommit()) {realConn.rollback();}realConn.close();} catch (Exception ignore) {}}// 关闭空闲连接for (int i = state.idleConnections.size(); i > 0; i--) {try {PooledConnection conn = state.idleConnections.remove(i - 1);conn.invalidate();Connection realConn = conn.getRealConnection();if (!realConn.getAutoCommit()) {realConn.rollback();}realConn.close();} catch (Exception ignore) {}}logger.info("PooledDataSource forcefully closed/removed all connections.");}}protected boolean pingConnection(PooledConnection conn) {boolean result = true;try {result = !conn.getRealConnection().isClosed();} catch (SQLException e) {logger.info("Connection " + conn.getRealHashCode() + " is BAD: " + e.getMessage());result = false;}if (result) {if (poolPingEnabled) {if (poolPingConnectionsNotUsedFor >= 0 && conn.getTimeElapsedSinceLastUse() > poolPingConnectionsNotUsedFor) {try {logger.info("Testing connection " + conn.getRealHashCode() + " ...");Connection realConn = conn.getRealConnection();Statement statement = realConn.createStatement();ResultSet resultSet = statement.executeQuery(poolPingQuery);resultSet.close();if (!realConn.getAutoCommit()) {realConn.rollback();}result = true;logger.info("Connection " + conn.getRealHashCode() + " is GOOD!");} catch (Exception e) {logger.info("Execution of ping query '" + poolPingQuery + "' failed: " + e.getMessage());try {conn.getRealConnection().close();} catch (SQLException ignore) {}result = false;logger.info("Connection " + conn.getRealHashCode() + " is BAD: " + e.getMessage());}}}}return result;}public static Connection unwrapConnection(Connection conn) {if (Proxy.isProxyClass(conn.getClass())) {InvocationHandler handler = Proxy.getInvocationHandler(conn);if (handler instanceof javax.sql.PooledConnection) {return ((PooledConnection) handler).getRealConnection();}}return conn;}private int assembleConnectionTypeCode(String url, String username, String password) {return ("" + url + username + password).hashCode();}@Overridepublic Connection getConnection() throws SQLException {return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();}@Overridepublic Connection getConnection(String username, String password) throws SQLException {return popConnection(username, password).getProxyConnection();}protected void finalize() throws Throwable {forceCloseAll();super.finalize();}@Overridepublic <T> T unwrap(Class<T> iface) throws SQLException {throw new SQLException(getClass().getName() + " is not a wrapper.");}@Overridepublic boolean isWrapperFor(Class<?> iface) throws SQLException {return false;}@Overridepublic PrintWriter getLogWriter() throws SQLException {return DriverManager.getLogWriter();}@Overridepublic void setLogWriter(PrintWriter out) throws SQLException {DriverManager.setLogWriter(out);}@Overridepublic void setLoginTimeout(int seconds) throws SQLException {DriverManager.setLoginTimeout(seconds);}@Overridepublic int getLoginTimeout() throws SQLException {return DriverManager.getLoginTimeout();}@Overridepublic Logger getParentLogger() throws SQLFeatureNotSupportedException {return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);}public void setDriver(String driver) {dataSource.setDriver(driver);forceCloseAll();}public void setUrl(String url) {dataSource.setUrl(url);forceCloseAll();}public void setUsername(String username) {dataSource.setUsername(username);forceCloseAll();}public void setPassword(String password) {dataSource.setPassword(password);forceCloseAll();}public void setDefaultAutoCommit(boolean defaultAutoCommit) {dataSource.setAutoCommit(defaultAutoCommit);forceCloseAll();}public int getPoolMaximumActiveConnections() {return poolMaximumActiveConnections;}public void setPoolMaximumActiveConnections(int poolMaximumActiveConnections) {this.poolMaximumActiveConnections = poolMaximumActiveConnections;}public int getPoolMaximumIdleConnections() {return poolMaximumIdleConnections;}public void setPoolMaximumIdleConnections(int poolMaximumIdleConnections) {this.poolMaximumIdleConnections = poolMaximumIdleConnections;}public int getPoolMaximumCheckoutTime() {return poolMaximumCheckoutTime;}public void setPoolMaximumCheckoutTime(int poolMaximumCheckoutTime) {this.poolMaximumCheckoutTime = poolMaximumCheckoutTime;}public int getPoolTimeToWait() {return poolTimeToWait;}public void setPoolTimeToWait(int poolTimeToWait) {this.poolTimeToWait = poolTimeToWait;}public String getPoolPingQuery() {return poolPingQuery;}public void setPoolPingQuery(String poolPingQuery) {this.poolPingQuery = poolPingQuery;}public boolean isPoolPingEnabled() {return poolPingEnabled;}public void setPoolPingEnabled(boolean poolPingEnabled) {this.poolPingEnabled = poolPingEnabled;}public int getPoolPingConnectionsNotUsedFor() {return poolPingConnectionsNotUsedFor;}public void setPoolPingConnectionsNotUsedFor(int poolPingConnectionsNotUsedFor) {this.poolPingConnectionsNotUsedFor = poolPingConnectionsNotUsedFor;}public int getExpectedConnectionTypeCode() {return expectedConnectionTypeCode;}public void setExpectedConnectionTypeCode(int expectedConnectionTypeCode) {this.expectedConnectionTypeCode = expectedConnectionTypeCode;}
}

3.4.2 池化链接的代理

PooledConnection.java

package com.lino.mybatis.datasource.pooled;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Objects;/*** @description: 池化代理的链接*/
public class PooledConnection implements InvocationHandler {private static final String CLOSE = "close";private static final Class<?>[] IFACES = new Class<?>[]{Connection.class};private int hashCode = 0;private PooledDataSource dataSource;/*** 真实的连接*/private Connection realConnection;/*** 代理的连接*/private Connection proxyConnection;private long checkOutTimestamp;private long createdTimestamp;private long lastUsedTimestamp;private int connectionTypeCode;private boolean valid;public PooledConnection(Connection connection, PooledDataSource dataSource) {this.hashCode = connection.hashCode();this.realConnection = connection;this.dataSource = dataSource;this.createdTimestamp = System.currentTimeMillis();this.lastUsedTimestamp = System.currentTimeMillis();this.valid = true;this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String methodName = method.getName();// 如果是调用 CLOSE 关闭连接方法,则将连接加入连接池中,并返回nullif (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) {dataSource.pushConnection(this);return null;} else {if (!Object.class.equals(method.getDeclaringClass())) {// 除了toString()方法,其他方法调用之前要检查connection是否还是合法的,不合法要抛出SQL异常checkConnection();}// 其他方法交给connection去调用return method.invoke(realConnection, args);}}private void checkConnection() throws SQLException {if (!valid) {throw new SQLException("Error accessing PooledConnection. Connection is invalid.");}}public void invalidate() {valid = false;}public boolean isValid() {return valid && realConnection != null && dataSource.pingConnection(this);}public Connection getRealConnection() {return realConnection;}public Connection getProxyConnection() {return proxyConnection;}public int getRealHashCode() {return realConnection == null ? 0 : realConnection.hashCode();}public int getConnectionTypeCode() {return connectionTypeCode;}public void setConnectionTypeCode(int connectionTypeCode) {this.connectionTypeCode = connectionTypeCode;}public long getCreatedTimestamp() {return createdTimestamp;}public void setCreatedTimestamp(long createdTimestamp) {this.createdTimestamp = createdTimestamp;}public long getLastUsedTimestamp() {return lastUsedTimestamp;}public void setLastUsedTimestamp(long lastUsedTimestamp) {this.lastUsedTimestamp = lastUsedTimestamp;}public long getTimeElapsedSinceLastUse() {return System.currentTimeMillis() - lastUsedTimestamp;}public long getAge() {return System.currentTimeMillis() - createdTimestamp;}public long getCheckOutTimestamp() {return checkOutTimestamp;}public void setCheckOutTimestamp(long checkOutTimestamp) {this.checkOutTimestamp = checkOutTimestamp;}public long getCheckOutTime() {return System.currentTimeMillis() - checkOutTimestamp;}@Overridepublic int hashCode() {return hashCode;}@Overridepublic boolean equals(Object obj) {if (obj instanceof PooledConnection) {return realConnection.hashCode() == ((PooledConnection) obj).realConnection.hashCode();} else if (obj instanceof Connection) {return hashCode == obj.hashCode();} else {return false;}}
}
  • 当我们需要对链接进行池化处理,当链接调用一些 CLOSE 方法时,也需要把链接从池中关闭和恢复可用,允许其他用户获取到链接。
  • 那么这里就需要对连接类进行代理包装,处理 CLOSE 方法。
  • 通过 PooledConnection 实现 InvocationHandle#invoke 方法,包装代理链接,这样就可以对具体的调用方法进行控制。
  • invoke 方法中处理对 CLOSE 方法控制以外,排除 toStringObject 的方法后,则是其他真正需要被DB链接处理的方法。
  • 对于 CLOSE 方法的数据源回收操作 dataSource.pushConnection(this);有一个具体的实现方法,在池化实现类 PooledDataSource 中进行处理。

3.4.3 池状态定义

PoolState.java

package com.lino.mybatis.datasource.pooled;import java.util.ArrayList;
import java.util.List;/*** @description: 池状态*/
public class PoolState {protected PooledDataSource dataSource;/*** 空闲连接*/protected final List<PooledConnection> idleConnections = new ArrayList<>();/*** 活跃连接*/protected final List<PooledConnection> activeConnections = new ArrayList<>();/*** 请求次数*/protected long requestCount = 0;/*** 总请求时间*/protected long accumulatedRequestTime = 0;protected long accumulatedCheckoutTime = 0;protected long claimedOverdueConnectionCount = 0;protected long accumulatedCheckoutTimeOfOverdueConnections = 0;/*** 总等待时间*/protected long accumulatedWaitTime = 0;/*** 要等待的次数*/protected long hadToWaitCount = 0;/*** 失败连接次数*/protected long badConnectionCount = 0;public PoolState(PooledDataSource dataSource) {this.dataSource = dataSource;}public synchronized long getRequestCount() {return requestCount;}public synchronized long getAverageRequestTime() {return requestCount == 0 ? 0 : accumulatedRequestTime / requestCount;}public synchronized long getAverageWaitTime() {return hadToWaitCount == 0 ? 0 : accumulatedWaitTime / hadToWaitCount;}public synchronized long getHadToWaitCount() {return hadToWaitCount;}public synchronized long getBadConnectionCount() {return badConnectionCount;}public synchronized long getClaimedOverdueConnectionCount() {return claimedOverdueConnectionCount;}public synchronized long getAverageOverdueCheckoutTime() {return claimedOverdueConnectionCount == 0 ? 0 : accumulatedCheckoutTimeOfOverdueConnections / claimedOverdueConnectionCount;}public synchronized long getAverageCheckoutTime() {return requestCount == 0 ? 0 : accumulatedCheckoutTime / requestCount;}public synchronized int getIdleConnectionCount() {return idleConnections.size();}public synchronized int getActiveConnectionCount() {return activeConnections.size();}
}
  • 定义连接池状态。包括:空闲连接、活跃连接、请求次数、总请求时间、总等待时间、要等待的数次、失败连接次数等。
  • 给连接池状态添加 synchronized 锁,避免并发出现的问题。

3.4.4 pushConnection 回收链接

protected void pushConnection(PooledConnection connection) throws SQLException {synchronized (state) {state.activeConnections.remove(connection);// 判断连接是否有效if (connection.isValid()) {// 如果空闲连接小于设定数量,也就是太少时if (state.idleConnections.size() < poolMaximumIdleConnections && connection.getConnectionTypeCode() == expectedConnectionTypeCode) {state.accumulatedCheckoutTime += connection.getCheckOutTime();if (!connection.getRealConnection().getAutoCommit()) {connection.getRealConnection().rollback();}// 实例化一个新的DB连接,加入到idle列表PooledConnection newConnection = new PooledConnection(connection.getRealConnection(), this);state.idleConnections.add(newConnection);newConnection.setCreatedTimestamp(connection.getCreatedTimestamp());newConnection.setLastUsedTimestamp(connection.getLastUsedTimestamp());connection.invalidate();logger.info("Returned connection " + newConnection.getRealHashCode() + " to pool.");// 通知其他线程可以来抢DB连接了state.notifyAll();}// 否则,空闲连接还比较充足else {state.accumulatedCheckoutTime += connection.getCheckOutTime();if (!connection.getRealConnection().getAutoCommit()) {connection.getRealConnection().rollback();}// 将connection关闭connection.getRealConnection().close();logger.info("Closed connection " + connection.getRealHashCode() + ".");connection.invalidate();}} else {logger.info("A bad connection (" + connection.getRealHashCode() + ") attempted to return to the pool, discarding connection.");state.badConnectionCount++;}}
}
  • pooleadDataSource#pushConnection 数据源回收的处理中,核心在于 判断链接是否有效,以及进行相关的 空闲链接校验,判断是否把链接回到到 idle 空闲链接列表中,并通知其他线程来抢占。
  • 如果现在有空闲链接充足,那么这个回收的链接则会进行回滚和关闭的处理。
    • 回滚connection.getRealConnection().rollbak()
    • 关闭connection.getRealConnection().close()

3.4.5 popConnection 获取链接

private PooledConnection popConnection(String username, String password) throws SQLException {boolean countteWait = false;PooledConnection conn = null;long t = System.currentTimeMillis();int localBadConnectionCount = 0;while (conn == null) {synchronized (state) {// 如果有空闲连接:返回第一个if (!state.idleConnections.isEmpty()) {conn = state.idleConnections.remove(0);logger.info("Check out connention " + conn.getRealHashCode() + " form pool.");}// 如果无空闲连接:创建新的连接else {// 活跃连接数不足if (state.activeConnections.size() < poolMaximumActiveConnections) {conn = new PooledConnection(dataSource.getConnection(), this);logger.info("Created connention " + conn.getRealHashCode() + ".");}// 活跃连接数已满else {// 取得活跃连接列表的第一个,也就是最老的一个连接PooledConnection oldestActiveConnection = state.activeConnections.get(0);long longestCheckoutTime = oldestActiveConnection.getCheckOutTime();// 如果checkout时间过长,则这个连接标记为过期if (longestCheckoutTime > poolMaximumCheckoutTime) {state.claimedOverdueConnectionCount++;state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;state.accumulatedCheckoutTime += longestCheckoutTime;state.activeConnections.remove(oldestActiveConnection);if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {oldestActiveConnection.getRealConnection().rollback();}// 删掉最老的连接,然后重新实例化一个新的连接conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);oldestActiveConnection.invalidate();logger.info("Claimed overdue connention " + conn.getRealHashCode() + ".");}// 如果checkout超时时间不够长,则等待else {try {if (!countteWait) {state.hadToWaitCount++;countteWait = true;}logger.info("Waiting as long as " + poolTimeToWait + " millisecond for connection.");long wt = System.currentTimeMillis();state.wait(poolTimeToWait);state.accumulatedWaitTime += System.currentTimeMillis() - wt;} catch (InterruptedException e) {break;}}}}// 获得到连接if (conn != null) {if (conn.isValid()) {if (!conn.getRealConnection().getAutoCommit()) {conn.getRealConnection().rollback();}conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));// 记录checkout时间conn.setCheckOutTimestamp(System.currentTimeMillis());conn.setLastUsedTimestamp(System.currentTimeMillis());state.activeConnections.add(conn);state.requestCount++;state.accumulatedCheckoutTime += System.currentTimeMillis() - t;} else {logger.info("A bad connection (" + conn.getRealHashCode() + ") was returned form the pool, getting another connection.");// 如果没拿到,统计信息:失败连接 +1state.badConnectionCount++;localBadConnectionCount++;conn = null;// 失败次数较多:抛异常if (localBadConnectionCount > (poolMaximumIdleConnections + 3)) {logger.debug("PooledDataSource: Could not get a good connection to the database.");throw new SQLException("PooledDataSource: Could not get a good connection to the database.");}}}}}if (conn == null) {logger.debug("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");throw new SQLException("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");}return conn;
}
  • popConnection 获取链接是一个 while 死循环操作,只有获取到链接抛异常才会退出循环。
  • 获取链接的过程会使用 synchronized 进行加锁,因为所有线程在资源竞争的情况下,都需要进行加锁处理。
  • 在加锁的代码块中通过判断是否还有空闲链接进行返回,如果没有则会判断活跃连接数是否充足,不充足则进行创建后返回。
  • 在这里也会遇到活跃链接已经进行循环等待的过程,最后再不能获取则抛出异常。

3.5 数据源工厂

💡 数据源工厂包括两部分:分别是无池化和有池化,有池化的工厂继承无池化工厂。
mybatis 源码的实现类中,这样可以减少对 Properties 统一包装的反射方式的属性处理。

3.5.1 无池化工厂

UnpooledDataSourceFactory.java

package com.lino.mybatis.datasource.unpooled;import com.lino.mybatis.datasource.DataSourceFactory;
import javax.sql.DataSource;
import java.util.Properties;/*** @description: 无池化数据源工厂*/
public class UnpooledDataSourceFactory implements DataSourceFactory {protected Properties props;@Overridepublic void setProperties(Properties props) {this.props = props;}@Overridepublic DataSource getDataSource() {UnpooledDataSource unpooledDataSource = new UnpooledDataSource();unpooledDataSource.setDriver(props.getProperty("driver"));unpooledDataSource.setUrl(props.getProperty("url"));unpooledDataSource.setUsername(props.getProperty("username"));unpooledDataSource.setPassword(props.getProperty("password"));return unpooledDataSource;}
}
  • 简单包装 getDataSource 获取数据源处理,把必要的参数进行传递。

3.5.2 有池化工厂

PooledDataSourceFactory.java

package com.lino.mybatis.datasource.pooled;import com.lino.mybatis.datasource.unpooled.UnpooledDataSourceFactory;
import javax.sql.DataSource;/*** @description: 有连接池的数据源工厂*/
public class PooledDataSourceFactory extends UnpooledDataSourceFactory {@Overridepublic DataSource getDataSource() {PooledDataSource pooledDataSource = new PooledDataSource();pooledDataSource.setDriver(props.getProperty("driver"));pooledDataSource.setUrl(props.getProperty("url"));pooledDataSource.setUsername(props.getProperty("username"));pooledDataSource.setPassword(props.getProperty("password"));return pooledDataSource;}
}
  • 有池化的数据源工厂实现的也比较简单,只是继承 UnpooledDataSourceFactory 共用获取属性的能力,以及实例化出池化数据源。

3.6 新增类型别名注册器

Configuration.java

public Configuration() {typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);typeAliasRegistry.registerAlias("DRUID", DruidDataSourceFactory.class);typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
}
  • 将两个数据源和对应的工厂实现类配置到 Configuration 配置类中,这样在解析 XML 时根据不同的数据源类型获取和实例化对应的实现类。
  • 在构造方法 Configuration 添加 UNPOOLED、POOLED 两个数据源注册到类型注册器中,方便后续使用 XMLConfigBuilder#envirenmentElement 方法解析 XML 处理数据源时进行使用。

四、测试:数据源池化技术实现

4.1 配置数据源

4.1.1 无池化:UNPOOLED

mybatis-config-datasource.xml

<environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="UNPOOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment>
</environments>

4.1.2 有池化:POOLED

mybatis-config-datasource.xml

<environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment>
</environments>

4.2 单元测试

4.2.1 基础测试

ApiTest.java

@Test
public void test_SqlSessionFactory() throws IOException {// 1.从SqlSessionFactory中获取SqlSessionSqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config-datasource.xml"));SqlSession sqlSession = sqlSessionFactory.openSession();// 2.获取映射器对象IUserDao userDao = sqlSession.getMapper(IUserDao.class);// 3.测试验证for (int i = 0; i < 50; i++) {User user = userDao.queryUserInfoById(1L);logger.info("测试结果:{}", JSON.toJSONString(user));}
}
  • 在无池化和有池化的测试中,基础的测试单元不需要改变,仍是通过 SqlSessionFactory 中获取 SqlSession 并获得映射对象和执行方法调用。
  • 另外添加了50次的查询调用,便于验证连接池的创建和获取以及等待。
  • 变化的在于 mybatis-config-datasource.xmldataSource 数据源类型的调整 dataSource type="UNPOOLED/POOLED"

4.2.2 无池化测试结果:UNPOOLED

10:42:29.188 [main] INFO  com.lino.mybatis.test.ApiTest - 测试结果:{"id":1,"userHead":"1_04","userId":"10001","userName":"小灵哥"}
10:42:29.205 [main] INFO  com.lino.mybatis.test.ApiTest - 测试结果:{"id":1,"userHead":"1_04","userId":"10001","userName":"小灵哥"}
10:42:29.236 [main] INFO  com.lino.mybatis.test.ApiTest - 测试结果:{"id":1,"userHead":"1_04","userId":"10001","userName":"小灵哥"}
10:42:29.252 [main] INFO  com.lino.mybatis.test.ApiTest - 测试结果:{"id":1,"userHead":"1_04","userId":"10001","userName":"小灵哥"}
10:42:29.268 [main] INFO  com.lino.mybatis.test.ApiTest - 测试结果:{"id":1,"userHead":"1_04","userId":"10001","userName":"小灵哥"}
10:42:29.299 [main] INFO  com.lino.mybatis.test.ApiTest - 测试结果:{"id":1,"userHead":"1_04","userId":"10001","userName":"小灵哥"}
10:42:29.315 [main] INFO  com.lino.mybatis.test.ApiTest - 测试结果:{"id":1,"userHead":"1_04","userId":"10001","userName":"小灵哥"}
...
  • 无池化的连接池操作,会不断的与数据库建立新的链接并执行 SQL 操作,这个过程中只要数据库还有链接可以被链接,就可以创建链接。

4.2.3 有池化测试结果:POOLED

10:46:58.765 [main] INFO  c.l.m.d.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
10:46:58.765 [main] INFO  c.l.m.d.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
10:46:58.765 [main] INFO  c.l.m.d.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
10:46:58.765 [main] INFO  c.l.m.d.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
10:46:59.444 [main] INFO  c.l.m.d.pooled.PooledDataSource - Created connention 1436664465.
10:46:59.507 [main] INFO  com.lino.mybatis.test.ApiTest - 测试结果:{"id":1,"userHead":"1_04","userId":"10001","userName":"小灵哥"}
10:46:59.533 [main] INFO  c.l.m.d.pooled.PooledDataSource - Created connention 1205406622.
10:46:59.533 [main] INFO  com.lino.mybatis.test.ApiTest - 测试结果:{"id":1,"userHead":"1_04","userId":"10001","userName":"小灵哥"}
10:46:59.543 [main] INFO  c.l.m.d.pooled.PooledDataSource - Created connention 796667727.
10:46:59.554 [main] INFO  com.lino.mybatis.test.ApiTest - 测试结果:{"id":1,"userHead":"1_04","userId":"10001","userName":"小灵哥"}
10:46:59.570 [main] INFO  c.l.m.d.pooled.PooledDataSource - Created connention 1541857308.
10:46:59.570 [main] INFO  com.lino.mybatis.test.ApiTest - 测试结果:{"id":1,"userHead":"1_04","userId":"10001","userName":"小灵哥"}
10:46:59.586 [main] INFO  c.l.m.d.pooled.PooledDataSource - Created connention 2095303566.
10:46:59.586 [main] INFO  com.lino.mybatis.test.ApiTest - 测试结果:{"id":1,"userHead":"1_04","userId":"10001","userName":"小灵哥"}
10:46:59.602 [main] INFO  c.l.m.d.pooled.PooledDataSource - Created connention 581318631.
10:46:59.602 [main] INFO  com.lino.mybatis.test.ApiTest - 测试结果:{"id":1,"userHead":"1_04","userId":"10001","userName":"小灵哥"}
10:46:59.633 [main] INFO  c.l.m.d.pooled.PooledDataSource - Created connention 1989184704.
10:46:59.633 [main] INFO  com.lino.mybatis.test.ApiTest - 测试结果:{"id":1,"userHead":"1_04","userId":"10001","userName":"小灵哥"}
10:46:59.649 [main] INFO  c.l.m.d.pooled.PooledDataSource - Created connention 199640888.
10:46:59.649 [main] INFO  com.lino.mybatis.test.ApiTest - 测试结果:{"id":1,"userHead":"1_04","userId":"10001","userName":"小灵哥"}
10:46:59.665 [main] INFO  c.l.m.d.pooled.PooledDataSource - Created connention 1243806178.
10:46:59.665 [main] INFO  com.lino.mybatis.test.ApiTest - 测试结果:{"id":1,"userHead":"1_04","userId":"10001","userName":"小灵哥"}
10:46:59.690 [main] INFO  c.l.m.d.pooled.PooledDataSource - Created connention 1007880005.
10:46:59.690 [main] INFO  com.lino.mybatis.test.ApiTest - 测试结果:{"id":1,"userHead":"1_04","userId":"10001","userName":"小灵哥"}
10:46:59.690 [main] INFO  c.l.m.d.pooled.PooledDataSource - Waiting as long as 20000 millisecond for connection.
10:47:19.690 [main] INFO  c.l.m.d.pooled.PooledDataSource - Claimed overdue connention 1436664465.
10:47:19.690 [main] INFO  com.lino.mybatis.test.ApiTest - 测试结果:{"id":1,"userHead":"1_04","userId":"10001","userName":"小灵哥"}
...
  • 通过使用连接池的配置可以看到,在调用和获取链接的过程中,当调用次数达到10此以后,连接池中就有了10个活跃链接,再调用时则需要等待连接释放后才能使用并执行 SQL 操作。

4.3 连接池验证

test_pooled:连接池验证

@Test
public void test_pooled() throws IOException, SQLException, InterruptedException {PooledDataSource pooledDataSource = new PooledDataSource();pooledDataSource.setDriver("com.mysql.jdbc.Driver");pooledDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true");pooledDataSource.setUsername("root");pooledDataSource.setPassword("123456");// 持续获取连接while (true) {Connection connection = pooledDataSource.getConnection();System.out.println(connection);Thread.sleep(1000);connection.close();}
}

测试结果

10:52:54.704 [main] INFO  c.l.m.d.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
10:52:54.704 [main] INFO  c.l.m.d.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
10:52:54.704 [main] INFO  c.l.m.d.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
10:52:54.704 [main] INFO  c.l.m.d.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
10:52:55.386 [main] INFO  c.l.m.d.pooled.PooledDataSource - Created connention 103536485.
com.mysql.jdbc.JDBC4Connection@62bd765
10:52:56.401 [main] INFO  c.l.m.d.pooled.PooledDataSource - Returned connection 103536485 to pool.
10:52:56.401 [main] INFO  c.l.m.d.pooled.PooledDataSource - Check out connention 103536485 form pool.
com.mysql.jdbc.JDBC4Connection@62bd765
10:52:57.404 [main] INFO  c.l.m.d.pooled.PooledDataSource - Returned connection 103536485 to pool.
10:52:57.404 [main] INFO  c.l.m.d.pooled.PooledDataSource - Check out connention 103536485 form pool.
com.mysql.jdbc.JDBC4Connection@62bd765
10:52:58.412 [main] INFO  c.l.m.d.pooled.PooledDataSource - Returned connection 103536485 to pool.
10:52:58.412 [main] INFO  c.l.m.d.pooled.PooledDataSource - Check out connention 103536485 form pool.
com.mysql.jdbc.JDBC4Connection@62bd765
10:52:59.416 [main] INFO  c.l.m.d.pooled.PooledDataSource - Returned connection 103536485 to pool.
10:52:59.416 [main] INFO  c.l.m.d.pooled.PooledDataSource - Check out connention 103536485 form pool.
  • 从连接的 hashCode 的值 @62bd765,可以看出数据库链接已经被缓存了,只要有空闲链接,就会调用数据库中同一个链接,节约资源。

!https://img-blog.csdnimg.cn/0332e7801f6b410daf16a5d4d5393a4e.jpeg

五、总结:数据源池化技术实现

  • 完成了 Mybatis 数据源池化的设计和实现,连接池的实现重点包括:synchronized 加锁、创建链接、活跃数量控制、休眠等待时长、抛异常逻辑等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/116168.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux系统下建立Socket聊天服务器

目录 1.服务器结构 2.各模块函数 2.1 socket函数 2.2 bind函数 2.3 Listen函数 2.4 accept函数 2.5 接收发送函数 2.6 close函数 2.7 connect函数 3 代码段 3.1 服务器代码 1.服务器结构 使用socket的API函数编写服务端和客户端程序的步骤图示: 2.各模块函数 服务…

Lliux管理员一些小技巧

1、查看bash日志 history命令显示日期和时间 2、打印时候对行列转换 xargs命令是改变已存在的文件的输出格式。“cat 文件名”是根据文件的行分隔符输出显示在屏幕上。如想改变一下&#xff0c;想把所有行合并为一行&#xff0c;就可以使用管道及xargs命令。 cat 文件名 |…

【原创】H3C三层交换机VLAN路由

网络拓扑图 VLAN 配置 VLAN 100 VLAN 200 [H3C]int vlan 100 ip address 1.1.1.1 255.255.255.0[H3C-Vlan-interface100]int vlan 200 ip address 2.2.2.1 255.255.255.0[H3C]int GigabitEthernet 1/0/1port access vlan 100[H3C]int GigabitEthernet 1/0/2port access vlan 2…

sql:SQL优化知识点记录(六)

&#xff08;1&#xff09;索引优化1 查看一下有没有建立索引&#xff1a; 用到索引中的一个&#xff1a;type中的ref决定访问性能 用到索引中的两个&#xff1a;通过key_len的长度可以看出来&#xff0c;比第一个大一点。或者通过ref&#xff1a;中用到了两个常量const 用到了…

黑马 大事件项目 笔记

学习视频&#xff1a;黑马 Vue23 课程 后台数据管理系统 - 项目架构设计 在线演示&#xff1a;https://fe-bigevent-web.itheima.net/login 接口文档: https://apifox.com/apidoc/shared-26c67aee-0233-4d23-aab7-08448fdf95ff/api-93850835 接口根路径&#xff1a; http:/…

Spring 系统架构

Spring总共大约有 20个模块&#xff0c;由1300多个不同的文件构成。而这些组件被分别整合在核心容器&#xff08;CoreContainer&#xff09;、AOP&#xff08;Aspect Oriented Programming&#xff09;和设备支持&#xff08;Instrmentation&#xff09;、数据访问及集成&#…

pyqt5 QuickStart

在使用pyqt5之前&#xff0c;建议下载一个Anaconda环境&#xff0c;这样下载python包更方便&#xff0c;本篇文章是建立在已经安装好Anaconda的情况下使用的。IDE就是标准的PyCharm了。 一、pyqt包的安装 python终端执行下面两个安装命令&#xff1a; pip install PyQt5 pip …

Matlab(数值微积分)

目录 1.多项式微分与积分 1.1 微分 1.2 多项式微分 1.3 如何正确的使用Matlab? 1.3.1 Matlab表达多项式 1.3.2 polyval() 多项式求值 1.3.3 polyder()多项式微分 1.4 多项式积分 1.4.1 如何正确表达 1.4.2 polyint() 多项式积分 2.数值的微分与积分 2.1 数值微分 2…

论文阅读_扩散模型_SDXL

英文名称: SDXL: Improving Latent Diffusion Models for High-Resolution Image Synthesis 中文名称: SDXL&#xff1a;改进潜在扩散模型的高分辨率图像合成 论文地址: http://arxiv.org/abs/2307.01952 代码: https://github.com/Stability-AI/generative-models 时间: 2023-…

5G NR:RACH流程 -- Msg1之选择正确的PRACH时频资源

PRACH的时域资源是如何确定的 PRACH的时域资源主要由参数“prach-ConfigurationIndex”决定。拿着这个参数的取值去协议38211查表6.3.3.2-2/3/4&#xff0c;需要注意根据实际情况在这三张表中进行选择&#xff1a; FR1 FDD/SULFR1 TDDFR2 TDD Random access preambles can onl…

4.2 实现基于栈的表达式求值计算器(难度4/10)

本作业主要考察&#xff1a;解释器模式的实现思想/栈结构在表达式求值方面的绝对优势 C数据结构与算法夯实基础作业列表 通过栈的应用&#xff0c;理解特定领域设计的关键作用&#xff0c;给大家眼前一亮的感觉。深刻理解计算机语言和人类语言完美结合的杰作。是作业中的上等…

VSCode之C++ CUDA极简环境配置

背景 想要了解CUDA并行计算原理&#xff0c;同时针对深度学习中出现一些“不支持算子”可能需要手写的需要&#xff0c;配置一个简单的CUDA编译环境&#xff0c;探索CUDA编程的范式【注&#xff1a;CUDA环境配置略】。结果展示 示例代码 #include "cuda_runtime.h" …

Redis发布订阅

Redis发布订阅 Redis 发布订阅(pub/sub)是一种 消息通信模式&#xff1a;发送者(pub)发送消息&#xff0c;订阅者(sub)接收消息。 Redis 客户端可以订阅任意数量的频道。 订阅/发布消息图&#xff1a; 下图展示了频道 channel1 &#xff0c; 以及订阅这个频道的三个客户端 —…

Python钢筋混凝土结构计算.pdf-混凝土强度设计值

计算原理&#xff1a; 需要注意的是&#xff0c;根据不同的规范和设计要求&#xff0c;上述公式可能会有所差异。因此&#xff0c;在进行混凝土强度设计值的计算时&#xff0c;请参考相应的规范和设计手册&#xff0c;以确保计算结果的准确性和合规性。 代码实现&#xff1a; …

SAP_ABAP_OLE_EXCEL批导案例

SAP ABAP顾问能力模型梳理_企业数字化建设者的博客-CSDN博客SAP Abap顾问能力模型https://blog.csdn.net/java_zhong1990/article/details/132469977 一、OLE_EXCEL批导 1.1 下载按钮 1.2 选择EXCEL上传&#xff0c;解析EXCLE数据&#xff0c; Call屏幕。 1.3 实现效果 1.4…

报错:axios 发送的接口请求 404

axios 发送的接口请求 404 一、问题二、分析 一、问题 二、分析 axios 发送的接口请求 404&#xff0c;根本没有把接口信息发送到后端&#xff0c;这个时候你可以查看检查一下自己的接口名字&#xff0c;或让后端配合换一个接口名字再发送一次接口请求

编程题四大算法思想(三)——贪心法:找零问题、背包问题、任务调度问题、活动选择问题、Prim算法

文章目录 贪心法找零问题&#xff08;change-making problem&#xff09;贪心算法要求基本思想适合求解问题的特征 背包问题0/1背包问题0/1背包问题——贪心法 分数背包问题 任务调度问题活动选择问题活动选择——贪心法最早结束时间优先——最优性证明 Prim算法 贪心法 我在当…

【多线程案例】单例模式(懒汉模式和饿汉模式)

文章目录 1. 什么是单例模式&#xff1f;2. 立即加载/“饿汉模式”3. 延时加载/“懒汉模式”3.1 第一版3.2 第二版3.3 第三版3.4 第四版 1. 什么是单例模式&#xff1f; 提起单例模式&#xff0c;就必须介绍设计模式&#xff0c;而设计模式就是在软件设计中&#xff0c;针对特殊…

Vue2项目练手——通用后台管理项目第一节

Vue2项目练手——通用后台管理项目 知识补充yarn和npm区别npm的缺点&#xff1a;yarn的优点 npm查看镜像和设置镜像 项目介绍项目的技术栈 项目搭建文件目录 创建路由&#xff0c;引入element-uirouter/index.jsmain.jspages/Users.vuepages/Main.vuepages/Home.vuepages/Login…

linux C++ 海康截图Demo

项目结构 CMakeLists.txt cmake_minimum_required(VERSION 3.7)project(CapPictureTest)include_directories(include)link_directories(${CMAKE_SOURCE_DIR}/lib ${CMAKE_SOURCE_DIR}/lib/HCNetSDKCom) add_executable(CapPictureTest ${CMAKE_SOURCE_DIR}/src/CapPictureTes…