「这是我参与11月更文挑战的第36天,活动详情查看:2021最后一次更文挑战」。
一、HBase 实战
1.1、需求分析
-
微博内容的浏览,数据库表设计
-
用户社交体现:关注用户,取关用户
-
拉取关注的人的微博内容
1.2、代码实现
1.2.1、代码设计总览
-
创建命名空间以及表名的定义
-
创建微博内容表(发布微博)
-
创建用户关系表(关注用户)
-
取关用户
-
获取关注的人的微博内容(前几条:inbox version)
-
获取微博详情
-
测试
1.2.2、创建命名空间以及表名的定义
public class Constants {
// HBase 配置信息
public static final Configuration CONFIGURATION = HBaseConfiguration.create();
// 命名空间
public static final String NAMESPACE = "weibo";
public static final String SYMBOL = ":";
// 微博内容表
public static final String CONTENT_TABLE = NAMESPACE + SYMBOL + "content";
public static final String CONTENT_TABLE_CF = "info";
public static final int CONTENT_TABLE_VERSIONS = 1;
// 用关系表表
public static final String RELATION_TABLE = NAMESPACE + SYMBOL + "relation";
public static final String RELATION_TABLE_CF1 = "attends";
public static final String RELATION_TABLE_CF2 = "fans ";
public static final int RELATION_TABLE_VERSIONS = 1;
// 收件箱表
public static final String INBOX_TABLE = NAMESPACE + SYMBOL + "inbox";
public static final String INBOX_TABLE_CF = "info";
public static final int INBOX_TABLE_VERSIONS = 2;
}
// 创建命名空间
public static void createNamespace(String namespace) throws Exception {
//1.获取Connection对象
Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);
//2.获取Admin对象
Admin admin = connection.getAdmin();
//3.构建命名空间描述器
NamespaceDescriptor namespaceDescriptor = NamespaceDescriptor.create(namespace).build();
//4.创建命名空间
admin.createNamespace(namespaceDescriptor);
//5.关闭资源
admin.close();
connection.close();
}
1.2.3、发布微博
| 方法名 | publishWeiBo |
|---|---|
| Table Name | weibo:content |
| RowKey | 用户ID_时间戳 |
| ColumnFamily | info |
| ColumnLabel | 内容 |
| Version | 1个版本 |
- 微博内容表中添加 1 条数据
- 微博收件箱表对所有粉丝用户添加数据
// 发布微博
public static void publishWeiBo(String uid, String content) throws Exception {
// 获取连接
Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);
// 第一部分:操作微博内容表
// 1.获取微博表对象
Table contentTable = connection.getTable(TableName.valueOf(Constants.CONTENT_TABLE));
// 2.获取当前时间戳
long ts = System.currentTimeMillis();
// 3.获取RowKey
String rowKey = uid + "_" + ts;
// 4.创建Put对象
Put contentPut = new Put(Bytes.toBytes(rowKey));
// 5.给Put对象赋值
contentPut.addColumn(Bytes.toBytes(Constants.CONTENT_TABLE_CF), Bytes.toBytes("content"), Bytes.toBytes(content));
// 6.执行插入数据操作
contentTable.put(contentPut);
// 第二部分:操作收件箱表
// 1.获取用户关系表对象
Table relationTable = connection.getTable(TableName.valueOf(Constants.RELATION_TABLE));
// 2.获取当前发布微博人的fans列族数据
Get get = new Get(Bytes.toBytes(uid));
get.addFamily(Bytes.toBytes(Constants.RELATION_TABLE_CF2));
Result result = relationTable.get(get);
// 3.创建一个集合,用于存放微博内容表的Put对象
List<Put> inboxPutList = new ArrayList<>();
// 4.遍历粉丝
for (Cell cell : result.rawCells()) {
// 5.构建微博收件箱表的Put对象
Put inboxPut = new Put(CellUtil.cloneQualifier(cell));
// 6.给收件箱表的Put对象赋值
inboxPut.addColumn(Bytes.toBytes(Constants.INBOX_TABLE_CF), Bytes.toBytes(uid), Bytes.toBytes(rowKey));
// 7.将收件箱表的Put对象存入集合
inboxPutList.add(inboxPut);
}
// 8.判断是否有粉丝
if (inboxPutList.size() > 0) {
// 获取收件箱表对象
Table inboxTable = connection.getTable(TableName.valueOf(Constants.INBOX_TABLE));
// 执行收件箱表数据插入操作
inboxTable.put(inboxPutList);
// 关闭收件箱表
inboxTable.close();
}
// 关闭资源
relationTable.close();
contentTable.close();
connection.close();
}
1.2.4、关注用户
| 方法名 | addAttends |
|---|---|
| Table Name | weibo:relation |
| RowKey | 用户ID |
| ColumnFamily | attends、fans |
| ColumnLabel | 关注用户ID、粉丝用户ID |
| Version | 1个版本 |
- 在微博用户关系表中,对当前主动操作的用户添加新关注的好友
- 在微博用户关系表中,对被关注的用户添加新的粉丝
- 微博收件箱表中添加所关注的用户发布的微博
// 关注用户
public static void addAttends(String uid, String... attends) throws Exception {
if (attends.length <= 0) {
System.out.println("请选择要关注的人!!!");
return;
}
// 获取连接
Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);
// 第一部分:操作用户关系表
// 1.获取用户关系表对象
Table relationTable = connection.getTable(TableName.valueOf(Constants.RELATION_TABLE));
// 2.创建一个集合,用于存放用户关系表的Put对象
List<Put> relationPuts = new ArrayList<>();
// 3.创建操作者的Put对象
Put uidPut = new Put(Bytes.toBytes(uid));
// 4.循环创建被关注者的Put对象
for (String attend : attends) {
// 5.给操作者的Put对象赋值
uidPut.addColumn(Bytes.toBytes(Constants.RELATION_TABLE_CF1), Bytes.toBytes(attend), Bytes.toBytes(attend));
// 6.创建被关注者的Put对象
Put attendPut = new Put(Bytes.toBytes(attend));
// 7.给被关注者的Put对象赋值
attendPut.addColumn(Bytes.toBytes(Constants.RELATION_TABLE_CF2), Bytes.toBytes(uid), Bytes.toBytes(uid));
// 8.将被关注者的Put对象放入集合
relationPuts.add(attendPut);
}
// 9.将操作者的Put对象添加至集合
relationPuts.add(uidPut);
// 10.执行用户关系表的插入数据操作
relationTable.put(relationPuts);
// 第二部分:操作用户收件箱表
// 1.获取微博内容表对象
Table contentTable = connection.getTable(TableName.valueOf(Constants.CONTENT_TABLE));
// 2.创建收件箱表的Put对象
Put inboxPut = new Put(Bytes.toBytes(uid));
// 3.循环attends,获取每个被关注者的近期发布的微博
for (String attend : attends) {
// 4.获取当前被关注者的近期发布的微博(scan)-> 集合ResultScan
Scan scan = new Scan().withStartRow(Bytes.toBytes(attend + "_")).withStopRow(Bytes.toBytes(attend + "|"));
ResultScanner resultScanner = contentTable.getScanner(scan);
// 定义一个时间戳
long ts = System.currentTimeMillis();
// 5.对获取的值进行遍历
for (Result result : resultScanner) {
// 6.给收件箱表的Put对象赋值
inboxPut.addColumn(Bytes.toBytes(Constants.INBOX_TABLE_CF), Bytes.toBytes(attend), ts++, result.getRow());
}
}
// 7.判断当前的Put对象是否为空
if (!inboxPut.isEmpty()) {
// 获取收件箱表对象
Table inboxTable = connection.getTable(TableName.valueOf(Constants.INBOX_TABLE));
// 插入数据
inboxTable.put(inboxPut);
// 关闭收件箱表连接
inboxTable.close();
}
// 关闭资源
contentTable.close();
relationTable.close();
connection.close();
}
1.2.5、移除(取关)用户
| 方法名 | addAttends |
|---|---|
| Table Name | weibo:relation |
| RowKey | 用户ID |
| ColumnFamily | attends、fans |
| ColumnLabel | 关注用户ID、粉丝用户ID |
| Version | 1个版本 |
- 在微博用户关系表中,对当前主动操作的用户移除取关的好友(attends)
- 在微博用户关系表中,对被取关的用户移除粉丝
- 微博收件箱中删除取关的用户发布的微博
// 取关用户
public static void deleteAttends(String uid, String... attends) throws Exception {
if (attends.length <= 0) {
System.out.println("请选择要取关的人!!!");
return;
}
// 获取连接
Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);
// 第一部分:操作用户关系表
Table relationTable = connection.getTable(TableName.valueOf(Constants.RELATION_TABLE));
// 2.创建一个集合,用于存放用户关系表的Delete对象
List<Delete> relDelList = new ArrayList<>();
// 3.创建操作者的Delete对象
Delete uidDel = new Delete(Bytes.toBytes(uid));
// 4.循环创建被取关者的Delete对象
for (String attend : attends) {
// 5.给操作者的Delete对象赋值
uidDel.addColumns(Bytes.toBytes(Constants.RELATION_TABLE_CF1), Bytes.toBytes(attend));
// 6.创建被取关者的Delete对象
Delete attDel = new Delete(Bytes.toBytes(attend));
// 7.给被取关着的Delete对象赋值
attDel.addColumns(Bytes.toBytes(Constants.RELATION_TABLE_CF2), Bytes.toBytes(uid));
// 8.将被取关着的Delete对象添加至集合
relDelList.add(attDel);
}
// 9.将操作者的Delete对象添加至集合
relDelList.add(uidDel);
// 10.执行用户关系表的删除操作
relationTable.delete(relDelList);
// 第二部分:操作用户收件箱表
// 1.获取收件箱表对象
Table inboxTable = connection.getTable(TableName.valueOf(Constants.INBOX_TABLE));
// 2.创建操作者的Delete对象
Delete inboxDel = new Delete(Bytes.toBytes(uid));
// 3.给操作者的Delete对象赋值
for (String attend : attends) {
inboxDel.addColumns(Bytes.toBytes(Constants.INBOX_TABLE_CF), Bytes.toBytes(attend));
}
// 4.执行收件箱表的删除操作
inboxTable.delete(inboxDel);
// 关闭资源
inboxTable.close();
relationTable.close();
connection.close();
}
1.2.6、获取关注的人的微博内容(前几条:inbox version)
- 从微博收件箱中获取所关注的用户的微博 RowKey
- 根据获取的 RowKey,得到微博内容
// 获取用户的初始化页面(用户登录进入主页面,显示所关注的人的微博内容)
public static void getInit(String uid) throws Exception {
// 1.获取连接
Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);
// 2.获取收件箱表对象
Table inboxTable = connection.getTable(TableName.valueOf(Constants.INBOX_TABLE));
// 3.获取微博内容表对象
Table contentTable = connection.getTable(TableName.valueOf(Constants.CONTENT_TABLE));
// 4.创建收件箱表Get对象,并获取数据(设置最大版本)
Get inboxGet = new Get(Bytes.toBytes(uid));
inboxGet.readAllVersions();
Result result = inboxTable.get(inboxGet);
// 5.遍历获取的数据
for (Cell cell : result.rawCells()) {
// 6.构建微博内容表Get对象
Get contentGet = new Get(CellUtil.cloneValue(cell));
// 7.获取该Get对象的数据内容
Result contentResult = contentTable.get(contentGet);
// 8.解析内容并打印
for (Cell contentCell : contentResult.rawCells()) {
System.out.printf("RK:{%s}\tCF:{%s}\tCN:{%s}\tCV:{%s}",
Bytes.toString(CellUtil.cloneRow(contentCell)),
Bytes.toString(CellUtil.cloneFamily(contentCell)),
Bytes.toString(CellUtil.cloneQualifier(contentCell)),
Bytes.toString(CellUtil.cloneValue(contentCell)));
System.out.println();
}
}
// 9.关闭资源
contentTable.close();
inboxTable.close();
connection.close();
}
1.2.7、获取微博详情
// 获取用户微博详情(点击进入某个人的微博详情)
public static void getWeiBoDetail(String uid) throws Exception {
// 1.获取连接
Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);
// 2.获取微博内容表对象
Table contentTable = connection.getTable(TableName.valueOf(Constants.CONTENT_TABLE));
// 3.构建Scan对象
Scan scan = new Scan();
// 3.1构建过滤器
RowFilter rowFilter = new RowFilter(CompareOperator.EQUAL, new SubstringComparator(uid + "_"));
scan.setFilter(rowFilter);
// 4.获取数据
ResultScanner resultScanner = contentTable.getScanner(scan);
// 5.解析数据并打印
for (Result result : resultScanner) {
for (Cell cell : result.rawCells()) {
System.out.printf("RK:{%s}\tCF:{%s}\tCN:{%s}\tCV:{%s}",
Bytes.toString(CellUtil.cloneRow(cell)),
Bytes.toString(CellUtil.cloneFamily(cell)),
Bytes.toString(CellUtil.cloneQualifier(cell)),
Bytes.toString(CellUtil.cloneValue(cell)));
System.out.println();
}
}
// 6.关闭资源
contentTable.close();
connection.close();
}
1.2.8、测试
public static void test() throws Exception {
// 1001发布微博
HBaseDao.publishWeiBo("1001", "赶紧下课吧!!!");
// 1002关注1001和1003
HBaseDao.addAttends("1002", "1001", "1003");
// 获取1002初始化页面
HBaseDao.getInit("1002");
System.out.println("===================111===================");
// 1003发布3条微博,同时1001发布2条微博
HBaseDao.publishWeiBo("1003", "谁说的赶紧下课!!!");
Thread.sleep(10);
HBaseDao.publishWeiBo("1001", "我没说话!!!");
Thread.sleep(10);
HBaseDao.publishWeiBo("1003", "那谁说的!!!");
Thread.sleep(10);
HBaseDao.publishWeiBo("1001", "反正飞机是下线了!!!");
Thread.sleep(10);
HBaseDao.publishWeiBo("1003", "你们爱咋滴咋滴!!!");
// 获取1002初始化页面
HBaseDao.getInit("1002");
System.out.println("===================222===================");
// 1002取关1003
HBaseDao.deleteAttends("1002", "1003");
// 获取1002初始化页面
HBaseDao.getInit("1002");
System.out.println("===================333===================");
// 1002再次关注1003
HBaseDao.addAttends("1002", "1003");
// 获取1002初始化页面
HBaseDao.getInit("1002");
System.out.println("===================444===================");
// 获取1001微博详情
HBaseDao.getWeiBoDetail("1001");
}