项目说明
实现了定时从指定的网站上 , 获取新闻服务
gitee代码 服务端 -------公共模块 ----------在服务端用(NewsObtain)模拟客户端获取新闻
技术点
- 基于事件的xml解析,SAX解析来解析网站xml格式的网站的信息
- 构建一个缓存队列,用阻塞队列LinkedBlockingQueue 来缓存数据 ,实现生产者消费者模式 ,从而降低服务端和客户端的耦合.
- 并用布隆过滤器来实现缓存数据去重。
- 使用CountDownLatch类来实现,消费线程与生产线程的关闭控制.
- 使用ScheduledThreadPoolExecutor线程池来实现周期性执行任务.
- NIO非阻塞流技术读取指定文件中的网站名、网站地址和将客户端获得的新闻信息存入文件中。
知识点
- XML解析
DOM解析: 基于文档对象模型, 它是一次性将文档加载到内存
//1.读取books.xml文件
SAXReader reader = new SAXReader();
Document document = reader.read("src/books.xml");
//2.通过Document对象获得根元素
Element rootElement = document.getRootElement();
//3.通过根元素获得book标签对象集合
List<Element> books = rootElement.elements("book");
//4.遍历每个book标签对象
for (Element book : books){
//asXML将标签对象转化为标签字符串
// System.out.println(book.asXML());
//通过父元素获得name元素
Element nameElement = book.element("name");
//获得标签中的文本内容
String nameTest = nameElement.getText();
String price = book.elementText("price");
//获得标签的属性值
String sn = book.attributeValue("sn");
String author = book.elementText("author");
}
SAX解析: 基于事件的解析, 因为从RSS源上下载的新闻数据的大小不可知,所以要采用SAX解析,以防内存溢出. 步骤:
//构建一个sax解析器
SAXParserFactory spf = SAXParserFactory.newInstance();
//2.获得解析器
SAXParser sp = spf.newSAXParser();
// 调用解析方法解析xml 参数: 解析内容的地址 要使用的SAX DefaultHandler。
//DefaultHandler类是SAX2事件处理程序的默认基类
sp.parse(String uri, DefaultHandler dh);
//让一个类继承 DefaultHandler 类重写
/**
* 解析xml中的文本
*/
public void characters(char ch[], int start, int length);
/**
* 元素开头处理
*/
public void startElement(String uri, String localName,
String qName, Attributes attributes)
throws SAXException ;
/**
* 元素结尾处理
*/
public void endElement(String uri, String localName, String qName)
throws SAXException;
- 阻塞队列
与普队列的区别: 当队列为空时,从队列中取元素的操作会被阻塞,,直到其他线程往队列中存元素; 当队列满时,向队列中存元素的操作也会被阻塞,直到队列中的元素减少。 LinkedBlockingQueue一个由链表结构组成的有界阻塞队列:按照FIFO原则对元素进行排序。 默认最大长度为Integer.MAX_VALUE。
通过ReentrantLock来保证数据的一致性; LinkedBlockingQueue分别使用了takeLock 和 putLock 对并发进行控制,也就是说,添加和删除操作并不是互斥操作,可以同时进行,这样也就可以大大提高吞吐量. - 布隆过滤器 通过多次hash算法的得到地址来判断这个数据是否有过缓存。
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>29.0-jre</version>
</dependency>
/**
* 参数 : 数据类型 预计数据量 期望的误报概率
*/
BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.forName("UTF-8")), 100000, 0.01);
//判断数据是否存在
bloomFilter.mightContain(item.getId())
- CountDownLatch类:定义一个计数器,然后给需要等待的线程,需要阻塞的地方使用await()方法阻塞,直到在其他线程中调用countDown()方法使计数器减至0。
- ScheduledThreadPoolExecutor线程池:
周期性地执行某项任务,包括以固定速率执行任务或者以固定延迟执行任务
建议在提交给ScheduledThreadPoolExecutor的任务要住catch异常
public static void main(String[] args) {
ScheduledThreadPoolExecutor poolExecutor = new ScheduledThreadPoolExecutor(1);
//方法只执行一次
// poolExecutor.schedule(new ScheduleWorker(ScheduleWorker.Normal),5, TimeUnit.SECONDS);
//在固定时间运行
//固定延时 每两个任务之间间隔时间
poolExecutor.scheduleWithFixedDelay(new ScheduleWorker(ScheduleWorker.Normal),1000,2000,TimeUnit.MILLISECONDS);
//固定频率 到这个时间间隔就执行, 上个任务运行完就立刻执行,没有就等上个任务执行完就立刻执行 固定时间间隔执行
poolExecutor.scheduleAtFixedRate(new ScheduleWorker(ScheduleWorker.Normal),1000,2000,TimeUnit.MILLISECONDS);
poolExecutor.shutdown();
}
6.NIO:
一种同步非阻塞的I/O模型
Path file = Paths.get(path);
try (InputStream inputStream = Files.newInputStream(file)) {
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
//按行读取文件
while ((line = reader.readLine()) != null) {}
} catch (IOException e) {
e.printStackTrace();
}