java threadsafequeue_<Web Crawler><Java><thread-safe queue>

Basic Solution

The simplest way is to build a web crawler that runs on a single machine with single thread.

So, a basic web crawler should be like this:

Start with a URL pool that contains all the websites we want to crawl.

For each URL, issue a HTTP GET request to fetch the web page content.

Parse the content(usually HTML) and extract potential URLs that we want to crawel.

Add new URLs to the pool and keep crawling.

Scale issues

As is known to all, any system will face a bunch of issues after scaling.

Then, what can be bottlenecks of a distributed web crawler? And how to solve them?

Crawling frequency

How often will u crawel a website?

对于小网站,它们的服务器可能负载不了过于频繁的请求。

一种解决方式是参照robot.txt文件。

Dedup

In a single machine, u can keep the URL pool in memory and remove duplicate entries.

However, things becomes more complicated in a distributed system.

So how can we dedup these URLs?

一种常见的做法是使用Bloom Filter。bf是一种空间有效的系统,它可以用来检测一个元素是否在集合中。但是bf给出的在pool中的判断可能是错误的。(不在的判断是精准的)

Parsing

After fetching the response data, the next step is to parse the data(usually HTML) to extract the information we care about.

This sounds like a simple thing, but it can be quite hard to make it robust.

Other pro.

detect loops: many websites may contain links like A -> B -> C -> A, and ur crawler may end up running forever. How to fix this? 【实际上去重之后就不会有环路了吧,like BFS】

DNS lookup: when the system get scaled to certain level, DNS lookup can be a bottleneck and u may build ur own DNS server.

A java Web Crawler

为了移除暂不想关注的点,包括html解析,cookie的使用等等。直接爬一个接口,返回的是json。

多线程:

queue使用的是BlockingQueue阻塞队列,offer的时候设置一个等待时间,超时则返回null,同样的poll也有等待时间;

visited使用Collections.synchronizedSet

处理url:HTTP GET request: 使用URL(requestUrl).openStream(),因为不用设置额外的头部,所以很简单;

分情况:

如果该url是json,则解析该json,把取得的链接放到queue中;

如果该url是mp3,则下载保存到本地

Thread-safe Queue

java提供了两种thread-safe的类:

BlockingQueue: 阻塞队列:

入队操作:

add(e): 在队列满的时候会报异常;

offer(e): 不会报异常,也不会阻塞,返回值是boolean。即在队满的时候不会插入元素,而直接返回false;

offer(e, timeout, unit): 可以设定等待时间;

put(e): 在队列满时会阻塞;

出队操作:

remove(): 从空队列remove会报异常;

poll(): 不会报异常也不会阻塞,与offer(e)相对应;

poll(timeout, unit): 设定等待时间;

take(): 队列为空时会阻塞;

查看元素:

element(): 在队列为空时报异常;

peek(): 不报异常也不阻塞,返回boolean;

BlockingQueue接口的具体实现类:

ArrayBlockingQueue:构造函数必须带int参数以指明大小;

LinkedBlockingQueue:若其构造函数带一个规定大小的参数,生成的BlockingQueue有大小限制,若不带大小参数,所生成的BlockingQueue的大小由Integer.MAX_VALUE来决定;

PriorityBlockingQueue:其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数的Comparator决定的顺序

concurrentLinkedQueue: 非阻塞队列

ConcurrentLinkedQueue是一个无锁的并发线程安全队列;

对比锁机制的实现,无锁机制的难点在于要充分考虑线程间的协调。简单说来就是多个线程对内部数据结构进行访问时,若其中一个线程执行的中途因为一些原因出现故障,其他的线程能够监测并帮助完成剩下的操作。这就需要把数据结构的操作过程精细地划分为多个状态或阶段,考虑每个阶段或状态多线程访问会出现的情况。

ConcurrentLinkedQueue有两个volatile的线程共享变量:head、tail。要保证队列的线程安全就是要保证对这两个node的引用的访问(更新、查看)的原子性和可见性。

由于volatile本身能保证可见性,所以就是对其修改的原子性要被保证。

anyway,阻塞算法其实本质就是加锁,使用synchronized关键字。而相比之下,非阻塞算法的设计和实现就比较困难了,要通过低级的原子性来支持并发。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值