HBase学习之路(八):HBase协处理器+Hadoop的表操作行为日志监控案例实战

内容简介

    • 一、协处理的基本概念
    • 二、基于协处理器的行为表操作行为监控
        • 0.实战内容与思路
        • 1.在HBase中创建两张表用于测试
        • 2. 创建普通的Java工程引入Maven支持
        • 3.创建MyRegionObserver类,继承BaseRegionObserver
        • 4.将代码打包并提交到HBase集群
        • 5.配置Hbase的配置文件并重启HBase
        • 6.测试并查看行为监控
    • 三、总结

一、协处理的基本概念

  • 使用客户端的API,配合过滤器可以对数据进行限制,使得返回客户端的数据更加精确。如果更近一步,将数据的某些处理流程直接放到服务端执行,然后仅仅返回一个小的处理结果集,类似于在服务端开启一个小型的MapReduce作业来分布式处理数据,这就是HBase的协处理器机制。
  • 协处理器允许用户在region服务器执行自己的代码,即在允许用户执行region级别的操作。协处理器分为两大类:observer与endpoint。observer类似于关系型数据库中的触发器,endpoint则类似于关系型数据库中的存储过程,简单理解就是observer是一个观察者,当某些行为比如插入数据发生或者结束时,可以对应做出何种行为,属于被动调用,而endpoint则类似于调用一个函数,或者自定义的代码,属于主动调用。
  • observer分为三类:
    (1).RegionObserver:用户可以利用这种协处理器处理数据修改事件,与表的region紧密相连,是针对表数据做出修改时的协处理器。
    (2).MasterObserver:可以被用于管理或DDL类型的操作,比如创建、修改表属性等,这是针对集群事件的协处理器。
    (3).WALObserver:供控制WAL的钩子函数。
    不同的observer提供了针对本observer的回调函数,供不同的事件进行回调。
  • endpoint:除了事件处理之外,有时候还需要将用户的自定义操作添加到服务端,endpoint通过远程过程调用来扩展RPC协议,与关系型数据库的存储过程功能相似。

二、基于协处理器的行为表操作行为监控

0.实战内容与思路

  • 本次案例HBase版本是hbase-1.2.0-cdh5.7.0,Hadoop版本是hadoop-2.6.0-cdh5.7.0 开发工具是IDEA2018。
  • 实战内容:在操作HBase的表的过程中,可以对某些操作,比如put,get操作进行监控,一旦发生了此类的行为,就把操作该行为的时间、操作的表名以及操作行为所涉及的RowKey记录到一个HDFS上的文件上。
  • 自定义一个类来继承BaseRegionObserver类,此类已经实现了所有的有关Region级别的所有方法,如果直接实现RegionObserver接口则要实现接口里面的所有方法,非常多,默认情况下继承BaseRegionObserver类没有任何的功能,用户则可以找对应要使用的方法进行重写就可以了。然后将代码打包并提交到HBase的集群中,最后配置HBase并重启即可。

1.在HBase中创建两张表用于测试

  • 在Linux终端输入命令hbase shell进入HBase的终端。执行命令create 'table1','info'来创建一张表名为table1,列族为info的表,再执行命令create 'table2','info'来创建一张表名为table1,列族为info的表:
    在这里插入图片描述

2. 创建普通的Java工程引入Maven支持

  • 完整Maven依赖如下:
    	 <properties>
    	    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    	    <maven.compiler.source>1.7</maven.compiler.source>
    	    <maven.compiler.target>1.7</maven.compiler.target>
    	    <hadoop.version>2.6.0-cdh5.7.0</hadoop.version>
    	    <hbase.version>1.2.0-cdh5.7.0</hbase.version>
      </properties>
    
      <repositories>
    	    <repository>
    	      <id>cloudera</id>
    	      <url>https://repository.cloudera.com/artifactory/cloudera-repos</url>
    	    </repository>
    	  </repositories>
    	
    	  <dependencies>
    	    <dependency>
    	      <groupId>org.apache.hbase</groupId>
    	      <artifactId>hbase-client</artifactId>
    	      <version>${hbase.version}</version>
    	    </dependency>
    	
    	    <dependency>
    	      <groupId>org.apache.hbase</groupId>
    	      <artifactId>hbase-server</artifactId>
    	      <version>${hbase.version}</version>
    	    </dependency>
    	
    	    <dependency>
    	      <groupId>org.apache.hadoop</groupId>
    	      <artifactId>hadoop-client</artifactId>
    	      <version>${hadoop.version}</version>
    	    </dependency>
    	
    	    <dependency>
    	      <groupId>junit</groupId>
    	      <artifactId>junit</artifactId>
    	      <version>4.11</version>
    	    </dependency>
      </dependencies>
    
      <build>
        <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
          <plugins>
            <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
            <plugin>
              <artifactId>maven-clean-plugin</artifactId>
              <version>3.1.0</version>
            </plugin>
            <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
            <plugin>
              <artifactId>maven-resources-plugin</artifactId>
              <version>3.0.2</version>
            </plugin>
            <plugin>
              <artifactId>maven-compiler-plugin</artifactId>
              <version>3.8.0</version>
            </plugin>
            <plugin>
              <artifactId>maven-surefire-plugin</artifactId>
              <version>2.22.1</version>
            </plugin>
            <plugin>
              <artifactId>maven-jar-plugin</artifactId>
              <version>3.0.2</version>
            </plugin>
            <plugin>
              <artifactId>maven-install-plugin</artifactId>
              <version>2.5.2</version>
            </plugin>
            <plugin>
              <artifactId>maven-deploy-plugin</artifactId>
              <version>2.8.2</version>
            </plugin>
            <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
            <plugin>
              <artifactId>maven-site-plugin</artifactId>
              <version>3.7.1</version>
            </plugin>
            <plugin>
              <artifactId>maven-project-info-reports-plugin</artifactId>
              <version>3.0.0</version>
            </plugin>
          </plugins>
        </pluginManagement>
      </build>
    </project>
    

3.创建MyRegionObserver类,继承BaseRegionObserver

  • 在MyRegionObserver类中编写HDFS文件写入方法,用于将协处理器生成的日志信息写入HDFS中。有一点需要注意,日志是不断产生的,因此需要追加写入,但是HDFS的API不支持追加写入,因此会判断日志文件是否存在,若存在则会先创建一个输入流,然后将旧的文件内容复制到新文件上,然后追加新日志。
    private void outWrite(String str) {
        try {
            // 判断文件是否存在标志
            boolean isExist = false;
            // 创建HDFS的输入流
            Configuration configuration = new Configuration();
            // 初始化HDFS文件系统写入路径,该路径为你Hadoop集群的URL地址
            FileSystem fs = FileSystem.get(new URI("hdfs://hadoop000:8020"),configuration);
            FSDataInputStream inputStream = null;
            // 如果存在该文件则先读取
            // 该路径为你存放日志文件的HDFS目录
            if(fs.exists(new Path("/data/MyRegionObserver.txt"))){
                 inputStream = fs.open(new Path("/data/MyRegionObserver.txt"));
                 isExist = true;
            }
            FSDataOutputStream outputStream = fs.create(new Path("/data/MyRegionObserver.txt"),true);
            // 将旧的行为日志复制到新文件中
            if(isExist) {
                IOUtils.copyBytes(inputStream, outputStream, 1024);
            }
            // 将新的行为信息追加写入HDFS
            outputStream.write((str + "\r\n").getBytes());
            outputStream.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    
  • MyRegionObserver类中编写日期格式方法,用于格式化表操作行为发生时的时间戳。
     private String dateFormat(Date date){
        String formatDate = "YYYY/MM/dd HH:mm:ss";
        SimpleDateFormat dateFormat = new SimpleDateFormat();
        dateFormat.applyPattern(formatDate);
        return dateFormat.format(date);
    }
    
  • MyRegionObserver类完整代码如下:
    /**
     * 自定义协处理器,区域观察者
     * 对表的行为进行日志监控
     */
    public class MyRegionObserver extends BaseRegionObserver {
    
        private void outWrite(String str) {
            try {
                // 判断文件是否存在标志
                boolean isExist = false;
                // 创建HDFS的输入流
                Configuration configuration = new Configuration();
                // 初始化HDFS文件系统写入路径,该路径为你Hadoop集群的URL地址
                FileSystem fs = FileSystem.get(new URI("hdfs://hadoop000:8020"),configuration);
                FSDataInputStream inputStream = null;
                // 如果存在该文件则先读取
                // 该路径为你存放日志文件的HDFS目录
                if(fs.exists(new Path("/data/MyRegionObserver.txt"))){
                     inputStream = fs.open(new Path("/data/MyRegionObserver.txt"));
                     isExist = true;
                }
                FSDataOutputStream outputStream = fs.create(new Path("/data/MyRegionObserver.txt"),true);
                // 将旧的行为日志复制到新文件中
                if(isExist) {
                    IOUtils.copyBytes(inputStream, outputStream, 1024);
                }
                // 将新的行为信息追加写入HDFS
                outputStream.write((str + "\r\n").getBytes());
                outputStream.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    
        // 时间格式化工具类
        private String dateFormat(Date date){
            String formatDate = "YYYY/MM/dd HH:mm:ss";
            SimpleDateFormat dateFormat = new SimpleDateFormat();
            dateFormat.applyPattern(formatDate);
            return dateFormat.format(date);
        }
    
        @Override
        public void preGetOp(ObserverContext<RegionCoprocessorEnvironment> e, Get get, List<Cell> results) throws IOException {
            super.preGetOp(e, get, results);
            String tableName = e.getEnvironment().getRegion().getTableDesc().getNameAsString();
            outWrite("[" + dateFormat(new Date()) + "] " + "表" + tableName + "开始执行Get操作,RowKey:" +
                    new String(get.getRow()));
        }
    
        @Override
        public void postGetOp(ObserverContext<RegionCoprocessorEnvironment> e, Get get, List<Cell> results) throws IOException {
            super.postGetOp(e, get, results);
            String tableName = e.getEnvironment().getRegion().getTableDesc().getNameAsString();
            outWrite("[" + dateFormat(new Date()) + "] " + "表" + tableName + "结束执行Get操作,RowKey:" +
                    new String(get.getRow()));
        }
    
        @Override
        public void prePut(ObserverContext<RegionCoprocessorEnvironment> e, Put put, WALEdit edit, Durability durability) throws IOException {
            super.prePut(e, put, edit, durability);
            String tableName = e.getEnvironment().getRegion().getTableDesc().getNameAsString();
            outWrite("[" + dateFormat(new Date()) + "] " + "表" + tableName + "开始执行Put操作,RowKey:" +
                    new String(put.getRow()));
        }
    
        @Override
        public void postPut(ObserverContext<RegionCoprocessorEnvironment> e, Put put, WALEdit edit, Durability durability) throws IOException {
            super.postPut(e, put, edit, durability);
            String tableName = e.getEnvironment().getRegion().getTableDesc().getNameAsString();
            outWrite("[" + dateFormat(new Date()) + "] " + "表" + tableName + "结束执行Put操作,RowKey:" +
                    new String(put.getRow()));
        }
    
    }
    

4.将代码打包并提交到HBase集群

  • 使用IDEA的Maven自带的打包工具,或者直接使用Maven来打包也可以,以下是使用IDEA的Maven自带的打包工具打包:
    HBase学习之路(八):HBase协处理器+Hadoop的表操作行为日志监控案例实战_第1张图片
  • 将打包好的jar包放到HBase所有节点的$HBASE_HOME/lib路径下。

5.配置Hbase的配置文件并重启HBase

  • 进入所有HBase节点的$HBASE_HOME/conf目录下,编辑hbase-site.xml文件,加入如下配置:
    <property>
            <name>hbase.coprocessor.region.classes</name>
            <value>com.train.hbase.MyRegionObserver</value>
    </property>
    
    value标签的值为你编写MyRegionObserver类的全类名。
  • 确保jar包已经分发到所有HBase节点指定的目录下,且配置文件正确配置并分发到所有节点后,重新启动HBase服务。

6.测试并查看行为监控

  • 进入HBase的终端,首先在表table1中插入一条数据:put 'table1','xiao-bang-zhu','info:column','12345'
    然后读取该数据:get 'table1','xiao-bang-zhu'
    再在表table2中插入一条数据:put 'table2','xiao-bang-zhu','info:column','wewdd'
    然后读取该数据:get 'table2','xiao-bang-zhu'
    HBase学习之路(八):HBase协处理器+Hadoop的表操作行为日志监控案例实战_第2张图片
  • 因为日志文件在HDFS上,所以可以在终端直接输入命令:hdfs dfs -cat 日志文件的HDFS目录查看日志文件:
    HBase学习之路(八):HBase协处理器+Hadoop的表操作行为日志监控案例实战_第3张图片
    可以看到我们刚才的对table1和table2表的操作已经被详细记录到日志中,包括操作的具体时间和RowKey等。除此之外还有许多HBase内部的操作也会被记录进去,比如我们执行get操作时会在meta表中寻找get操作RowKey所对应的region的位置,而meta表本身也在某个region中,因此也会触发协处理器进行日志的记录。

三、总结

  • 本文简单阐述了HBase中协处理器的概念,其核心思想可以概括为:将某些数据的处理工作直接交由服务端去完成,并将处理结果以小数据结果集的形式返回给客户端。事实上,协处理器的内容非常多,其中有更为细致的知识,碍于篇幅,这里没有给出。过滤器、计数器、协处理器为HBase的三大高级特性,如果配合来使用可以完成很多复杂的业务。感谢你的阅读,如有错误请不吝赐教!
  • 更多内容请查看 萧邦主的技术博客导航

你可能感兴趣的:(Hadoop生态)