hbase项目
四、HBase 项目
4.1、涉及概念梳理:命名空间
4.1.1、命名空间的结构
1) Table:表,所有的表都是命名空间的成员,即表必属于某个命名空间,如果没有指定, 则在 default 默认的命名空间中。
2) RegionServer group:一个命名空间包含了默认的 RegionServer Group。
3) Permission:权限,命名空间能够让我们来定义访问控制列表 ACL(Access Control List)。 例如,创建表,读取表,删除,更新等等操作。
4) Quota:限额,可以强制一个命名空间可包含的 region 的数量。(属性:hbase.quota.enabled)
4.1.2、命名空间的使用
1) 创建命名空间
hbase(main):002:0> create_namespace 'ns_school'
2) 创建表时指定命名空间
hbase(main):004:0> create 'ns_school:tbl_student','info'
4.2、微博系统
4.1.1、需求分析
1) 微博内容的浏览,数据库表设计
2) 用户社交体现:关注用户,取关用户
3) 拉取关注的人的微博内容
4.1.2、代码实现
代码设计总览:
1) 创建命名空间以及表名的定义
2) 创建微博内容表
3) 创建用户关系表
4) 创建用户微博内容接收邮件表
5) 发布微博内容
6) 添加关注用户
7) 移除(取关)用户
8) 获取关注的人的微博内容
9) 测试
1) 创建命名空间以及表名的定义
//获取配置 conf
private Configuration conf = HBaseConfiguration.create();
//在下面每一个用到配置的加上
conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum", "master,node1,node2");
conf.set("hbase.zookeeper.property.clientPort", "2181");
conf.set("hbase.master", "master:60000");
//微博内容表的表名
private static final byte[] TABLE_CONTENT= Bytes.toBytes("ns_weibo:content");
//用户关系表的表名
private static final byte[] TABLE_RELATION = Bytes.toBytes("ns_weibo:relation");
//微博收件箱表的表名
private static final byte[] TABLE_INBOX = Bytes.toBytes("ns_weibo:inbox");
/**
* 初始化命名空间
* @param args
*/
public void initNamespace(){
HBaseAdmin admin = null;
try {
Connection connection = ConnectionFactory.createConnection(conf); admin = (HBaseAdmin) connection.getAdmin();
//命名空间类似于关系型数据库中的 schema,可以想象成文件夹
NamespaceDescriptor weibo = NamespaceDescriptor.create("ns_weibo")
.addConfiguration("creator", "Jinji")
.addConfiguration("create_time", System.currentTimeMillis() + "")
.build(); admin.createNamespace(weibo);
} catch (MasterNotRunningException e) {
e.printStackTrace();
} catch (ZooKeeperConnectionException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(null != admin){
try {
admin.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 创建微博内容表
* Table Name:ns_weibo:content
* RowKey:用户 ID_时间戳
* ColumnFamily:info
* ColumnLabel:标题,内容,图片 URL
* Version:1 个版本
*/
public void createTableContent(){
HBaseAdmin admin = null; Connection connection = null;
try {
connection = ConnectionFactory.createConnection(conf);
admin = (HBaseAdmin) connection.getAdmin();
//创建表表述
HTableDescriptor contentTableDescriptor = new HTableDescriptor(TableName.valueOf(TABLE_CONTENT));
//创建列族描述
HColumnDescriptor infoColumnDescriptor = new
HColumnDescriptor(Bytes.toBytes("info"));
//设置块缓存
infoColumnDescriptor.setBlockCacheEnabled(true);
//设置块缓存大小 infoColumnDescriptor.setBlocksize(2097152);
//设置压缩方式
//infoColumnDescriptor.setCompressionType(Algorithm.SNAPPY);
//设置版本确界 infoColumnDescriptor.setMaxVersions(1); infoColumnDescriptor.setMinVersions(1);
contentTableDescriptor.addFamily(infoColumnDescriptor);
admin.createTable(contentTableDescriptor);
} catch (IOException e) {
e.printStackTrace();
} finally{
if(null != admin){
try {
admin.close();
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 用户关系表
* Table Name:ns_weibo:relation
* RowKey:用户 ID
* ColumnFamily:attends,fans
* ColumnLabel:关注用户 ID,粉丝用户 ID
* ColumnValue:用户 ID
* Version:1 个版本
*/
public void createTableRelation(){
HBaseAdmin admin = null;
try {
Connection connection = ConnectionFactory.createConnection(conf);
admin = (HBaseAdmin) connection.getAdmin();
HTableDescriptor relationTableDescriptor = new
HTableDescriptor(TableName.valueOf(TABLE_RELATION));
//关注的人的列族
HColumnDescriptor attendColumnDescriptor = new HColumnDescriptor(Bytes.toBytes("attends"));
//设置块缓存
attendColumnDescriptor.setBlockCacheEnabled(true);
//设置块缓存大小 attendColumnDescriptor.setBlocksize(2097152);
//设置压缩方式
//attendColumnDescriptor.setCompressionType(Algorithm.SNAPPY);
//设置版本确界 attendColumnDescriptor.setMaxVersions(1);
attendColumnDescriptor.setMinVersions(1);
//粉丝列族
HColumnDescriptor fansColumnDescriptor = new
HColumnDescriptor(Bytes.toBytes("fans"));
fansColumnDescriptor.setBlockCacheEnabled(true);
fansColumnDescriptor.setBlocksize(2097152);
fansColumnDescriptor.setMaxVersions(1);
fansColumnDescriptor.setMinVersions(1);
relationTableDescriptor.addFamily(attendColumnDescriptor);
relationTableDescriptor.addFamily(fansColumnDescriptor);
admin.createTable(relationTableDescriptor);
} catch (MasterNotRunningException e) {
e.printStackTrace();
} catch (ZooKeeperConnectionException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(null != admin){
try {
admin.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
4) 创建微博收件箱表
表结构:
/**
* 创建微博收件箱表
* Table Name: ns_weibo:inbox
* RowKey:用户 ID
* ColumnFamily:info
* ColumnLabel:用户 ID_发布微博的人的用户 ID
* ColumnValue:关注的人的微博的 RowKey
* Version:1000
*/
public void createTableInbox(){
HBaseAdmin admin = null;
try {
Connection connection = ConnectionFactory.createConnection(conf);
admin = (HBaseAdmin) connection.getAdmin();
HTableDescriptor inboxTableDescriptor = new HTableDescriptor(TableName.valueOf(TABLE_INBOX));
HColumnDescriptor infoColumnDescriptor = new
HColumnDescriptor(Bytes.toBytes("info"));
infoColumnDescriptor.setBlockCacheEnabled(true);
infoColumnDescriptor.setBlocksize(2097152);
infoColumnDescriptor.setMaxVersions(1000);
infoColumnDescriptor.setMinVersions(1000);
inboxTableDescriptor.addFamily(infoColumnDescriptor);;
admin.createTable(inboxTableDescriptor);
} catch (MasterNotRunningException e) {
e.printStackTrace();
} catch (ZooKeeperConnectionException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(null != admin){
try {
admin.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
5) 发布微博内容
a、微博内容表中添加 1 条数据
b、微博收件箱表对所有粉丝用户添加数据
代码:Message.java
package com.z.hbase.weibo;
public class Message {
private String uid;
private String timestamp; private String content;
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public String toString() {
return "Message [uid=" + uid + ", timestamp=" + timestamp + ", content=" + content +
"]";
}
}
代码:public void publishContent(String uid, String content)
/**
* 发布微博
* a、微博内容表中数据+1
* b、向微博收件箱表中加入微博的 Rowkey
*/
public void publishContent(String uid, String content){
Connection connection = null;
try {
connection = ConnectionFactory.createConnection(conf);
//a、微博内容表中添加 1 条数据,首先获取微博内容表描述
Table contentTable = connection.getTable(TableName.valueOf(TABLE_CONTENT));
//组装 Rowkey
long timestamp = System.currentTimeMillis();
Table relationTable = connection.getTable(TableName.valueOf(TABLE_RELATION));
//b.2、取出目标数据
Get get = new Get(Bytes.toBytes(uid));
get.addFamily(Bytes.toBytes("fans"));
Result result = relationTable.get(get);
List<byte[]> fans = newArrayList<byte[]>();
//遍历取出当前发布微博的用户的所有粉丝数据
for(Cell cell : result.rawCells()){
fans.add(CellUtil.cloneQualifier(cell));
}
//如果该用户没有粉丝,则直接 return
if(fans.size() <= 0) return;
//开始操作收件箱表
Table inboxTable = connection.getTable(TableName.valueOf(TABLE_INBOX));
//每一个粉丝,都要向收件箱中添加该微博的内容,所以每一个粉丝都是一个 Put
//对象
List<Put> puts = newArrayList<Put>();
for(byte[] fan : fans){
Put fansPut = new Put(fan);
fansPut.addColumn(Bytes.toBytes("info"), Bytes.toBytes(uid), timestamp, Bytes.toBytes(rowKey));
puts.add(fansPut);
}
inboxTable.put(puts);
} catch (IOException e) {
e.printStackTrace();
}finally{
if(null != connection){
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
6) 添加关注用户
a、在微博用户关系表中,对当前主动操作的用户添加新关注的好友 b、在微博用户关系表中,对被关注的用户添加新的粉丝 c、微博收件箱表中添加所关注的用户发布的微博
代码实现:public void addAttends(String uid, String... attends)
/**
* 关注用户逻辑
* a、在微博用户关系表中,对当前主动操作的用户添加新的关注的好友
* b、在微博用户关系表中,对被关注的用户添加粉丝(当前操作的用户)
* c、当前操作用户的微博收件箱添加所关注的用户发布的微博 rowkey
*/
public void addAttends(String uid, String... attends){
//参数过滤
if(attends == null || attends.length <= 0 || uid == null || uid.length() <= 0){
return;
}
Connection connection = null;
try {
connection = ConnectionFactory.createConnection(conf);
//用户关系表操作对象(连接到用户关系表)
Table relationTable = connection.getTable(TableName.valueOf(TABLE_RELATION)); List<Put> puts = newArrayList<Put>();
//a、在微博用户关系表中,添加新关注的好友
Put attendPut = new Put(Bytes.toBytes(uid));
for(String attend : attends){
//为当前用户添加关注的人
attendPut.addColumn(Bytes.toBytes("attends"), Bytes.toBytes(attend), Bytes.toBytes(attend));
//b、为被关注的人,添加粉丝
Put fansPut = new Put(Bytes.toBytes(attend));
fansPut.addColumn(Bytes.toBytes("fans"), Bytes.toBytes(uid),
Bytes.toBytes(uid));
//将所有关注的人一个一个的添加到 puts(List)集合中 puts.add(fansPut);
}
puts.add(attendPut); relationTable.put(puts);
//c.1、微博收件箱添加关注的用户发布的微博内容(content)的 rowkey
Table contentTable = connection.getTable(TableName.valueOf(TABLE_CONTENT));
Scan scan = new Scan();
//用于存放取出来的关注的人所发布的微博的 rowkey
List<byte[]> rowkeys = new ArrayList<byte[]>();
for(String attend : attends){
//过滤扫描 rowkey,即:前置位匹配被关注的人的 uid_
RowFilter filter = new RowFilter(CompareFilter.CompareOp.EQUAL, new SubstringComparator(attend + "_"));
//为扫描对象指定过滤规则
scan.setFilter(filter);
//通过扫描对象得到 scanner
ResultScanner result = contentTable.getScanner(scan);
//迭代器遍历扫描出来的结果集
Iterator<Result> iterator = result.iterator();
while(iterator.hasNext()){
//取出每一个符合扫描结果的那一行数据
Result r = iterator.next();
for(Cell cell : r.rawCells()){
//将得到的 rowkey 放置于集合容器中
rowkeys.add(CellUtil.cloneRow(cell));
}
}
}
//c.2、将取出的微博 rowkey 放置于当前操作的用户的收件箱中
if(rowkeys.size() <= 0) return;
//得到微博收件箱表的操作对象
Table inboxTable = connection.getTable(TableName.valueOf(TABLE_INBOX));
//用于存放多个关注的用户的发布的多条微博 rowkey 信息
List<Put> inboxPutList = newArrayList<Put>();
for(byte[] rk : rowkeys){
Put put = new Put(Bytes.toBytes(uid));
//uid_timestamp
String rowKey= Bytes.toString(rk);
//截取 uid
String attendUID = rowKey.substring(0, rowKey.indexOf("_"));
long timestamp = Long.parseLong(rowKey.substring(rowKey.indexOf("_") + 1));
//将微博 rowkey 添加到指定单元格中
put.addColumn(Bytes.toBytes("info"), Bytes.toBytes(attendUID), timestamp, rk); inboxPutList.add(put);
}
inboxTable.put(inboxPutList);
} catch (IOException e) {
e.printStackTrace();
}finally{
if(null != connection){
try {
connection.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
7) 移除(取关)用户
a、在微博用户关系表中,对当前主动操作的用户移除取关的好友(attends) b、在微博用户关系表中,对被取关的用户移除粉丝 c、微博收件箱中删除取关的用户发布的微博
代码:public void removeAttends(String uid, String... attends)
/**
* 取消关注(remove)
* a、在微博用户关系表中,对当前主动操作的用户删除对应取关的好友
* b、在微博用户关系表中,对被取消关注的人删除粉丝(当前操作人)
* c、从收件箱中,删除取关的人的微博的 rowkey
*
*/
public void removeAttends(String uid, String... attends){
//过滤数据
if(uid == null || uid.length() <= 0 || attends == null || attends.length <= 0) return;
try {
Connection connection = ConnectionFactory.createConnection(conf);
//a、在微博用户关系表中,删除已关注的好友
Table relationTable = connection.getTable(TableName.valueOf(TABLE_RELATION));
//待删除的用户关系表中的所有数据
List<Delete> deleteList = newArrayList<Delete>();
//当前取关操作者的 uid 对应的 Delete 对象
Delete attendDelete = new Delete(Bytes.toBytes(uid));
//上面进行标记的,要放入循环里才能真的执行全部移除关系表中的对应关注表
//遍历取关,同时每次取关都要将被取关的人的粉丝-1 for(String attend : attends){
attendDelete.addColumn(Bytes.toBytes("attends"), Bytes.toBytes(attend));
//b、在微博用户关系表中,对被取消关注的人删除粉丝(当前操作人)
Delete fansDelete = new Delete(Bytes.toBytes(attend));
fansDelete.addColumn(Bytes.toBytes("fans"), Bytes.toBytes(uid));
deleteList.add(fansDelete);
}
deleteList.add(attendDelete);
//同样上面放入上面的循环里面
relationTable.delete(deleteList);
//c、删除取关的人的微博 rowkey 从 收件箱表中
Table inboxTable = connection.getTable(TableName.valueOf(TABLE_INBOX));
Delete inboxDelete = new Delete(Bytes.toBytes(uid));
for(String attend : attends){
inboxDelete.addColumn(Bytes.toBytes("info"), Bytes.toBytes(attend));
}
inboxTable.delete(inboxDelete);
} catch (IOException e) {
e.printStackTrace();
}
}
8) 获取关注的人的微博内容
a、从微博收件箱中获取所关注的用户的微博 RowKey b、根据获取的 RowKey,得到微博内容
代码实现:public List<Message> getAttendsContent(String uid)
/**
* 获取微博实际内容
* a、从微博收件箱中获取所有关注的人的发布的微博的 rowkey
* b、根据得到的 rowkey 去微博内容表中得到数据
* c、将得到的数据封装到 Message 对象中
*/
public List<Message> getAttendsContent(String uid){
Connection connection = null;
try {
connection = ConnectionFactory.createConnection(conf);
Table inboxTable = connection.getTable(TableName.valueOf(TABLE_INBOX));
//a、从收件箱中取得微博 rowKey Get get = new Get(Bytes.toBytes(uid));
//设置最大版本号 get.setMaxVersions(5);
List<byte[]> rowkeys = new ArrayList<byte[]>(); Result result = inboxTable.get(get);
for(Cell cell : result.rawCells()){
rowkeys.add(CellUtil.cloneValue(cell));
}
//b、根据取出的所有 rowkey 去微博内容表中检索数据
Table contentTable = connection.getTable(TableName.valueOf(TABLE_CONTENT));
List<Get> gets = newArrayList<Get>();
//根据 rowkey 取出对应微博的具体内容
for(byte[] rk : rowkeys){
Get g = new Get(rk); gets.add(g);
}
//得到所有的微博内容的 result 对象
Result[] results = contentTable.get(gets);
//将每一条微博内容都封装为消息对象
List<Message> messages = newArrayList<Message>();
for(Result res : results){
for(Cell cell : res.rawCells()){
Message message = new Message();
String rowKey = Bytes.toString(CellUtil.cloneRow(cell));
String userid = rowKey.substring(0, rowKey.indexOf("_"));
String timestamp = rowKey.substring(rowKey.indexOf("_") + 1);
String content = Bytes.toString(CellUtil.cloneValue(cell));
message.setContent(content);
message.setTimestamp(timestamp);
message.setUid(userid);
messages.add(message);
}
}
return messages;
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
9) 测试
-- 测试发布微博内容
public void testPublishContent(WeiBo wb)
-- 测试添加关注
public void testAddAttend(WeiBo wb)
-- 测试取消关注
public void testRemoveAttend(WeiBo wb)
-- 测试展示内容
public void testShowMessage(WeiBo wb)
代码:
/**
* 发布微博内容
* 添加关注
* 取消关注
* 展示内容
*/
public void testPublishContent(WeiBo wb){
wb.publishContent("0001", "今天买了一包空气,送了点薯片,非常开心!!");
wb.publishContent("0001", "今天天气不错。");
}
public void testAddAttend(WeiBo wb){
wb.publishContent("0008", "准备下课!");
wb.publishContent("0009", "准备关机!");
wb.addAttends("0001", "0008", "0009");
}
public void testRemoveAttend(WeiBo wb){
wb.removeAttends("0001", "0008");
}
public void testShowMessage(WeiBo wb){
List<Message> messages = wb.getAttendsContent("0001");
for(Message message : messages){
System.out.println(message);
}
}
public static void main(String[] args) {
WeiBo weibo = new WeiBo(); weibo.initTable();
weibo.testPublishContent(weibo);
weibo.testAddAttend(weibo); weibo.testShowMessage(weibo); weibo.testRemoveAttend(weibo);
weibo.testShowMessage(weibo);
}
五、总结
不一定所有的企业都会使用 HBase,大数据的框架可以是相互配合相互依赖的,同时,根据 不同的业务,部分框架之间的使用也可以是相互独立的。例如有些企业在处理整个业务时, 只是用 HDFS+Spark 部分的内容。所以在学习 HBase 框架时,一定要有宏观思维,了解其 框架特性,不一定非要在所有的业务中使用所有的框架,要具体情况具体分析,酌情选择。
5.1、HBase在商业项目中的能力
每天:
1) 消息量:发送和接收的消息数超过 60 亿
2) 将近 1000 亿条数据的读写
3) 高峰期每秒 150 万左右操作
4) 整体读取数据占有约 55%,写入占有 45%
5) 超过 2PB 的数据,涉及冗余共 6PB 数据
6) 数据每月大概增长 300 千兆字节。
5.2、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/<CADcMMgFzmX0x
YYso-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%7Ce6f233490acdf4785b697
d4b457f7adb0a72b69f%7Clout
https://blog.csdn.net/a519781181/article/details/79423512
hbase项目的更多相关文章
- 【HBase学习】Apache HBase项目简介
原创声明:转载请注明作者和原始链接 http://www.cnblogs.com/zhangningbo/p/4068957.html 英文原版:http://hbase.apache.o ...
- Hadoo生态中pHive HBase 项目的区别
http://jenmhdn.iteye.com/blog/1678789 导读:Apache Hive是一个构建于Hadoop(分布式系统基础架构)顶层的数据仓库,Apache HBase是运行于H ...
- Hbase 项目
需求分析 1) 微博内容的浏览,数据库表设计 2) 用户社交体现:关注用户,取关用户 3) 拉取关注的人的微博内容 表结构 代码实现 1) 创建命名空间以及表名的定义 2) 创建微博内容表 3) 创 ...
- Hbase项目(完整版)
涉及概念梳理:命名空间 4.1.1.命名空间的结构 1) Table:表,所有的表都是命名空间的成员,即表必属于某个命名空间,如果没有指定,则在default默认的命名空间中. 2) RegionSe ...
- 【Hadoop学习】Apache HBase项目简介
正在撰写,稍后来访……
- HBase笔记:对HBase原理的简单理解
早些时候学习hadoop的技术,我一直对里面两项技术倍感困惑,一个是zookeeper,一个就是Hbase了.现在有机会专职做大数据相关的项目,终于看到了HBase实战的项目,也因此有机会搞懂Hbas ...
- HBase应用开发回顾与总结系列之一:概述HBase设计规范
概述 笔者本人接触研究HBase也有半年之久了,虽说不上深入和系统,但至少算是比较沉迷.作为部门里大数据技术的探路者,笔者还要承担起技术传播的职责,所以在摸索研究的过程中总是不断地进行总结和测试, ...
- HBase学习(十四)LINUX下用Eclipse构建HBase开发环境
Eclipse,HBase版本号眼下没有发现须要特别指定 1:从HBase集群中复制一份Hbase部署文件,放置在开发端某一文件夹下(如在/app/hadoop/hbase096文件夹下). 2:在e ...
- Phoenix(sql on hbase)简单介绍
Phoenix(sql on hbase)简单介绍 介绍: Phoenix is a SQL skin over HBase delivered as a client-embedded JDBC d ...
随机推荐
- 脱壳系列(二) - EZIP 壳
程序: 运行程序 用 PEiD 查壳 EZIP 1.0 用 OD 打开 按 F8 往下走 这个看似是 OEP 的地方却不是 OEP 因为代码段从 00401000 开始 可以看到,壳伪造了 3 个区段 ...
- python socket打造一个定位工具
前言: 刚刚学习socket,打算后期得学习 怎么写exploit. 原理: 其实很简单,客户端写个爬虫.然后将获取到的IP放入高德地图 在通过socket发送.利用ngrok达到能外网搞事. 准备: ...
- Docker垃圾回收机制
由Docker垃圾回收机制引发的一场血案 AlstonWilliams 关注 2017.04.01 19:00* 字数 1398 阅读 253评论 0喜欢 0 今天早晨,在我还没睡醒的时候,我们团队中 ...
- 关于C++成员函数指针的使用
在做项目的时候,遇到了在类中根据不同的调用函数,在被调用函数的某处需要做不同的处理,本来就想着直接在类中设个标记变量判断下就好了,不过觉得这样代码可能看起来会有些凌乱,而且效率估计有些低,于是想起来使 ...
- 检测客户端系统-PHP
if(isset($_SERVER['HTTP_USER_AGENT'])) { $userAgent = strtolower($_SERVER['HTTP_USER_AGENT']); $clie ...
- Service和Thread的关系
Service确实是运行在主线程里的,也就是说如果你在Service里编写了非常耗时的代码,程序必定有问题. Android的后台就是指,它的运行是完全不依赖UI的.即使Activity被销毁,或者程 ...
- CBV 验证装饰器的使用
下面是3种方式: from django.shortcuts import render, redirect from django.views import View # Create your v ...
- malloc()与calloc区别 (转)
另外说明: 1.分配内存空间函数malloc 调用形式: (类型说明符*) malloc (size) 功能:在内存的动态存储区中分配一块长度为"size" 字节的连续区域.函数的 ...
- Python 小练习三 发邮件
import smtplib,os from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipar ...
- Tomcat ( 单机多 Tomcat 并存 )
简介: Tomcat 扩展( 一台服务器运行多个 tomcat ) 一.安装 JDK .Tomcat shell > rpm -ivh jdk-8u25-linux-x64.rpm # 安装 j ...