回顧
上一章講到,通過(guò)FTPClient進(jìn)行上長(zhǎng)傳和下載操作磕道,但是頻繁new FTPClient進(jìn)行連接操作供屉,是否會(huì)導(dǎo)致資源浪費(fèi)呢。
流程排查
FTPClient ftpClient = new FTPClient();
try {
ftpClient.connect(props.getHost(), props.getPort());
ftpClient.login(props.getUsername(), props.getPassword());
我們知道每次使用前溺蕉,需要連接伶丐,連接需要new一個(gè)對(duì)象,并且connect和login疯特。我們來(lái)看下哗魂,這幾個(gè)方法核心實(shí)現(xiàn):
FTPClient
public FTPClient()
{
__initDefaults();
__dataTimeout = -1;
__remoteVerificationEnabled = true;
__parserFactory = new DefaultFTPFileEntryParserFactory();
__configuration = null;
__listHiddenFiles = false;
__useEPSVwithIPv4 = false;
__random = new Random();
__passiveLocalHost = null;
}
private void __initDefaults()
{
__dataConnectionMode = ACTIVE_LOCAL_DATA_CONNECTION_MODE;
__passiveHost = null;
__passivePort = -1;
__activeExternalHost = null;
__reportActiveExternalHost = null;
__activeMinPort = 0;
__activeMaxPort = 0;
__fileType = FTP.ASCII_FILE_TYPE;
__fileStructure = FTP.FILE_STRUCTURE;
__fileFormat = FTP.NON_PRINT_TEXT_FORMAT;
__fileTransferMode = FTP.STREAM_TRANSFER_MODE;
__restartOffset = 0;
__systemName = null;
__entryParser = null;
__entryParserKey = "";
__featuresMap = null;
}
簡(jiǎn)單的初始化數(shù)據(jù)操作。這個(gè)構(gòu)造函數(shù)沒(méi)有什么復(fù)雜操作漓雅,也沒(méi)任何讀取緩存操作录别,就是new一個(gè)對(duì)象朽色。
connect
// helper method to allow code to be shared with connect(String,...) methods
private void _connect(InetAddress host, int port, InetAddress localAddr, int localPort)
throws SocketException, IOException
{
_socket_ = _socketFactory_.createSocket();
...隱藏判定、賦值...
_socket_.connect(new InetSocketAddress(host, port), connectTimeout);
_connectAction_();
}
看起來(lái)有三個(gè)功能
1组题、代碼復(fù)用葫男,shared code
2、創(chuàng)建一個(gè)socket崔列。socketFactory.createSocket();
3梢褐、連接socket。 socket.connect(new InetSocketAddress(host, port), connectTimeout);
我們可以進(jìn)一步看一下connect方法赵讯。發(fā)現(xiàn)是常規(guī)的socket認(rèn)證連接盈咳,故可以判定,整個(gè)機(jī)制中無(wú)ftp連接池復(fù)用概念瘦癌。每次使用FTPClient都需要?jiǎng)?chuàng)建對(duì)象猪贪、連接、并釋放
public void connect(SocketAddress endpoint, int timeout) throws IOException {
...隱藏判定讯私、賦值...
InetSocketAddress epoint = (InetSocketAddress) endpoint;
InetAddress addr = epoint.getAddress ();
int port = epoint.getPort();
checkAddress(addr, "connect");
SecurityManager security = System.getSecurityManager();
if (security != null) {
if (epoint.isUnresolved())
security.checkConnect(epoint.getHostName(), port);
else
security.checkConnect(addr.getHostAddress(), port);
}
if (!created)
createImpl(true);
if (!oldImpl)
impl.connect(epoint, timeout);
else if (timeout == 0) {
if (epoint.isUnresolved())
impl.connect(addr.getHostName(), port);
else
impl.connect(addr, port);
} else
throw new UnsupportedOperationException("SocketImpl.connect(addr, timeout)");
connected = true;
/*
* If the socket was not bound before the connect, it is now because
* the kernel will have picked an ephemeral port & a local address
*/
bound = true;
}
引入連接池機(jī)制
依賴
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.4.2</version>
</dependency>
如何使用
我們考慮下热押,一個(gè)連接池核心操作應(yīng)該具備哪些
1、初始化池資源
2斤寇、生產(chǎn)桶癣、獲取、銷毀對(duì)象
3娘锁、監(jiān)控忙碌和空閑資源牙寞,可進(jìn)行動(dòng)態(tài)分配
4、監(jiān)控資源有效性
建立配置信息
從yml配置文件中讀取用戶名莫秆、密碼间雀、地址、端口信息等镊屎。
@Data
@ConfigurationProperties(prefix = "ftp")
static class FtpConfigProperties {
private String host = "localhost";
private int port = FTPClient.DEFAULT_PORT;
private String username;
private String password;
private int bufferSize = 8096;
/**
* 初始化連接數(shù)
*/
private Integer initialSize = 1;
}
初始化池資源
通過(guò)FtpClientPooledObjectFactory這個(gè)類惹挟,初始化數(shù)據(jù)池容器。核心方法為
new GenericObjectPool<>();
@Slf4j
@Configuration
@ConditionalOnClass({GenericObjectPool.class, FTPClient.class})
@ConditionalOnProperty(value = "ftp.enabled", havingValue = "true")
@EnableConfigurationProperties(FTPConfiguration.FtpConfigProperties.class)
public class FTPConfiguration {
//資源池容器缝驳,為接口泛型
private ObjectPool<FTPClient> pool;
//構(gòu)造是傳入连锯,通過(guò)ftp選擇是否加載此bean
public FTPConfiguration(FtpConfigProperties props) {
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
//設(shè)置最小線程數(shù)量
poolConfig.setMinIdle(1);
//設(shè)置最大空閑時(shí)間,當(dāng)大于50s時(shí)用狱,移除連接對(duì)象运怖,直至只剩最后一個(gè)
poolConfig.setSoftMinEvictableIdleTimeMillis(50000);
//巡檢時(shí)間,每30秒巡檢一次
poolConfig.setTimeBetweenEvictionRunsMillis(30000);
//生產(chǎn)數(shù)據(jù)池
pool = new GenericObjectPool<>(new FtpClientPooledObjectFactory(props), poolConfig);
//設(shè)置初始值和最大值
preLoadingFtpClient(props.getInitialSize(), poolConfig.getMaxIdle());
}
生產(chǎn)夏伊、獲取摇展、銷毀對(duì)象
對(duì)象的生產(chǎn)、獲取署海、銷毀通過(guò)接口的方式實(shí)現(xiàn)吗购,即上面interface ObjectPool<T>
我們先看下它的方法
public interface ObjectPool<T> {
//獲取
T borrowObject() throws Exception, NoSuchElementException,
IllegalStateException;
//歸還
void returnObject(T obj) throws Exception;
//驗(yàn)證對(duì)象是否有效
void invalidateObject(T obj) throws Exception;
//添加對(duì)象
void addObject() throws Exception, IllegalStateException,
UnsupportedOperationException;
/***獲取實(shí)例和活動(dòng)實(shí)例医男,清理資源和關(guān)閉數(shù)據(jù)池***/
int getNumIdle();
int getNumActive();
void clear() throws Exception, UnsupportedOperationException;
void close();
}