背景:
要抓取网络数据,使用了chromedriver。因为创建和销毁驱动耗时间,所以考虑使用对象池管理。刚好apache提供了开源库,不用重复造轮子了。并且支持多key的池子。如:手机端的驱动放一个池子中,PC端的驱动放一个池子中。简单使用如下:
maven引入:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.4.2</version>
</dependency>
模式类型枚举
public enum EnumDriverObject {
chrome_pc("PC模式"),
chrome_phone("Phone模式");
EnumDriverObject(String text){
}
}
对象池工厂
import org.apache.commons.pool2.KeyedPooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
import java.util.HashMap;
import java.util.Map;
/**
* @desc:apache对象池,基于key的多模式对象池
* @author: hecj
**/
public class DriverObjectKeyFactory implements KeyedPooledObjectFactory<String, Object> {
private final static Logger log = LoggerFactory.getLogger(DriverObjectKeyFactory.class);
private static GenericKeyedObjectPool genericKeyedObjectPool = null;
private static Object lock = new Object();
// 最大驱动个数
private static int maxDriverCount = 20;
// 初始化驱动个数
private static int initDriverCount = 2;
// 空闲驱动个数
private static int idleDriverCount = 2;
// 最大个数
private static int maxTotal = 100;
private static Long maxWait = 10000l;
/**
* 单例
*/
public static GenericKeyedObjectPool getInstance() {
if (genericKeyedObjectPool == null) {
synchronized (lock) {
if (genericKeyedObjectPool == null) {
try{
Environment env = SpringUtil.getBean(Environment.class);
maxDriverCount = Integer.parseInt(env.getProperty("chrome.maxDriverCount"));
idleDriverCount = Integer.parseInt(env.getProperty("chrome.idleDriverCount"));
initDriverCount = Integer.parseInt(env.getProperty("xylink.chrome.initDriverCount"));
maxTotal = Integer.parseInt(env.getProperty("chrome.maxTotal"));
maxWait = Long.parseLong(env.getProperty("chrome.maxWait"));
} catch (Exception ex){
}
GenericKeyedObjectPoolConfig genericKeyedObjectPoolConfig = new GenericKeyedObjectPoolConfig();
genericKeyedObjectPoolConfig.setMaxIdlePerKey(idleDriverCount);
genericKeyedObjectPoolConfig.setMaxTotalPerKey(maxDriverCount);
genericKeyedObjectPoolConfig.setMaxTotal(maxTotal);
genericKeyedObjectPoolConfig.setMinIdlePerKey(initDriverCount);
genericKeyedObjectPoolConfig.setMaxWaitMillis(maxWait);
DriverObjectKeyFactory driverObjectKeyFactory = new DriverObjectKeyFactory();
genericKeyedObjectPool = new GenericKeyedObjectPool<>(driverObjectKeyFactory, genericKeyedObjectPoolConfig);
}
}
}
return genericKeyedObjectPool;
}
/**
* 根据key创建对应的对象
*/
@Override
public PooledObject<Object> makeObject(String key) throws Exception {
Object object = null;
if(EnumDriverObject.chrome_pc.name().equals(key)){
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.addArguments("--headless");
object = new ChromeDriver(chromeOptions);
} else if(EnumDriverObject.chrome_phone.name().equals(key)) {
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.addArguments("--headless");
Map<String, Object> deviceMetrics = new HashMap<String, Object>();
deviceMetrics.put("width", 360);
deviceMetrics.put("height", 640);
deviceMetrics.put("pixelRatio", 3.0);
Map<String, Object> mobileEmulation = new HashMap<String, Object>();
mobileEmulation.put("deviceMetrics", deviceMetrics);
mobileEmulation.put("userAgent", "Mozilla/5.0 (Linux; Android 8.1.0; en-us; Nexus 5 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19");
chromeOptions.setExperimentalOption("mobileEmulation", mobileEmulation);
object = new ChromeDriver(chromeOptions);
}else{
log.error("对象key无效");
throw new RuntimeException("对象key无效");
}
return new DefaultPooledObject<>(object);
}
/**
* 对象销毁时执行 对应方法:genericKeyedObjectPool.invalidateObject
*/
@Override
public void destroyObject(String key, PooledObject<Object> p) throws Exception {
Object object = p.getObject();
if(object instanceof ChromeDriver){
((ChromeDriver)object).quit();
}
}
@Override
public boolean validateObject(String key, PooledObject<Object> p) {
return p.getObject() != null;
}
@Override
public void activateObject(String key, PooledObject<Object> p) throws Exception {
}
@Override
public void passivateObject(String key, PooledObject<Object> p) throws Exception {
}
}
调用方式
GenericKeyedObjectPool genericKeyedObjectPool = DriverObjectKeyFactory.getInstance();
// 先从池子中拿一个对象
ChromeDriver driver = (ChromeDriver)genericKeyedObjectPool.borrowObject(EnumDriverObject.chrome_pc);
...
doSomething();
...
// 用完之后释放掉
genericKeyedObjectPool.returnObject(EnumDriverObject.chrome_pc,driver);
// 如果对象有问题,也可以销毁掉
genericKeyedObjectPool.invalidateObject(EnumDriverObject.chrome_pc,driver);