简

人生短暂,学海无边,而大道至简。


  • 首页

  • 归档

  • 分类

  • 标签

WebSocket、Socket、TCP、HTTP区别

发表于 2020-05-22

Springboot项目的接口防刷

发表于 2020-05-19 | 分类于 spring boot

创建一个注解,通过注解控制接口请求次数

1
2
3
4
5
6
7
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AccessControl {
// 在 second 秒内,最大只能请求 maxCount 次
int seconds();
int maxCount();
}

拦截器处理注解信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@Component
public class AccessControlInterceptor extends HandlerInterceptorAdapter {

@Autowired
private RedisTemplate redisTemplate;

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//判断请求是否属于方法的请求
if(handler instanceof HandlerMethod){
HandlerMethod hm = (HandlerMethod) handler;
//获取方法中的注解,看是否有该注解
AccessControl accessControl = hm.getMethodAnnotation(AccessControl.class);
if(accessControl == null){
return true;
}
int seconds = accessControl.seconds();
int maxCount = accessControl.maxCount();
String key = request.getRequestURI()+request.getSession().getId();
// 从缓存中获取,当前这个请求访问了几次
Integer redisCount = (Integer) redisTemplate.opsForValue().get(key);
if(redisCount == null){
redisTemplate.opsForValue().set(key,1,seconds, TimeUnit.SECONDS);
}else{
if(redisCount.intValue() >= maxCount){
render(response,"次数超过");
return false;
}
redisTemplate.opsForValue().increment(key);
}
return true;
}

return super.preHandle(request, response, handler);

}
private void render(HttpServletResponse response, String msg)throws Exception {
response.setContentType("application/json;charset=UTF-8");
OutputStream out = response.getOutputStream();
out.write(msg.getBytes("UTF-8"));
out.flush();
out.close();
}
}

注册拦截器

1
2
3
4
5
6
7
8
9
10
11
@Configuration
public class WebConfig implements WebMvcConfigurer {

@Autowired
private AccessControlInterceptor interceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor);
}
}

使用

1
2
3
4
@AccessControl(seconds = 60,maxCount = 1)
@RequestMapping("register")
public ModelAndView register(User user) throws Exception{
//.......

深入理解Volatile关键字及其实现原理

发表于 2020-05-03 | 分类于 java

volatile通常被比喻成”轻量级的synchronized”,是Java中提供的另一种解决可见性和有序性问题的方案。

volatile的原理

可见性实现

为了提高处理器的执行速度,在处理器和内存之间增加了多级缓存来提升。但是由于引入了多级缓存,就存在缓存数据不一致问题。在多处理器下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议。

缓存一致性协议:每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器要对这个数据进行修改操作的时候,会强制重新从系统内存里把数据读到处理器缓存里。

对于volatile变量,当对volatile变量进行写操作的时候,JVM会向处理器发送一条lock前缀的指令,将这个缓存中的变量回写到系统主存中。而其他处理器的缓存由于遵守了缓存一致性协议,也会把这个变量的值从主存加载到自己的缓存中。这就保证了一个volatile在并发编程中,其值在多个缓存中是可见的。

每个线程的工作内存是CPU寄存器和高速缓存的抽象。

两条线程Thread-A与Threab-B同时操作主存中的一个volatile变量i时,Thread-A写了变量i,那么:

Thread-A发出LOCK#指令 发出的LOCK#指令锁总线(或锁缓存行),同时让Thread-B高速缓存中的缓存行内容失效。

Thread-A向主存回写最新修改的i Thread-B读取变量i,那么:

Thread-B发现对应地址的缓存行被锁了,等待锁的释放,缓存一致性协议会保证它读取到最新的值

由此可以看出,volatile关键字的读和普通变量的读取相比基本没差别,差别主要还是在变量的写操作上。

有序性

Happen-before

JSR 133中对Happen-before的定义:如果a happen-before b,则a所做的任何操作对b是可见的。

happen-before规则:

同一个线程中的,前面的操作 happen-before 后续的操作。(即单线程内按代码顺序执行。但是,在不影响在单线程环境执行结果的前提下,编译器和处理器可以进行重排序,这是合法的。换句话说,这一是规则无法保证编译重排和指令重排)。
监视器上的解锁操作 happen-before 其后续的加锁操作。(Synchronized 规则)
对volatile变量的写操作 happen-before 后续的读操作。(volatile 规则)
线程的start() 方法 happen-before 该线程所有的后续操作。(线程启动规则)
线程所有的操作 happen-before 其他线程在该线程上调用 join 返回成功后的操作。
如果 a happen-before b,b happen-before c,则a happen-before c(传递性)。

有序性即程序执行的顺序按照代码的先后顺序执行。

除了引入了时间片以外,由于处理器优化和指令重排等,CPU还可能对输入代码进行乱序执行,这就是可能存在有序性问题。

而volatile除了可以保证数据的可见性之外,还有一个强大的功能,那就是他可以禁止指令重排优化等。

普通的变量仅仅会保证在该方法的执行过程中所依赖的赋值结果的地方都能获得正确的结果,而不能保证变量的赋值操作的顺序与程序代码中的执行顺序一致。

volatile可以禁止指令重排,这就保证了代码的程序会严格按照代码的先后顺序执行。这就保证了有序性。被volatile修饰的变量的操作,会严格按照代码顺序执行。

volatile与synchronized的区别

  • (1)使用上的区别:Volatile只能修饰变量,synchronized只能修饰方法和语句块
  • (2)对原子性的保证:synchronized可以保证原子性,Volatile不能保证原子性
  • (3)对可见性的保证:都可以保证可见性,但实现原理不同,Volatile对变量加了lock,synchronized使用monitorEnter和monitorexit monitor JVM
  • (4)对有序性的保证:Volatile能保证有序,synchronized可以保证有序性,但是代价(重量级)并发退化到串行
  • (5)synchronized引起阻塞,Volatile不会引起阻塞

使用vscode搭建个人笔记环境

发表于 2020-04-20 | 分类于 其他

下载安装vscode

安装插件

中文包

Chinese (Simplified) Language Pack for Visual Studio Code

Markdown显示插件

类似于github的模板:Markdown Preview Github Styling

Markdown编辑插件

Markdown Shortcuts

图床插件

picgo

配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Pic Bed: Current

Picgo › Pic Bed › Github: Branch
master

Picgo › Pic Bed › Github: Custom Url
https://cdn.jsdelivr.net/gh/huingsggn/image-cloudings

Picgo › Pic Bed › Github: Path
img/

Picgo › Pic Bed › Github: Repo
huingsggn/image-cloudings

Picgo › Pic Bed › Github: Token

文件头插件

vscode-fileheader

配置:

1
2
3
4
5
6
7
8
9
Fileheader: Author
初晨
Fileheader: Last Modified By

Fileheader: Tpl

---\r\ntitle: \r\ntags: []\r\ncategory: \r\nauthor: {author}\r\ndate: {createTime}\r\n---

使用 Ctrl+Alt+I就可以生成要的格式

使用

在git上新建一个保存图片的库,按照上面的配置picgo,后 图片复制后 Ctrl+Alt+U即可上传到仓库,并且生成markdown的信息。

在git新建笔记库,在vscode代码库中 拉取后,就可以编写,加入库,推送等等操作。

20200420142856

预览html文档的插件

HBase介绍安装与操作

发表于 2020-01-20 | 分类于 HBase

介绍

  • HBase是Apache Hadoop的数据库,能够对大型数据提供随机、实时的读写访问,是Google的BigTable的开源实现。
  • HBase的目标是存储并处理大型的数据,更具体地说仅用普通的硬件配置,能够处理成千上万的行和列所组成的大型数据库。
  • HBase是一个开源的、分布式的、多版本的、面向列的存储模型。可以直接使用本地文件系统,也可使用Hadoop的HDFS文件存储系统。

为了提高数据的可靠性和系统的健壮性,并且发挥HBase处理大型数据的能力,还是使用HDFS作为文件存储系统更佳。

另外,HBase存储的是松散型数据,具体来说,HBase存储的数据介于映射(key/value)和关系型数据之间。如下图所示,HBase存储的数据从逻辑上看就是一张很大的表,并且它的数据列可以根据需要动态增加。每一个cell中的数据又可以有多个版本(通过时间戳来区别),从下图来看,HBase还具有 “ 向下提供存储,向上提供运算 “ 的特点。

逻辑结构

逻辑上,HBase的数据模型和关系型数据库类似,有表,有行列。但是从底层物理存储结构k-v来看,它更像一个多维度map。

20200423162536

物理存储结构

20200423162547

数据模型
Name Space

命名空间,类似于关系型数据库的 DatabBase 概念,每个命名空间下有多个表。HBase有两个自带的命名空间,分别是 hbase 和 default,hbase 中存放的是 HBase 内置的表,default 表是用户默认使用的命名空间。

Region

类似于关系型数据库的表概念。不同的是,HBase 定义表时只需要声明列族即可,不需要声明具体的列。往 HBase 写入数据时,字段可以动态、按需指定。因此,和关系型数据库相比,HBase 能够轻松应对字段变更的场景。

Row

HBase表中的每行数据都由一个 RowKey 和多个 Column(列)组成,数据是按照 RowKey的字典顺序存储的,并且查询数据时只能根据 RowKey 进行检索。

Column

HBase 中的每个列都由 Column Family(列族)和 Column Qualifier(列限定符)进行限定,例如 info:name,info:age。建表时,只需指明列族,而列限定符无需预先定义。

Time Stamp

用于标识数据的不同版本(version),每条数据写入时,如果不指定时间戳,系统会自动为其加上该字段,其值为写入 HBase 的时间。

Cell

由{rowkey, column Family:column Qualifier, time Stamp} 唯一确定的单元。cell 中的数据是没有类型的,全部是字节码形式存贮。

HBase体系架构

HBase的服务器体系结构遵从简单的主从服务器架构,它由HRegion Server群和HBase Master服务器构成。HBase Master负责管理所有的HRegion Server,而HBase中的所有RegionServer都是通过ZooKeeper来协调,并处理HBase服务器运行期间可能遇到的错误。

HBase Master Server本身并不存储HBase中的任何数据,HBase逻辑上的表可能会被划分成多个Region,然后存储到HRegion Server群中。HBase Master Server中存储的是从数据到HRegion Server的映射。HBase体系结构如下图所示:

20200423162558

Client

HBase Client使用HBase的RPC机制与HMaster和HRegionServer进行通信,对于管理类操作,Client与HMaster进行RPC;对于数据读写类操作,Client与HRegionServer进行RPC。

Zookeeper

Zookeeper Quorum中除了存储了ROOT表的地址和HMaster的地址,HRegionServer也会把自己以Ephemeral方式注册到 Zookeeper中,使得HMaster可以随时感知到各个HRegionServer的健康状态。此外,Zookeeper也避免了HMaster的单点问题。

HMaster
  • 管理用户对表的操作:create, delete, alter;
  • 管理HRegion Server的负载均衡,调整Region分布;
  • 在Region Split后,负责新Region的分配;
  • 在HRegion Server停机后,负责失效HRegion Server 上的Regions迁移。
    HDFS
    HDFS 为 HBase 提供最终的底层数据存储服务,同时为 HBase 提供高可用的支持。
    Region
    20200423162607

当表的大小超过设置值的时候,HBase会自动地将表划分为不同的区域,每个区域包含所有行的一个子集。对用户来说,每个表是一堆数据的集合,靠主键来区分。从物理上来说,一张表被拆分成了多块,每一块就是一个Region。一个Region会保存一个表里面某段连续的数据,从开始主键到结束主键,一张完整的表格是保存在多个Region上面。

HRegionServer

Region 的管理者,主要作用:对于数据的操作:get, put, delete;对于 Region 的操作:splitRegion、compactRegion。

20200423162617

  • 所有的数据库数据一般是保存在Hadoop HDFS分布式文件系统上面,用户通过一系列HRegion Server获取这些数据,一台机器上面一般只运行一个HRegion Server,且每一个区段的HRegion也只会被一个HRegion Server维护。
  • HRegion Server主要负责响应用户I/O请求,向HDFS文件系统中读写数据,是HBase中最核心的模块。
  • HRegion Server内部管理了一系列HRegion对象,每个HRegion对应了Table中的一个Region,Region中由多个Store组成。每个Store对应了Table中的一个Column Family的存储,可以看出每个Column Family其实就是一个集中的存储单元,因此最好将具备共同IO特性的column放在一个Column Family中,这样最高效。
    HStore​(StoreFile)
  • HBase存储的核心。由MemStore和StoreFile组成。。
  • MemStore是Sorted Memory Buffer

Client写入 -> 存入MemStore,一直到MemStore满 -> Flush成一个StoreFile,直至增长到一定阈值 -> 触发Compact合并操作 -> 多个StoreFile合并成一个StoreFile,同时进行版本合并和数据删除 -> 当StoreFiles Compact后,逐步形成越来越大的StoreFile -> 单个StoreFile大小超过一定阈值后,触发Split操作,把当前Region Split成2个Region,Region会下线,新Split出的2个孩子Region会被HMaster分配到相应的HRegionServer上,使得原先1个Region的压力得以分流到2个Region上。

20200423162625

所以,HBase只是增加数据,有所得更新和删除操作,都是在Compact阶段做的,所以,用户写操作只需要进入到内存即可立即返回,从而保证I/O高性能。

  • HFile是底层的实现。
    MemStore
    写缓存,由于 HFile 中的数据要求是有序的,所以数据是先存储在 MemStore 中,排好序后,等到达刷写时机才会刷写到 HFile,每次刷写都会形成一个新的 HFile。
    Hlog
    在分布式系统环境中,无法避免系统出错或者宕机,因此一旦HRegion Server意外退出,MemStore中的内存数据将会丢失,这就需要引入HLog了。每个HRegion Server中都有一个HLog对象。

每个HRegionServer中都会有一个HLog对象,HLog是一个实现Write Ahead Log的类,每次用户操作写入Memstore的同时,也会写一份数据到HLog文件,HLog文件定期会滚动出新,并删除旧的文件(已持久化到StoreFile中的数据)。当HRegionServer意外终止后,HMaster会通过Zookeeper感知,HMaster首先处理遗留的HLog文件,将不同region的log数据拆分,分别放到相应region目录下,然后再将失效的region重新分配,领取到这些region的HRegionServer在Load Region的过程中,会发现有历史HLog需要处理,因此会Replay HLog中的数据到MemStore中,然后flush到StoreFiles,完成数据恢复。

Hbase的存储格式

HBase中的所有数据文件都存储在Hadoop HDFS文件系统上为例,主要包括上述提出的两种文件类型:

1
2
HFile, HBase中KeyValue数据的存储格式,HFile是Hadoop的二进制格式文件,实际上StoreFile就是对HFile做了轻量级包装,即StoreFile底层就是HFile。
HLog File,HBase中WAL(Write Ahead Log) 的存储格式,物理上是Hadoop的Sequence File。
  • Hbase的存储格式(HFile)*

20200423162635

1
2
3
4
5
6
7
8
9
HFile文件不定长,长度固定的块只有两个:Trailer和FileInfo
Trailer中指针指向其他数据块的起始点
File Info中记录了文件的一些Meta信息,例如:AVG_KEY_LEN, AVG_VALUE_LEN, LAST_KEY, COMPARATOR, MAX_SEQ_ID_KEY等
Data Index和Meta Index块记录了每个Data块和Meta块的起始点
Data Block是HBase I/O的基本单元,为了提高效率,HRegionServer中有基于LRU的Block Cache机制
每个Data块的大小可以在创建一个Table的时候通过参数指定,大号的Block有利于顺序Scan,小号Block利于随机查询
每个Data块除了开头的Magic以外就是一个个KeyValue对拼接而成, Magic内容就是一些随机数字,目的是防止数据损坏

HFile里面的每个KeyValue对就是一个简单的byte数组。这个byte数组里面包含了很多项,并且有固定的结构。

20200423162645

1
2
3
4
5
KeyLength和ValueLength:两个固定的长度,分别代表Key和Value的长度
Key部分:Row Length是固定长度的数值,表示RowKey的长度,Row 就是RowKey
Column Family Length是固定长度的数值,表示Family的长度
接着就是Column Family,再接着是Qualifier,然后是两个固定长度的数值,表示Time Stamp和Key Type(Put/Delete)
Value部分没有这么复杂的结构,就是纯粹的二进制数据
  • Hbase的存储格式(HLog File)*

20200423162657

  HLog文件就是一个普通的Hadoop Sequence File,Sequence File 的Key是HLogKey对象,HLogKey中记录了写入数据的归属信息,除了table和region名字外,同时还包括 sequence number和timestamp,timestamp是“写入时间”,sequence number的起始值为0,或者是最近一次存入文件系统中sequence number。

  HLog Sequece File的Value是HBase的KeyValue对象,即对应HFile中的KeyValue

ROOT表和META表

用户表的Regions元数据被存储在.META.表中,随着Region的增多,.META.表中的数据也会增大,并分裂成多个Regions。为了定位.META.表中各个Regions的位置,把.META.表中所有Regions的元数据保存在-ROOT-表中,最后由ZooKeeper记录-ROOT-表的位置信息。所有客户端访问用户数据前,需要首先访问ZooKeeper获得-ROOT-的位置,然后访问-ROOT-表获得.META.表的位置,最后根据.META.表中的信息确定用户数据存放的位置,如下图所示。

20200423162706

ROOT 表永远不会被分割,它只有一个Region,这样可以保证最多需要三次跳转就可以定位任意一个Region。为了加快访问速度,.META.表的Regions全部保存在内存中。客户端会将查询过的位置信息缓存起来,且缓存不会主动失效。如果客户端根据缓存信息还访问不到数据,则询问只有相关.META.表的Region服务器,试图获取数据的位置,如果还是失败,则询问-ROOT-表相关的.META.表在哪里。最后,如果前面的信息全部失效,则通过ZooKeeper重新定位Region的信息。所以如果客户端上的缓存全部是失效,则需要进行6次网络来回,才能定位到正确的Region。

20200423162716

MapReduce On HBase

在HBase系统上运行批处理运算,最方便和实用的模型依然是MapReduce,如下图:

20200423162725

HBase Table和Region的关系,比较类似HDFS File和Block的关系,HBase提供了配套的TableInputFormat和TableOutputFormat API,可以方便的将HBase Table作为Hadoop MapReduce的Source和Sink,对于MapReduce Job应用开发人员来说,基本不需要关注HBase系统自身的细节。

写流程

20200423162734

1
2
3
4
5
6
7
1)Client 先访问 zookeeper,获取 hbase:meta 表位于哪个 Region Server。 
2)访问对应的 Region Server,获取 hbase:meta 表,根据读请求的 namespace:table/rowkey,查询出目标数据位于哪个 Region Server 中的哪个 Region 中。并将该 table 的 region 信息以及 meta 表的位置信息缓存在客户端的 meta cache,方便下次访问。
3)与目标 Region Server 进行通讯;
4)将数据顺序写入(追加)到 WAL;
5)将数据写入对应的 MemStore,数据会在 MemStore 进行排序;
6)向客户端发送 ack;
7)等达到 MemStore 的刷写时机后,将数据刷写到 HFile。
MemStore Flush

20200423162743

MemStore 刷写时机:

1.当某个 memstroe 的大小达到了 hbase.hregion.memstore.flush.size(默认值 128M), 其所在 region 的所有 memstore 都会刷写。

** 当 memstore 的大小达到了hbase.hregion.memstore.flush.size(默认值 128M) ✖ hbase.hregion.memstore.block.multiplier(默认值 4)时 **,会阻止继续往该 memstore 写数据。

2.当 region server 中 memstore 的总大小达到** java_heapsize✖hbase.regionserver.global.memstore.size(默认值 0.4)✖hbase.regionserver.global.memstore.size.lower.limit(默认值 0.95) **,region 会按照其所有 memstore 的大小顺序(由大到小)依次进行刷写。

直到 region server中所有 memstore 的总大小减小到上述值以下。 当 region server 中 memstore 的总大小达到java_heapsize✖hbase.regionserver.global.memstore.size(默认值 0.4)时,会阻止继续往所有的 memstore 写数据。

  1. 到达自动刷写的时间,也会触发 memstore flush。自动刷新的时间间隔由该属性进行配置 hbase.regionserver.optionalcacheflushinterval(默认 1 小时)。

4.当 WAL 文件的数量超过 hbase.regionserver.max.logs,region 会按照时间顺序依次进行刷写,直到 WAL 文件数量减小到 hbase.regionserver.max.log 以下(该属性名已经废弃,现无需手动设置,最大值为 32)。

读流程

20200423162755

1
2
3
4
5
1)Client 先访问 zookeeper,获取 hbase:meta 表位于哪个 Region Server。
2)访问对应的 Region Server,获取 hbase:meta 表,根据读请求的 namespace:table/rowkey,查询出目标数据位于哪个 Region Server 中的哪个 Region 中。并将该 table 的 region 信息以及 meta 表的位置信息缓存在客户端的 meta cache,方便下次访问。
3)与目标 Region Server 进行通讯;
4)分别在 Block Cache(读缓存),MemStore 和 Store File(HFile)中查询目标数据,并将查到的所有数据进行合并。此处所有数据是指同一条数据的不同版本(time stamp)或者不同的类型(Put/Delete)。
5) 将从文件中查询到的数据块(Block,HFile 数据存储单元,默认大小为 64KB)缓存到Block Cache。 6)将合并后的最终结果返回给客户端。
StoreFile Compaction

由于memstore每次刷写都会生成一个新的HFile,且同一个字段的不同版本(timestamp)和不同类型(Put/Delete)有可能会分布在不同的 HFile 中,因此查询时需要遍历所有的 HFile。为了减少 HFile 的个数,以及清理掉过期和删除的数据,会进行 StoreFile Compaction。

Compaction 分为两种,分别是 Minor Compaction 和 Major Compaction。Minor Compaction会将临近的若干个较小的 HFile 合并成一个较大的 HFile,但不会清理过期和删除的数据。

Major Compaction 会将一个 Store 下的所有的 HFile 合并成一个大 HFile,并且会清理掉过期和删除的数据

20200423162804

Region Split

默认情况下,每个 Table 起初只有一个 Region,随着数据的不断写入,Region 会自动进行拆分。刚拆分时,两个子 Region 都位于当前的 Region Server,但处于负载均衡的考虑,HMaster 有可能会将某个 Region 转移给其他的 Region Server。

Region Split 时机:

1
2
1.当1个region中的某个Store下所有StoreFile的总大小超过hbase.hregion.max.filesize, 该 Region 就会进行拆分(0.94 版本之前)。
2. 当 1 个 region 中 的 某 个 Store 下所有 StoreFile 的 总 大 小 超 过 Min(R^2 * "hbase.hregion.memstore.flush.size",hbase.hregion.max.filesize"),该 Region 就会进行拆分,其中 R 为当前 Region Server 中属于该 Table 的个数(0.94 版本之后)。

20200423162817

安装

1、docker安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
docker run -d -h docker-hbase \
-p 2181:2181 \
-p 8081:8080 \
-p 8085:8085 \
-p 9090:9090 \
-p 9000:9000 \
-p 9095:9095 \
-p 16000:16000 \
-p 16010:16010 \
-p 16201:16201 \
-p 16301:16301 \
-p 16020:16020\
--name hbase \
harisekhon/hbase

-d 表示后台运行
-h 该容器的host为docker-hbase
-p 宿主机端口:容器端口
--name 该容器的名字

启动成功,访问web界面。

20200423162835

进入容器可以看到hbase正常

1
2
3
4
5
6
7
8
[root@VM_0_3_centos ~]# docker exec -it 6a /bin/bash
bash-4.4# hbase shell
2020-01-08 10:13:42,496 WARN [main] util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
HBase Shell
Use "help" to get list of supported commands.
Use "exit" to quit this interactive shell.
For Reference, please visit: http://hbase.apache.org/2.0/book.html#shell
Version 2.1.3, rda5ec9e4c06c537213883cca8f3cc9a7c19daf67, Mon Feb 11 15:45:33 CST 2019

2、集群搭建

3、常用操作命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
查看集群状态status

查看hbase版本version

查看所有表list

创建表 create 't1','[Column Family 1]','[Column Family 2]'

例如创建一个菜谱表,调味料为一个列族(类),食材则为另一个列族(类),脚本为create 'cookbook','seasoning','ngredients'

查看表的结构describe 't1'
删除表
disable 't1' 之后 drop 't1'
检查表是否存在exists 't1'
获取表中所有数据scan 't1'
-获取表中前10行数据scan 't1',{LIMIT=>10}
获取指定column(ad:pv)的前10行数据scan 't1',{COLUMNS=>['ad:pv'],LIMIT=>10}
加Filter:如过滤rowkey以“2015”开头的前10行数据 scan 't1',{FILTER=>"PrefixFilter('2015')",LIMIT=>10}
查询rowkey=001下所有的数据get 't1','001'
查询rowkey=001下family=ad,column=pvget 't1','001','ad:pv' 或者 get 't1','001',{COLUMN=>'ad:pv'}
添加数据put <table>,<rowkey>,<family:column>,<value>,<timestamp>
例如:向’t1’表中添加rowkey=002,family=ad,column=pv,value=1000,时间戳默认
put 't1','002','ad:pv','1000'

删除rowkey=002的所有数据deleteall 't1','002'
删除rowkey=002中ad:pv数据delete 't1','002','ad:pv'
清空表truncate 't1'
统计行数count 't1'
查询表t1中的行数,每100条显示一次count 't1',{INTERVAL=>100}

附录:Hbase默认端口

20200423162851

Hadoop安装和配置及使用介绍

发表于 2019-12-23 | 分类于 Hadoop

由于Hadoop是基于Java开发,所以首选在Linux上安装好Java环境。

1、Hadoop的下载安装

下载包到/usr/local

解压:tar -zxvf /usr/local/hadoop-3.1.2.tar.gz -C /usr/local

添加环境变量vim /etc/profile

1
2
3
4
export HADOOP_HOME=/usr/local/hadoop-3.1.2
export HADOOP_COMMON_LIB_NATIVE_DIR=$HADOOP_HOME/lib/native
export PATH=$PATH:$HADOOP_HOME/sbin
export PATH=$PATH:$HADOOP_HOME/bin

source /etc/profile

执行测试:hadoop version

2、目录结构

1
2
3
4
5
bin目录:存放对Hadoop相关服务(HDFS,YARN)进行操作的脚本
etc目录:Hadoop的配置文件目录,存放Hadoop的配置文件
lib目录:存放Hadoop的本地库(对数据进行压缩解压缩功能)
sbin目录:存放启动或停止Hadoop相关服务的脚本
share目录:存放Hadoop的依赖jar包、文档、和官方案例

Hadoop运行模式包括:本地模式、伪分布式模式以及完全分布式模式。

3、本地运行模式(单机模式)

3.1、官方Grep案例
1
2
3
4
5
6
7
8
9
创建在hadoop-2.7.2文件下面创建一个input文件夹
[root@VM_0_3_centos hadoop-3.1.2]# mkdir input
将Hadoop的xml配置文件复制到input
[root@VM_0_3_centos hadoop-3.1.2]# cp etc/hadoop/*.xml input
执行share目录下的MapReduce程序(统计符合后面正则式的词)
[root@VM_0_3_centos hadoop-3.1.2]# bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.2.jar grep input output 'dfs[a-z.]+'
查看输出结果
[root@VM_0_3_centos hadoop-3.1.2]# cat output/*
1 dfsadmin
3.2、官方WordCount案例 *

统计单词数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
建立输入目录和文件,并且写入测试内容
[root@VM_0_3_centos hadoop-3.1.2]# mkdir wcinput
[root@VM_0_3_centos hadoop-3.1.2]# cd wcinput
[root@VM_0_3_centos wcinput]# vim wc.input
[root@VM_0_3_centos wcinput]# cat wc.input
hadoop yarn
hadoop mapreduce
huyun
huyun

执行统计程序
[root@VM_0_3_centos hadoop-3.1.2]# hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.2.jar wordcount wcinput wcoutput
查看结果
[root@VM_0_3_centos hadoop-3.1.2]# cat wcoutput/*
hadoop 2
huyun 2
mapreduce 1
yarn 1

3、伪分布式运行模式

3.1、启动HDFS并运行MapReduce程序

** 配置集群 **
配置:hadoop-env.sh

1
2
vim etc/hadoop/hadoop-env.sh
修改JAVA_HOME 路径:export JAVA_HOME=

配置:core-site.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@VM_0_3_centos hadoop-3.1.2]# vim etc/hadoop/core-site.xml

<configuration>
<!-- 指定HDFS中NameNode的地址 -->
<property>
<name>fs.defaultFS</name>
<value>hdfs://localhost:9000</value>
</property>

<!-- 指定Hadoop运行时产生文件的存储目录 -->
<property>
<name>hadoop.tmp.dir</name>
<value>/usr/local/hadoop-3.1.2/data/tmp</value>
</property>


</configuration>

HDFS core-site.xml 参数配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
– fs.default.name
– 文件系统的名字。通常是NameNode的hostname与port
– 需要在每一个需要访问集群的机器上指定,包括集群中的节点
– 例如:hdfs://<your_namenode>:9000/

– fs.checkpoint.dir
– 以逗号分隔的文件夹列表,SecondNameNode用来存储checkpoint image文件
– 如果多于一个文件夹,那么都会被写入数据
– 需要在SecondNameNode上设定
– 默认值:${hadoop.tmp.dir}/dfs/namesecondary

– hadoop.tmp.dir
– HDFS与本地磁盘的临时文件
默认是/tmp/hadoop-${user.name}.需要在所有的节点中设定

– fs.trash.interval
– 当一个文件被删掉后,它会被放到用户目录的.Trash目录下,而不是立即删掉
– 经过此参数设置的分钟数之后,再删掉数据
– 默认是0,禁用此功能,建议1440(一天)

– io.file.buffer.size
– 设定在读写数据时的缓存大小,应该为硬件分页大小的2倍
– 默认是4096,建议为65536 ( 64K)

– hadoop.logfile.size
– hadoop.logfile.count
– 设置log文件的大小和数量

配置:hdfs-site.xml

1
2
3
4
5
6
7
vim etc/hadoop/hdfs-site.xml

<!-- 指定HDFS副本的数量 -->
<property>
<name>dfs.replication</name>
<value>1</value>
</property>

HDFS hdfs-site.xml 参数配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
– dfs.name.dir
– NameNode 元数据存放位置
– 默认值:使用core-site.xml中的hadoop.tmp.dir/dfs/name

– dfs.block.size
– 对于新文件切分的大小,单位byte。默认是64M,建议是128M。每一个节点都要指定,包括客户端。
– 默认值:67108864

– dfs.data.dir
– DataNode在本地磁盘存放block的位置,可以是以逗号分隔的目录列表,DataNode循环向磁盘中写入数据,每个DataNode可单独指定与其它DataNode不一样
– 默认值:${hadoop.tmp.dir}/dfs/data

– dfs.namenode.handler.count
– NameNode用来处理来自DataNode的RPC请求的线程数量
– 建议设置为DataNode数量的10%,一般在10~200个之间
– 如设置太小,DataNode在传输数据的时候日志中会报告“connecton refused"信息
– 在NameNode上设定
– 默认值:10

– dfs.datanode.handler.count
– DataNode用来连接NameNode的RPC请求的线程数量
– 取决于系统的繁忙程度
– 设置太小会导致性能下降甚至报错
– 在DataNode上设定
– 默认值:3

– dfs.datanode.max.xcievers
– DataNode可以同时处理的数据传输连接数
– 默认值:256
– 建议值:4096

– dfs.permissions
– 如果是true则检查权限,否则不检查(每一个人都可以存取文件)
– 于NameNode上设定
– 默认值:true

– dfs.datanode.du.reserved
– 在每个卷上面HDFS不能使用的空间大小
– 在每个DataNode上面设定
– 默认值:0
– 建议为10737418240,即10G。需要结合MapReduce场景设置。

– dfs.datanode.failed.volumes.tolerated
– DataNode可以容忍损块的磁盘数量,超过这个数量DataNode将会离线,所有在这个节点上面的block将会被重新复制
– 默认是0,但是在有多块磁盘的时候一般会增大这个值

– dfs.replication
– 在文件被写入的时候,每一块将要被复制多少份
– 默认是3份。建议3份
– 在客户端上设定

通常也需要在DataNode上设定

** 启动集群 **

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
格式化NameNode(第一次启动时格式化)
注意:格式化NameNode,会产生新的集群id,导致NameNode和DataNode的集群id不一致,集群找不到已往数据。所以,格式NameNode时,一定要先删除data数据和log日志,然后再格式化NameNode。
[root@VM_0_3_centos hadoop-3.1.2]# bin/hdfs namenode -format

启动NameNode
[root@VM_0_3_centos hadoop-3.1.2]# sbin/hadoop-daemon.sh start namenode
WARNING: Use of this script to start HDFS daemons is deprecated.
WARNING: Attempting to execute replacement "hdfs --daemon start" instead.

启动DataNode
[atguigu@hadoop101 hadoop-2.7.2]$ sbin/hadoop-daemon.sh start datanode

查看java进程
[root@VM_0_3_centos hadoop-3.1.2]# jps
10678 Jps
10599 DataNode
10044 NameNode

新版本可以用 sbin/start-dfs.sh启动,但是报错,
Starting namenodes on [localhost]
ERROR: Attempting to operate on hdfs namenode as root
解决方式:
$ vim sbin/start-dfs.sh
$ vim sbin/stop-dfs.sh
最前面增加以下内容
HDFS_DATANODE_USER=root
HADOOP_SECURE_DN_USER=hdfs
HDFS_NAMENODE_USER=root
HDFS_SECONDARYNAMENODE_USER=root

$ vim sbin/start-yarn.sh
$ vim sbin/stop-yarn.sh
最前面增加以下内容
YARN_RESOURCEMANAGER_USER=root
HADOOP_SECURE_DN_USER=yarn
YARN_NODEMANAGER_USER=root

web端查看HDFS文件系统:http://152.*****:9870/dfshealth.html#tab-overview 老版本端口是50070

查看Log日志 /usr/local/hadoop-3.1.2/logs

** 操作集群 **
在HDFS文件系统上创建一个input文件夹

1
2
[root@VM_0_3_centos hadoop-3.1.2]# bin/hdfs dfs -mkdir -p /user/root/input
2020-01-17 17:29:48,425 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable

将测试文件内容上传到文件系统上

1
2
[root@VM_0_3_centos hadoop-3.1.2]# bin/hdfs dfs -put wcinput/wc.input /user/root/input/
2020-01-17 17:31:20,430 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable

查看上传的文件是否正确

1
2
3
4
[root@VM_0_3_centos hadoop-3.1.2]# bin/hdfs dfs -ls  /user/root/input/
2020-01-17 17:32:25,279 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
Found 1 items
-rw-r--r-- 1 root supergroup 43 2020-01-17 17:31 /user/root/input/wc.input
1
2
3
4
5
6
7
[root@VM_0_3_centos hadoop-3.1.2]# bin/hdfs dfs -cat  /user/root/ input/wc.input
2020-01-17 17:32:37,910 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
cat: `/user/root': Is a directory
hadoop yarn
hadoop mapreduce
huyun
huyun

运行MapReduce程序

1
[root@VM_0_3_centos hadoop-3.1.2]# bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.2.jar wordcount /user/root/input/ /user/root/output

查看输出结果

1
2
3
4
5
6
[root@VM_0_3_centos hadoop-3.1.2]# bin/hdfs dfs -cat /user/root/output/*
2020-01-17 17:34:03,420 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
hadoop 2
huyun 2
mapreduce 1
yarn 1

将测试文件内容下载到本地

1
2
3
4
[root@VM_0_3_centos hadoop-3.1.2]# hdfs dfs -get /user/root/output/* ./wcoutput/
2020-01-17 17:36:40,621 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
get: `./wcoutput/_SUCCESS': File exists
get: `./wcoutput/part-r-00000': File exists

本地文件存在,先删除在下载

1
2
3
[root@VM_0_3_centos hadoop-3.1.2]# rm -rf ./wcoutput/*
[root@VM_0_3_centos hadoop-3.1.2]# hdfs dfs -get /user/root/output/* ./wcoutput/
2020-01-17 17:37:20,030 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable

查看本地结果

1
2
3
4
5
[root@VM_0_3_centos hadoop-3.1.2]# cat ./wcoutput/*
hadoop 2
huyun 2
mapreduce 1
yarn 1

删除输出结果

1
hdfs dfs -rm -r /user/root/output
3.2、启动YARN并运行MapReduce程序

配置集群
配置环境

1
2
3
4
[root@VM_0_3_centos hadoop-3.1.2]# vim etc/hadoop/yarn-env.sh 
[root@VM_0_3_centos hadoop]# vim mapred-env.sh

export JAVA_HOME=/usr/local/java8/jdk1.8.0_144

配置yarn-site.xml

1
2
3
4
5
6
7
8
9
10
11
<!-- Reducer获取数据的方式 -->
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>

<!-- 指定YARN的ResourceManager的地址 -->
<property>
<name>yarn.resourcemanager.hostname</name>
<value>VM_0_3_centos</value>
</property>

配置:mapred-site.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@VM_0_3_centos hadoop]# vim mapred-site.xml 

<!-- 指定MR运行在YARN上 -->
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
<property>
<name>yarn.app.mapreduce.am.env</name>
<value>HADOOP_MAPRED_HOME=/usr/local/hadoop-3.1.2</value>
</property>
<property>
<name>mapreduce.map.env</name>
<value>HADOOP_MAPRED_HOME=/usr/local/hadoop-3.1.2</value>
</property>
<property>
<name>mapreduce.reduce.env</name>
<value>HADOOP_MAPRED_HOME=/usr/local/hadoop-3.1.2</value>
</property>

<property>
<name>mapreduce.application.classpath</name>
<value>/usr/local/hadoop-3.1.2/share/hadoop/mapreduce/*,/usr/local/hadoop-3.1.2/share/hadoop/mapreduce/lib/*</value>
</property>

启动集群
启动前必须保证NameNode和DataNode已经启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@VM_0_3_centos hadoop-3.1.2]# sbin/yarn-daemon.sh start resourcemanager
WARNING: Use of this script to start YARN daemons is deprecated.
WARNING: Attempting to execute replacement "yarn --daemon start" instead.

[root@VM_0_3_centos hadoop-3.1.2]# sbin/yarn-daemon.sh start nodemanager
WARNING: Use of this script to start YARN daemons is deprecated.
WARNING: Attempting to execute replacement "yarn --daemon start" instead.

[root@VM_0_3_centos hadoop-3.1.2]# jps
21680 DataNode
13315 ResourceManager
13831 Jps
13706 NodeManager
21482 NameNode

集群操作
YARN的浏览器查看:http://152.....56:8088/cluster

20200423162313

删除文件系统上的output文件

1
[root@VM_0_3_centos hadoop-3.1.2]# bin/hdfs dfs -rm -R /user/root/output

执行MapReduce程序

1
bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.2.jar wordcount /user/root/input/ /user/root/output

查看运行结果可以通过浏览器查看。

1
2


3.3、配置历史服务器

为了查看程序的历史运行情况,需要配置一下历史服务器。

配置mapred-site.xml

1
2
3
4
5
6
7
8
9
10
11
12
vim mapred-site.xml

<!-- 历史服务器端地址 -->
<property>
<name>mapreduce.jobhistory.address</name>
<value>VM_0_3_centos:10020</value>
</property>
<!-- 历史服务器web端地址 -->
<property>
<name>mapreduce.jobhistory.webapp.address</name>
<value>VM_0_3_centos:19888</value>
</property>

启动历史服务器

sbin/mr-jobhistory-daemon.sh start historyserver

查看JobHistory

http://IP:19888/jobhistory

3.4、配置日志的聚集

应用运行完成以后,将程序运行日志信息上传到HDFS系统上。方便的查看到程序运行详情,方便开发调试。

注意:开启日志聚集功能,需要重新启动NodeManager 、ResourceManager和HistoryManager。

配置yarn-site.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
[atguigu@hadoop101 hadoop]$ vi yarn-site.xml

<!-- 日志聚集功能使能 -->
<property>
<name>yarn.log-aggregation-enable</name>
<value>true</value>
</property>

<!-- 日志保留时间设置7天 -->
<property>
<name>yarn.log-aggregation.retain-seconds</name>
<value>604800</value>
</property>

重启NodeManager 、ResourceManager和HistoryManager

1
2
3
4
5
6
7
[root@VM_0_3_centos hadoop-3.1.2]# sbin/yarn-daemon.sh stop resourcemanager
[root@VM_0_3_centos hadoop-3.1.2]# sbin/yarn-daemon.sh stop nodemanager
[root@VM_0_3_centos hadoop-3.1.2]# sbin/mr-jobhistory-daemon.sh stop historyserver

[root@VM_0_3_centos hadoop-3.1.2]# sbin/yarn-daemon.sh start resourcemanager
[root@VM_0_3_centos hadoop-3.1.2]# sbin/yarn-daemon.sh start nodemanager
[root@VM_0_3_centos hadoop-3.1.2]# sbin/mr-jobhistory-daemon.sh start historyserver

删除HDFS上已经存在的输出文件

1
[root@VM_0_3_centos hadoop-3.1.2]# bin/hdfs dfs -rm -R /user/root/output

执行WordCount程序

1
[root@VM_0_3_centos hadoop-3.1.2]# hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.2.jar wordcount /user/root/input /user/root/output

查看日志

http://IP:19888/jobhistory

3.5、配置文件说明

Hadoop配置文件分两类:默认配置文件和自定义配置文件,只有用户想修改某一默认配置值时,才需要修改自定义配置文件,更改相应属性值。

默认配置文件:

1
2
3
4
5
默认文件	                      在Hadoop的jar包中的位置
[core-default.xml] hadoop-common-2.7.2.jar/ core-default.xml
[hdfs-default.xml] hadoop-hdfs-2.7.2.jar/ hdfs-default.xml
[yarn-default.xml] hadoop-yarn-common-2.7.2.jar/ yarn-default.xml
[mapred-default.xml] hadoop-mapreduce-client-core-2.7.2.jar/ mapred-default.xml

自定义配置文件:core-site.xml、hdfs-site.xml、yarn-site.xml、mapred-site.xml

4、完全分布式运行模式

centos 设置systemctl自启服务文件

发表于 2019-12-23 | 分类于 linux

方式一

建立服务文件

vim /usr/lib/systemd/system/mongodb.service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Unit] 

Description=mongodb
After=network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
ExecStart=/usr/local/mongodb/bin/mongod -config /usr/local/mongodb/bin/mongodb.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/usr/local/mongodb/bin/mongod --shutdown -config /usr/local/mongodb/bin/mongodb.conf
PrivateTmp=true

[Install]
WantedBy=multi-user.target

文件内容解释

Description:描述服务

After:描述服务类别

[Service]服务运行参数的设置

Type=forking是后台运行的形式

ExecStart为服务的具体运行命令

ExecReload为重启命令

ExecStop为停止命令

PrivateTmp=True表示给服务分配独立的临时空间

注意:启动、重启、停止命令全部要求使用绝对路径

[Install]服务安装的相关设置,可设置为多用户

设置开机自启动

任意目录下执行

systemctl enable xxx.service

其他命令

启动服务

systemctl start xxxx.service

设置开机自启动

systemctl enable xxxx.service

停止开机自启动

systemctl disable xxxx.service

查看服务当前状态

systemctl status xxx.service

重新启动服务

systemctl restart xxx.service

查看所有已启动的服务

systemctl list-units –type=service

CentOS7.0中systemctl启动关闭服务的用法

systemctl是主要的工具,它融合之前service和chkconfig的功能于一体。可以使用它永久性或只在当前会话中启用/禁用服务。
systemctl可以列出正在运行的服务状态
systemd-cgls以树形列出正在运行的进程,它可以递归显示控制组内容。

1
2
3
4
5
6
7
8
9
10
11
启动/关闭、启用/禁用服务:
启动一个服务:systemctl start postfix.service
关闭一个服务:systemctl stop postfix.service
重启一个服务:systemctl restart postfix.service
显示一个服务的状态:systemctl status postfix.service

在开机时启用一个服务:systemctl enable postfix.service
在开机时禁用一个服务:systemctl disable postfix.service
查看服务是否开机启动:systemctl is-enabled postfix.service;echo $?

查看已启动的服务列表:systemctl list-unit-files|grep enabled

说明:启用服务就是在当前“runlevel”的配置文件目录/etc/systemd/system/multi-user.target.wants/里,建立/usr/lib/systemd/system里面对应服务配置文件的软链接;禁用服务就是删除此软链接

方式二

先在/etc/rc.d/init.d下新建文件 mongod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/bin/sh
#
#chkconfig: 2345 80 90
#description: mongodb

if test -f /sys/kernel/mm/transparent_hugepage/enabled; then
echo never > /sys/kernel/mm/transparent_hugepage/enabled
fi
if test -f /sys/kernel/mm/transparent_hugepage/defrag; then
echo never > /sys/kernel/mm/transparent_hugepage/defrag
fi

start() {
/usr/local/mongodb/bin/mongod -config /usr/local/mongodb/bin/mongodb.conf
}

stop() {
/usr/local/mongodb/bin/mongod -config /usr/local/mongodb/bin/mongodb.conf --shutdown
}

case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
*)
echo $"Usage: $0 {start|stop|restart}"
exit 1
esac

授权 等操作:

chmod +x /etc/rc.d/init.d/mongod

chkconfig –add mongod

chkconfig –level 345 mongod on

chkconfig –list mongod

service mongod start

之后就可以开始使用service mongod start|stop|restart 管理mongodb服务了。

Hexo搭建

发表于 2019-12-23 | 分类于 hexo

Hexo搭建

解决重定向问题:
npm i hexo-generator-cname –save

NodeJS内存不足 node –max-old-space-size=8192 –optimize-for-size –max-executable-size=8192 –max_old_space_size=8192 –optimize_for_size –max_executable_size=8192 node_modules/karma/bin/karma start –single-run –max_new_space_size=8192 –prod –aot

Redis 管道技术

发表于 2019-09-02 | 分类于 redis
管道

Redis是一种基于客户端-服务端模型以及请求/响应协议的TCP服务。这意味着通常情况下一个请求会遵循以下步骤:

  • 客户端向服务端发送一个查询请求,并监听Socket返回,通常是以阻塞模式,等待服务端响应。
  • 服务端处理命令,并将结果返回给客户端。

Redis 管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应。

管道技术最显著的优势是提高了 redis 服务的性能。

pipeline的思想

1
2
3
如果client执行一些相互之间无关的命令或者不需要获取命令的返回值,那么redis允许你连续发送多条命令,而不需要等待前面命令执行完毕。
比如我们执行3条INCR命令,如果使用管道,理论上只需要一个RTT+3条命令的执行时间即可,如果不适用管道,那么可能需要额外的两个RTT时间。
管道相当于批处理脚本,相当于是命令集。

Redis的管道可以在大量数据需要一次性操作完成的时候,使用Pipeline进行批处理,将一大队的操作合并成一次操作,可以减少链路层的时间消耗。

1
2
3
4
5
6
7
8
Pipeline pipe = jedis.pipelined(); // 先创建一个pipeline的链接对象
long start_pipe = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
pipe.set(String.valueOf(i), String.valueOf(i));
}
pipe.sync(); // 获取所有的response
long end_pipe = System.currentTimeMillis();
logger.info("the pipe total time is:" + (end_pipe - start_pipe));
SCAN、SSCAN、HSCAN、ZSCAN

SCAN、SSCAN、HSCAN、ZSCAN 4 个命令,分别用于集合、哈希键及有序集等。

1
2
3
4
SCAN:命令用于迭代当前数据库中的数据库键。
SSCAN:命令用于迭代集合键中的元素。
HSCAN:命令用于迭代哈希键中的键值对。
ZSCAN:命令用于迭代有序集合中的元素(包括元素成员和元素分值)。

SCAN cursor [MATCH pattern] [COUNT count]

当需要模糊查询或者大批量时候,keys * 进行查询 key 的时候会进行堵塞,导致 redis 整体不可用,因为redis是单线程,而使用 scan 命令则不会。

scan 游标 MATCH <返回和给定模式相匹配的元素> count 每次迭代所返回的元素数量 SCAN 命令是增量的循环,每次调用只会返回一小部分的元素。

在 Redis 中的具体用法如下:

1
2
scan 0 match xttblog.com* count 5
sscan myset 0 match 胡*

SCAN 命令对应的 Jedis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Jedis jedis = pool.getResource();
ScanParams scanParams = new ScanParams();
List<String> list = new ArrayList<>();
scanParams.match(key);
scanParams.count(100);
//当 SCAN 命令的游标参数被设置为 0 时, 服务器将开始一次新的迭代, 而当服务器向用户返回值为 0 的游标时, 表示迭代已结束。
String cursor = ScanParams.SCAN_POINTER_START;
while (true) {
ScanResult<String> scan = jedis.scan(cursor, scanParams);
List<String> elements = scan.getResult();
if (CollectionUtils.isNotEmpty(elements)) {
list.addAll(elements);
}
String cursor = scan.getStringCursor();
log.info("scan:返回用于下次遍历的游标 " + cursor);
log.info("scan:返回结果 " + elements);
if (ScanParams.SCAN_POINTER_START.equals(cursor)) {
break;
}
}
jedis.close();

其他操作类似。https://www.xttblog.com/?p=3635

注意:SCAN 命令不能保证每次返回的值都是有序的,同一个 key 有可能返回多次,不做区分,需要应用程序去处理。

一个批量删除的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
//key有模糊匹配*,查找到对应的去删除,启动一个线程去查找。通过游标的方式,用管道技术操作。
private Long executeDel(Jedis jedis, String key) throws Exception {
Long count = 0L;
CountDownLatch countDownLatch = new CountDownLatch(0);
Future[] futureArray = new Future[1];
ScanParams scanParams = new ScanParams().count(1);
scanParams.match(key);
ExecutorService executor = Executors.newSingleThreadExecutor();
futureArray[0] = executor.submit(new JedisDeleteThread(jedis, scanParams, countDownLatch));
try {
//等待线程汇总
countDownLatch.await();
try {
if (null != futureArray && futureArray.length > 0) {
for (int i = 0; i < futureArray.length; i++) {
count += (long) futureArray[i].get();
}
}
} catch (InterruptedException e) {
log.error(e.getMessage());
} catch (ExecutionException e) {
log.error(e.getMessage());
}
} catch (InterruptedException e) {
log.error(e.getMessage());
}
return count;
}

//线程 执行查找和删除处理
class JedisDeleteThread implements Callable<Long> {
private Jedis jedis;
ScanParams params;
CountDownLatch countDownLatch;

public JedisDeleteThread(Jedis jedis, ScanParams params, CountDownLatch downLatch) {
this.jedis = jedis;
this.params = params;
this.countDownLatch = downLatch;
}

@Override
public Long call() throws Exception {
long sum = 0;
try {
String cursor = ScanParams.SCAN_POINTER_START;
ScanResult<String> scanResult = null;
List<String> result = null;
Pipeline pipeline = jedis.pipelined();
do {
scanResult = jedis.scan(cursor, params);
result = scanResult.getResult();
if (result != null && result.size() > 0) {
sum += result.size();
int num = 0;
for (String key : result) {
pipeline.del(key);
num++;
if (num > 500) {
num = 0;
pipeline.sync();
}
}
if (num > 0) {
pipeline.sync();
}
}
cursor = scanResult.getStringCursor();
}
while (!cursor.equals("0"));
if (pipeline != null) {
pipeline.close();
}
} catch (Exception e) {
log.error("delete key error:", e);
} finally {
countDownLatch.countDown();
jedis.close();
}
return sum;
}
}

jvm深入-内存交互

发表于 2019-08-26 | 分类于 java

每一个线程有一个工作内存。工作内存和主存独立。工作内存存放主存中变量的值的拷贝。

20200423161045

当数据从主内存复制到工作存储时,必须出现两个动作:第一,由主内存执行的读(read)操作;第二,由工作内存执行的相应的load操作;当数据从工作内存拷贝到主内存时,也出现两个操作:第一个,由工作内存执行的存储(store)操作;第二,由主内存执行的相应的写(write)操作。

每一个操作都是原子的,即执行期间不会被中断

对于普通变量,一个线程中更新的值,不能马上反应在其他变量中。如果需要在其他线程中立即可见,需要使用volatile关键字作为标识。

20200423161055

1、可见性:一个线程修改了变量,其他线程可以立即知道

保证可见性的方法:

  • volatile
  • synchronized (unlock之前,写变量值回主存)
  • final(一旦初始化完成,其他线程就可见)

2、有序性:本线程内,操作都是有序的;线程外观察,操作都是无序的。

3、指令重排:

20200423161110

指令重排:破坏了线程间的有序性:

20200423161119

指令重排:保证有序性的方法:

20200423161127

指令重排的基本原则:

程序顺序原则:一个线程内保证语义的串行性

volatile规则:volatile变量的写,先发生于读

锁规则:解锁(unlock)必然发生在随后的加锁(lock)前

传递性:A先于B,B先于C 那么A必然先于C

线程的start方法先于它的每一个动作

线程的所有操作先于线程的终结(Thread.join())

线程的中断(interrupt())先于被中断线程的代码

对象的构造函数执行结束先于finalize()方法

12…25下一页
初晨

初晨

永远不要说你知道本质,更别说真相了。

249 日志
46 分类
109 标签
近期文章
  • WebSocket、Socket、TCP、HTTP区别
  • Springboot项目的接口防刷
  • 深入理解Volatile关键字及其实现原理
  • 使用vscode搭建个人笔记环境
  • HBase介绍安装与操作
© 2018 — 2020 Copyright
由 Hexo 强力驱动
|
主题 — NexT.Gemini v5.1.4