大数据技术之_11_HBase学习_03

  • 第8章 HBase 实战之谷粒微博
    • 8.1 需求分析
    • 8.2 代码实现
  • 第9章 扩展知识
    • 9.1 HBase 在商业项目中的能力
    • 9.2 布隆过滤器
    • 9.3 HBase2.0 新特性

第8章 HBase 实战之谷粒微博

8.1 需求分析

  1) 微博内容的浏览,数据库表设计
  2) 用户社交体现:关注用户,取关用户
  3) 拉取关注的人的微博内容

数据库表设计:
[外链图片转存失败(img-F1wqIrTB-1562638565417)(https://s2.ax1x.com/2019/03/10/ApGbZT.png)]

8.2 代码实现

目录结构:
[外链图片转存失败(img-7ZHBe2UO-1562638565419)(https://s2.ax1x.com/2019/03/10/ApGqdU.png)]
代码如下:
常量类:

package com.atguigu.constant;public class Constant {// 命名空间(相当于数据库名称)public static final String NAMESPACE = "weibo";// 内容表的表名public static final String CONTENT = "weibo:content";// 用户关系表的表名public static final String RELATIONS = "weibo:relations";// 微博收件箱表的表名public static final String INBOX = "weibo:inbox";
}

工具类:

package com.atguigu.util;import java.io.IOException;
import java.util.ArrayList;
import java.util.List;import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.NamespaceDescriptor;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Admin;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.hbase.filter.CompareFilter;
import org.apache.hadoop.hbase.filter.RowFilter;
import org.apache.hadoop.hbase.filter.SubstringComparator;
import org.apache.hadoop.hbase.util.Bytes;import com.atguigu.constant.Constant;public class WeiboUtil {// 获取配置confprivate static Configuration conf = null;static {// HBase配置文件conf = HBaseConfiguration.create();// 设置zookeeper地址conf.set("hbase.zookeeper.quorum", "192.168.25.102");conf.set("hbase.zookeeper.property.clientPort", "2181");}// 创建命名空间public static void createNamespace(String namespace) throws IOException {// 创建连接// 注意:关于Util中的异常不要在工具类Util中catch,应该抛出去,因为工具类Util会在业务线上的很多地方调用,每一个业务线对待异常的处理方式不一样。Connection conn = ConnectionFactory.createConnection(conf);Admin admin = conn.getAdmin();// 创建命名空间描述器NamespaceDescriptor namespaceDescriptor = NamespaceDescriptor.create(namespace).build();// 创建命名空间admin.createNamespace(namespaceDescriptor);// 关闭资源admin.close();}// 创建表public static void createTable(String tableName, int versions, String... columnFamily) throws IOException {// 创建连接Connection conn = ConnectionFactory.createConnection(conf);Admin admin = conn.getAdmin();// 创建表描述器HTableDescriptor hTableDescriptor = new HTableDescriptor(TableName.valueOf(tableName));// 循环添加列族for (String cf : columnFamily) {// 创建列描述器HColumnDescriptor hColumnDescriptor = new HColumnDescriptor(cf);// 指定列族的版本个数,默认是一个hColumnDescriptor.setMaxVersions(versions);hTableDescriptor.addFamily(hColumnDescriptor);}// 创建表admin.createTable(hTableDescriptor);// 关闭资源admin.close();}/*** 1、更新微博内容表的数据* 2、更新微博收件箱表的数据*       1)从用户关系表中获取当前操作人的fans*       2)去往微博收件箱表中更新数据* @param uid* @param content* @throws IOException*/// 发布微博(即插入一条数据)public static void createData(String uid, String content) throws IOException {// 创建连接Connection conn = ConnectionFactory.createConnection(conf);// 创建Table对象(新API),一共要操作3张表Table conTable = conn.getTable(TableName.valueOf(Constant.CONTENT));Table relaTable = conn.getTable(TableName.valueOf(Constant.RELATIONS));Table inboxTable = conn.getTable(TableName.valueOf(Constant.INBOX));// 拼接RowKeylong ts = System.currentTimeMillis();String rowKey = uid + "_" + ts;Put contentPut = new Put(Bytes.toBytes(rowKey));// 向Put对象中组装数据contentPut.addColumn(Bytes.toBytes("info"), Bytes.toBytes("content"), Bytes.toBytes(content));// 1、往内容表中添加数据conTable.put(contentPut);// 2、获取用户关系表的列族fansGet get = new Get(Bytes.toBytes(uid));get.addFamily(Bytes.toBytes("fans"));Result result = relaTable.get(get);Cell[] cells = result.rawCells();if (cells.length <= 0) {return;}// 3、更新fans的微博收件箱表List<Put> puts = new ArrayList<Put>();for (Cell cell : cells) {// 克隆用户关系表的列,作为收件箱表的RowKeybyte[] qualifier = CellUtil.cloneQualifier(cell);Put inboxPut = new Put(qualifier);// 添加列族、列、值inboxPut.addColumn(Bytes.toBytes("info"), Bytes.toBytes(uid), ts, Bytes.toBytes(rowKey));puts.add(inboxPut);}inboxTable.put(puts);// 关闭资源conTable.close();relaTable.close();inboxTable.close();conn.close();}/*** 1、在用户关系表中*      1)添加 操作者要关注人的attends(批量添加)*        2)添加 被关注人的fans* 2、在微博收件箱表中*      1)在微博内容表中获取被关注用户的3条数据(uid_ts)*         2)在微博收件箱表中添加操作人的关注用户信息* @param uid* @param uids* @throws IOException */// 关注用户public static void addAttend(String uid, String... uids) throws IOException {// 创建连接Connection conn = ConnectionFactory.createConnection(conf);// 创建Table对象(新API),一共要操作3张表Table conTable = conn.getTable(TableName.valueOf(Constant.CONTENT));Table relaTable = conn.getTable(TableName.valueOf(Constant.RELATIONS));Table inboxTable = conn.getTable(TableName.valueOf(Constant.INBOX));List<Put> puts = new ArrayList<Put>();// 创建操作者的Put对象Put relaPut = new Put(Bytes.toBytes(uid));for (String s : uids) {// 向Put对象中组装数据relaPut.addColumn(Bytes.toBytes("attends"), Bytes.toBytes(s), Bytes.toBytes(s));// 创建被关注者的Put对象Put fansPut = new Put(Bytes.toBytes(s));// 向Put对象中组装数据fansPut.addColumn(Bytes.toBytes("fans"), Bytes.toBytes(uid), Bytes.toBytes(uid));puts.add(fansPut);}puts.add(relaPut);// 执行添加操作relaTable.put(puts);Put inboxPut = new Put(Bytes.toBytes(uid));for (String s : uids) {// 在微博内容表中获取被关注者的3条数据(uid_ts),一个人对应多条内容表Scan scan = new Scan(Bytes.toBytes(s), Bytes.toBytes(s + "|"));ResultScanner results = conTable.getScanner(scan);for (Result result : results) {String rowStr = Bytes.toString(result.getRow());String[] split = rowStr.split("_"); // 获取发布微博的时间戳byte[] row = result.getRow();inboxPut.addColumn(Bytes.toBytes("info"), Bytes.toBytes(s), Long.parseLong(split[1]), row);}}// 在微博收件箱表中添加操作人的关注者信息inboxTable.put(inboxPut);// 关闭资源conTable.close();relaTable.close();inboxTable.close();conn.close();}/*** 1、用户关系表*      删除操作者attends的待取关用户*         删除待取关用户fans的操作者* 2、微博收件箱表*      删除操作者的待取关用户的信息* @param uid* @param uids* @throws IOException*/// 取关用户public static void delAttend(String uid, String...uids) throws IOException {// 创建连接Connection conn = ConnectionFactory.createConnection(conf);// 创建Table对象(新API),一共要操作2张表Table relaTable = conn.getTable(TableName.valueOf(Constant.RELATIONS));Table inboxTable = conn.getTable(TableName.valueOf(Constant.INBOX));List<Delete> deletes = new ArrayList<Delete>();// 创建操作者的Delete对象Delete relaDel = new Delete(Bytes.toBytes(uid));for (String s : uids) {relaDel.addColumn(Bytes.toBytes("attends"), Bytes.toBytes(s));// 创建被取关者的Delete对象Delete fansDel = new Delete(Bytes.toBytes(s));fansDel.addColumn(Bytes.toBytes("fans"), Bytes.toBytes(uid));deletes.add(fansDel);}deletes.add(relaDel);// 执行删除操作relaTable.delete(deletes);// 删除微博邮件收件箱表的信息Delete inboxDel = new Delete(Bytes.toBytes(uid));for (String s : uids) {inboxDel.addColumns(Bytes.toBytes("info"), Bytes.toBytes(s)); // 注意:删除所有版本 addColumns}// 执行删除操作inboxTable.delete(inboxDel);// 关闭资源relaTable.close();inboxTable.close();conn.close();}// 获取微博内容(初始化页面内容)public static void getInitData(String uid) throws IOException {// 创建连接Connection conn = ConnectionFactory.createConnection(conf);// 创建Table对象Table inboxTable = conn.getTable(TableName.valueOf(Constant.INBOX));Table conTable = conn.getTable(TableName.valueOf(Constant.CONTENT));// 获取微博收件箱表数据Get inboxGet = new Get(Bytes.toBytes(uid));inboxGet.getMaxVersions();Result result = inboxTable.get(inboxGet);// 遍历返回内容并将其封装成内容的Get对象List<Get> gets = new ArrayList<Get>();Cell[] cells = result.rawCells();for (Cell cell : cells) {Get contGet = new Get(CellUtil.cloneValue(cell));gets.add(contGet);}// 根据微博收件箱获取的值去往微博内容表上去获取具体内容Result[] results = conTable.get(gets);for (Result result2 : results) {Cell[] cell2 = result2.rawCells();for (Cell cell : cell2) {System.out.println("行键:" + Bytes.toString(CellUtil.cloneRow(cell))+ " 值:" + Bytes.toString(CellUtil.cloneValue(cell)));}}// 关闭资源conTable.close();inboxTable.close();conn.close();}// 获取微博内容(查看某个人所有微博内容)public static void getData(String uid) throws IOException {// 创建连接Connection conn = ConnectionFactory.createConnection(conf);// 创建Table对象Table conTable = conn.getTable(TableName.valueOf(Constant.CONTENT));// 扫描(过滤器)Scan scan = new Scan();RowFilter rowFilter = new RowFilter(CompareFilter.CompareOp.EQUAL, new SubstringComparator(uid + "_"));scan.setFilter(rowFilter);ResultScanner results = conTable.getScanner(scan);// 遍历打印for (Result result : results) {Cell[] cells = result.rawCells();for (Cell cell : cells) {System.out.println("行键:" + Bytes.toString(CellUtil.cloneRow(cell))+ " 值:" + Bytes.toString(CellUtil.cloneValue(cell)));}}// 关闭资源conTable.close();conn.close();}
}

测试类:

package com.atguigu.weibo;import java.io.IOException;import com.atguigu.constant.Constant;
import com.atguigu.util.WeiboUtil;public class Weibo {public static void init() throws IOException {// 创建相关命名空间和表WeiboUtil.createNamespace(Constant.NAMESPACE);// 创建微博内容表WeiboUtil.createTable(Constant.CONTENT, 1, "info");// 创建用户关系表WeiboUtil.createTable(Constant.RELATIONS, 1, "attends", "fans");// 创建微博邮件箱表(多版本)WeiboUtil.createTable(Constant.INBOX, 3, "info");}public static void main(String[] args) throws IOException {// 测试(手动调用)// init();// 测试:1001,1002,1003,发布微博// WeiboUtil.createData("1001", "天气好");// WeiboUtil.createData("1002", "天气好吗");// WeiboUtil.createData("1003", "天气不好吗");// 测试:1001关注1002、1003和1004// WeiboUtil.addAttend("1001", "1002", "1003", "1004");// 测试:获取1001初始化页面信息// WeiboUtil.getInitData("1001");// 测试:1004发布微博// WeiboUtil.createData("1004", "have a good weather!!!");// WeiboUtil.createData("1004", "good weather");// WeiboUtil.createData("1004", "weather");// 测试:获取1001初始化页面信息WeiboUtil.getInitData("1001");// 测试:获取1001发布的微博信息// WeiboUtil.getData("1004");// 测试:取消关注WeiboUtil.delAttend("1001", "1004");// 测试:获取1001初始化页面信息WeiboUtil.getInitData("1001");}
}

第9章 扩展知识

9.1 HBase 在商业项目中的能力

每天:
  1) 消息量:发送和接收的消息数超过60亿
  2) 将近1000亿条数据的读写
  3) 高峰期每秒150万左右操作
  4) 整体读取数据占有约55%,写入占有45%
  5) 超过2PB的数据,涉及冗余共6PB数据
  6) 数据每月大概增长300千兆字节(300000M=300G)

9.2 布隆过滤器

  在日常生活中,包括在设计计算机软件时,我们经常要判断一个元素是否在一个集合中。比如在字处理软件中,需要检查一个英语单词是否拼写正确(也就是要判断它是否在已知的字典中);在 FBI,一个嫌疑人的名字是否已经在嫌疑名单上;在网络爬虫里,一个网址是否被访问过等等。最直接的方法就是将集合中全部的元素存在计算机中,遇到一个新元素时,将它和集合中的元素直接比较即可。一般来讲,计算机中的集合是用哈希表(hash table)来存储的。它的好处是快速准确,缺点是费存储空间。当集合比较小时,这个问题不显著,但是当集合巨大时,哈希表存储效率低的问题就显现出来了。比如说,一个像 Yahoo、Hotmail 和 Gmail 那样的公众电子邮件(email)提供商,总是需要过滤来自发送垃圾邮件的人(spamer)的垃圾邮件。一个办法就是记录下那些发垃圾邮件的 email 地址。由于那些发送者不停地在注册新的地址,全世界少说也有几十亿个发垃圾邮件的地址,将他们都存起来则需要大量的网络服务器。如果用哈希表,每存储一亿个 email 地址, 就需要 1.6GB 的内存(用哈希表实现的具体办法是将每一个 email 地址对应成一个八字节的信息指纹googlechinablog.com/2006/08/blog-post.html,然后将这些信息指纹存入哈希表,由于哈希表的存储效率一般只有 50%,因此一个 email 地址需要占用十六个字节。一亿个地址大约要 1.6GB, 即十六亿字节的内存)。因此存贮几十亿个邮件地址可能需要上百 GB 的内存。除非是超级计算机,一般服务器是无法存储的。
  布隆过滤器只需要哈希表 1/8 到 1/4 的大小就能解决同样的问题
  Bloom Filter 是一种空间效率很高的随机数据结构,它利用位数组很简洁地表示一个集合,并能判断一个元素是否属于这个集合Bloom Filter 的这种高效是有一定代价的:在判断一个元素是否属于某个集合时,有可能会把不属于这个集合的元素误认为属于这个集合(false positive)。因此,Bloom Filter 不适合那些“零错误”的应用场合。而在能容忍低错误率的应用场合下,Bloom Filter 通过极少的错误换取了存储空间的极大节省
  下面我们具体来看 Bloom Filter 是如何用位数组表示集合的。初始状态时,Bloom Filter 是一个包含 m 位的位数组,每一位都置为 0,如下图所示。
[外链图片转存失败(img-v2jVgCvS-1562638565419)(https://s2.ax1x.com/2019/03/10/ApG7LV.png)]
  为了表达 S={x1, x2, … ,xn} 这样一个 n 个元素的集合,Bloom Filter 使用 k 个相互独立的哈希函数(Hash Function),它们分别将集合中的每个元素映射到{1, … , m}的范围中。对任意一个元素 x,第 i 个哈希函数映射的位置 hi(x) 就会被置为 1(1≤i≤k)。注意,如果一个位置多次被置为 1,那么只有第一次会起作用,后面几次将没有任何效果。如下图所示,k=3,且有两个哈希函数选中同一个位置(从左边数第五位)。
[外链图片转存失败(img-RmMvVrcZ-1562638565419)(https://s2.ax1x.com/2019/03/10/ApGTs0.png)]
  在判断 y 是否属于这个集合时,我们对 y 应用 k 次哈希函数,如果所有 hi(y) 的位置都是 1(1≤i≤k),那么我们就认为 y 是集合中的元素,否则就认为 y 不是集合中的元素。如图下图所示,y1 就不是集合中的元素。y2 或者属于这个集合,或者刚好是一个 false positive。
[外链图片转存失败(img-fTt6EGXz-1562638565420)(https://s2.ax1x.com/2019/03/10/ApGoMq.png)]
详解如下:
  为了 add 一个元素,用 k 个 hash function 将它 hash 得到 bloom filter 中 k 个 bit 位,将这 k 个 bit 位置 1。
  为了 query 一个元素,即判断它是否在集合中,用 k 个 hash function 将它 hash 得到 k 个 bit 位。若这 k bits 全为 1,则此元素在集合中;若其中任一位不为 1,则此元素比不在集合中(因为如果在,则在 add 时已经把对应的 k 个 bits 位置为 1)。
  不允许 remove 元素,因为那样的话会把相应的 k 个 bits 位置为 0,而其中很有可能有其他元素对应的位。因此 remove 会引入 false negative,这是绝对不被允许的。
  布隆过滤器决不会漏掉任何一个在黑名单中的可疑地址。但是,它有一条不足之处,也就是它有极小的可能将一个不在黑名单中的电子邮件地址判定为在黑名单中,因为有可能某个好的邮件地址正巧对应一个八个都被设置成一的二进制位。好在这种可能性很小,我们把它称为误识概率
  布隆过滤器的好处在于快速,省空间,但是有一定的误识别率,常见的补救办法是在建立一个小的白名单,存储那些可能个别误判的邮件地址。
  布隆过滤器具体算法高级内容,如错误率估计,最优哈希函数个数计算,位数组大小计算,请参见 http://blog.csdn.net/jiaomeng/article/details/1495500。

9.3 HBase2.0 新特性

  2017年8月22日凌晨2点左右,HBase发布了2.0.0 alpha-2,相比于上一个版本,修复了500个补丁,我们来了解一下2.0版本的HBase新特性。
最新文档:
  http://hbase.apache.org/book.html#ttl
官方发布主页:
  http://mail-archives.apache.org/mod_mbox/www-announce/201708.mbox/<CADcMMgFzmX0xYYso-UAYbU7V8z-Obk1J4pxzbGkRzbP5Hps+iA@mail.gmail.com

举例:

  1. region 进行了多份冗余
      主 region 负责读写,从 region 维护在其他 HregionServer 中,负责读以及同步主 region 中的信息,如果同步不及时,是有可能出现 client 在从 region 中读到了脏数据(主 region 还没来得及把 memstore 中的变动的内容 flush)。
  2. 更多变动
      https://issues.apache.org/jira/secure/ReleaseNote.jspa?version=12340859&styleName=&projectId=12310753&Create=Create&atl_token=A5KQ-2QAV-T4JA-FDED|e6f233490acdf4785b697d4b457f7adb0a72b69f|lout

我的GitHub地址:https://github.com/heizemingjun
我的博客园地址:https://www.cnblogs.com/chenmingjun
我的蚂蚁笔记博客地址:https://blog.leanote.com/chenmingjun
Copyright ©2018~2019 黑泽君
【转载文章务必保留出处和署名,谢谢!】

大数据技术之_11_HBase学习_03_HBase 实战之谷粒微博(练习API) + 扩展知识(布隆过滤器+HBase2.0 新特性)相关推荐

  1. 第一课 大数据技术之Fink1.13的实战学习-部署使用和基础概念

    第一课 大数据技术之Fink1.13的实战学习 文章目录 第一课 大数据技术之Fink1.13的实战学习 第一节 Fink介绍 1.1 Flink介绍背景 1.2 Flink 的应用场景 1.3 流式 ...

  2. 第三课 大数据技术之Fink1.13的实战学习-时间和窗口

    第三课 大数据技术之Fink1.13的实战学习-时间和窗口 文章目录 第三课 大数据技术之Fink1.13的实战学习-时间和窗口 第一节 时间定义 1.1 Flink中的时间语义 1.2 两种时间语义 ...

  3. 第七课 大数据技术之Fink1.13的实战学习-Fink CEP

    第七课 大数据技术之Fink1.13的实战学习-Fink CEP 文章目录 第七课 大数据技术之Fink1.13的实战学习-Fink CEP 第一节 Fink CEP介绍 1.1 Flink CEP背 ...

  4. 第六课 大数据技术之Fink1.13的实战学习-Table Api和SQL

    第六课 大数据技术之Fink1.13的实战学习-Table Api和SQL 文章目录 第六课 大数据技术之Fink1.13的实战学习-Table Api和SQL 第一节 Fink SQL快速上手 1. ...

  5. 大数据技术之_19_Spark学习_07_Spark 性能调优 + 数据倾斜调优 + 运行资源调优 + 程序开发调优 + Shuffle 调优 + GC 调优 + Spark 企业应用案例

    大数据技术之_19_Spark学习_07 第1章 Spark 性能优化 1.1 调优基本原则 1.1.1 基本概念和原则 1.1.2 性能监控方式 1.1.3 调优要点 1.2 数据倾斜优化 1.2. ...

  6. 大数据技术之_20_Elasticsearch学习_01_概述 + 快速入门 + Java API 操作 + 创建、删除索引 + 新建、搜索、更新删除文档 + 条件查询 + 映射操作

    大数据技术之_20_Elasticsearch学习_01 一 概述 1.1 什么是搜索? 1.2 如果用数据库做搜索会怎么样? 1.3 什么是全文检索和 Lucene? 1.4 什么是 Elastic ...

  7. 大数据技术之_17_Storm学习_Storm 概述+Storm 基础知识+Storm 集群搭建+Storm 常用 API+Storm 分组策略和并发度

    大数据技术之_17_Storm学习 一 Storm 概述 1.1 离线计算是什么? 1.2 流式计算是什么? 1.3 Storm 是什么? 1.4 Storm 与 Hadoop 的区别 1.5 Sto ...

  8. 大数据技术之_16_Scala学习_13_Scala语言的数据结构和算法_Scala学习之旅收官之作

    大数据技术之_16_Scala学习_13 第十九章 Scala语言的数据结构和算法 19.1 数据结构(算法)的介绍 19.2 看几个实际编程中遇到的问题 19.2.1 一个五子棋程序 19.2.2 ...

  9. 大数据技术之_08_Hive学习_03_查询+函数

    大数据技术之_08_Hive学习_03 第6章 查询 6.1 基本查询(select ... from) 6.1.1 全表和特定列查询 6.1.2 列别名 6.1.3 算术运算符 6.1.4 常用函数 ...

最新文章

  1. 自制一个 elasticsearch-spring-boot-starter
  2. 登陆页老是提示验证码错误,validate验证控件IE下用remote方法明明返回true 但是还是报错,提示验证码错误...
  3. 数据库之Oracle(二)
  4. 【数据结构与算法】之深入解析“石子游戏V”的求解思路与算法示例
  5. matlab应用大全二手,MATLAB应用大全(附光盘)-JobPlus
  6. 使用SAP Analytics Cloud展示全球新冠肺炎确诊总人数的分布情况
  7. redis 安装错误 jemalloc.h: No such file or directory
  8. oracle清理asm归档日志,【Oracle】 rman 删除归档日志的命令
  9. WorkerMan 入门学习之(二)基础教程-Connection类的使用
  10. 图的遍历(深度优先搜索法和广度优先搜索法)
  11. mysql 默认时间_使用Sysbench对滴滴云MySQL进行基准测试
  12. java runnable 使用_java – 在哪里使用可调用以及在哪里使用Runnable接口?
  13. leetcode_150. 逆波兰表达式求值
  14. R语言高级算法之支持向量机(Support Vector Machine)
  15. C盘扩容好帮手——傲梅分区助手
  16. Java Socket 网络编程
  17. Django模板渲染错误
  18. cph = CoxPHFitter()训练过程中遇到的坑以及画图
  19. 人工神经网络的硬件实现,人工神经网络基本概念
  20. Arduino应用开发——LCD显示GIF动图

热门文章

  1. 浦东朋友夏天的时间线
  2. 下一代iPhone什么样
  3. 用友文件服务器恢复,用友U8账套数据恢复及备份.doc
  4. 利用机器学习实施网络犯罪 6种有效途径触目惊心
  5. VB计算MACD指标详细编码
  6. 《Python语言程序设计》王恺 王志 李涛 机械工业出版社第7章 I/O编程与异常 课后习题答案【强烈推荐】
  7. 梁祝二胡独奏_发展独奏
  8. fiddler监控手机请求 1
  9. DRM驱动(一)之显示处理器介绍
  10. Oracle X$Tables