由于公司业务上的需要,前段时间做了一阵子数据提取与转运的工作,主要运用了python和java。在开发的过程中,接触到了一些新的技术,产生了一些新的思路,在此记录一下。今天,就先来结合一次实际的数据提取经历,总结一下java如何利用JedisPool实现对Redis的多线程调用。
先大致说一下这次任务的总体思路:
由于数据量较大,所以在这里使用了多线程。整个流程主要分为两块,生产者从源数据中循环读取任务,并将任务放到redis缓冲池中;消费者从redis中获取任务,并执行,最终将数据插入到数据库中。
Java调用redis,需要在工程中导入驱动包:jedis,点我下载
在实际测试中,生产者的执行速率很快,所以这里生产者只开了一个线程去执行。
1、生产者循环读取任务并放入redis缓冲池中:
public class RedisQueueProducer{
private int pageNum = 0;
private Jedis jedis;
public void startWrite(Jedis jedis){
this.jedis = jedis;
MyExecutor executor = new MyExecutor();
executor.start();
}
private class MyExecutor extends Thread{
@Override
public void run() {
super.run();
List readData;
while (true) {
//分页从数据库中读取数据,直到数据全部取完,跳出循环
readData = JdbcUtils.queryData(pageNum);
if(readData.size()>0) {
String[] readDataArrays = new String[readData.size()];
jedis.lpush("sourceIds",readData.toArray(readDataArrays));
pageNum++;
}else{
break;
}
}
}
}
}
这里存入redis使用的lpush的方式,选择这种方式的好处是读取redis数据时,调用lpop可以直接弹出list顶部的第一条数据,该条数据也会从redis中移除,不会占用空间,即取即用。
2、消费者从redis中获取任务并执行,存入数据库中:
public class RedisQueueConsumer{
private Jedis jedis;
private HashDocumentIdZHO hashDocumentIdZHO;
public void startRead(Jedis jedis){
//连接本地的 Redis 服务
this.jedis = jedis;
hashDocumentIdZHO = new HashDocumentIdZHO();
MyExecutor executor = new MyExecutor();
executor.start();
}
private class MyExecutor extends Thread{
@Override
public void run() {
super.run();
while (true){
String sourceId = jedis.lpop("sourceIds");
if(sourceId!=null&&!sourceId.equals("")) {
if(JdbcUtils.getConn()==null){
JdbcUtils.connect();
}
ConvertIdResult result = hashDocumentIdZHO.getHashDocumentIdZHOSoap().convertClassicID(sourceId,1,2,true);
HashDoc doc = new HashDoc();
doc.setResult(result);
doc.setSourceDocId(sourceId);
JdbcUtils.insert(doc);
System.out.println("线程名称:" + Thread.currentThread().getName() + ",hash值:" + result.getResult());
}else{
try {
//如果没有数据,等待30s,再执行;后来查看jedis的api得知,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止,所以理论上这里可以不做设置。不过并没有实际试验过。
sleep(30000);
}catch (Exception e){
e.printStackTrace();
}
}
}
}
}
}
3、利用jedisPool实现多线程调用:
可能有小伙伴看到这里会问,为什么要用redis作为缓冲池呢?(如果没有人问,请容我自问自答一下...)因为这里用到了多线程,而redis本身是线程安全的,所以自然这里就选择了redis作为缓冲池。不过,redis本身是单线程的,如果我们需要通过java多线程从redis取数据,则需要借助jedisPool,生成一个redis连接池,让一个线程对应一个redis连接。
public class RedisQueuePool {
public static JedisPool jedisPool = null;
public static void main(String[] args) {
if(jedisPool == null){
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(10000);
//控制一个pool最多有多少个状态为idle(空闲)的jedis实例
jedisPoolConfig.setMaxIdle(8);
//获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间, 默认-1
jedisPoolConfig.setMaxWaitMillis(1);
//获得一个jedis实例的时候是否检查连接可用性(ping());如果为true,则得到的jedis实例均是可用的;
jedisPoolConfig.setTestOnBorrow(true);
//return 一个jedis实例给pool时,是否检查连接可用性(ping())
jedisPoolConfig.setTestOnReturn(true);
jedisPool = new JedisPool(jedisPoolConfig,"localhost");
}
//新建20个线程,执行操作
for (int i = 0; i < 20; i++) {
RedisQueueConsumer consumer = new RedisQueueConsumer();
consumer.startRead(jedisPool.getResource());
}
}
}
jedisPoolConfig是jedisPool的一些属性配置,详细的属性配置查看可以移步这里:属性配置列表。
如果想要使用jedisPool,还需要引入commons-pool2-2.4.2.jar。
以上就是JAVA利用JedisPool实现对Redis的多线程调用的思路与解决方案了,希望能对读到这里的你能有所帮助~