获取源数据推送到Kafka

打开BigData-KongGuan项目

  • 打开BigData-KongGuan项目,在上一个任务("用户登录")的基础上继续完成本阶段任务。

  • 初始化加载SpringBoot项目的代码所在位置src/main/java/com/qrsoft/BigDataKongGuanApplication.java ,代码如下:

    package com.qrsoft;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.scheduling.annotation.EnableScheduling;

    @SpringBootApplication
    @EnableScheduling
    public class BigDataKongGuanApplication{
    public static ConfigurableApplicationContext appConfig;
    public static void main(String[] args) throws IOException, URISyntaxException, InterruptedException {
    appConfig = SpringApplication.run(BigDataKongGuanApplication.class, args);
    }
    }

2、编写TimeTaskService类,并配置HBase参数和基础参数

  • 代码所在位置src/main/java/com/qrsoft/service/TimeTaskService.java。

  • 在com.qrsoft.service包下,创建TimeTaskService类,首先,定义一些变量用于临时保存查询结果:

    package com.qrsoft.service;

    import com.alibaba.fastjson.JSON;
    import com.qrsoft.common.Constants;
    import com.qrsoft.util.*;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.hadoop.hbase.filter.Filter;
    import org.apache.hadoop.hbase.filter.FilterList;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Service;

    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;

    @Service
    @Slf4j
    public class TimeTaskService {

      //本次查询的最后一个rowKey
      //kg_PlanData计划数据
      private static byte[] planDataLastRowKey = null;
      //Kg_WarnFlightHistory航班指令告警数据
      private static byte[] warnFlightLastRowKey = null;
      //Kg_ATCDutyInfo管制值班人员数据
      private static byte[] aTCDutyLastRowKey = null;
      //Kg_WarnSimilarHistory相似航班号告警数据
      private static byte[] warnSimilarLastRowKey = null;
      //kg_AFTN报文数据
      private static byte[] aFTNLastRowKey = null;
      //kg_ATC报文数据
      private static byte[] aTCLastRowKey = null;
      //kg_callSaturation扇区通话饱和度数据
      private static byte[] callSaturationLastRowKey = null;
    
      //kg_ATC扇区数据 计时
      private static String atcDataTime = "20180101";
      //kg_AFTN报文数据 计时
      private static String aftnDataTime = "20180101";
      //kg_callSaturation扇区通话饱和度数据 计时
      private static String callDataTime = "20180101";
      //kg_PlanData计划数据 计时
      private static String planDataTime = "20180101";
      //Kg_WarnFlightHistory航班指令告警数据 计时
      private static String warnFlightDataTime = "20180101";
      //Kg_WarnSimilarHistory相似航班号告警数据 计时
      private static String warnSimilarDataTime = "20180101";
      //Kg_ATCDuty 管制员数据 计时
      private static String atcDutyDataTime = "20180101";
    
      //每次查询的数据量
      private static int selectLimit = 30;
      
      	// ... 后面将继续编写以下方法 ...
      	// ... getDateAndDepositInKafka方法 ...
      	// ... timingGetData方法 ...
    

    }

3、在TimeTaskService类中,添加getDateAndDepositInKafka方法,实现HBase分页查询,查询指定数量的数据,将数据放到Kafka中

  • 在getDateAndDepositInKafka方法会使用到其他一些工具类,例如:常量工具类、分页模型工具类、HBase数据访问工具类等;

1)编写getDateAndDepositInKafka方法,用于读取HBase的数据,并将数据放到不同的Kafka Topic中:

private static byte[] getDateAndDepositInKafka(String topic, String tableName,byte[] lastRowKey, String date, String family, String column) throws SQLException {
    return null;
    // ... 此处需要补充代码 ...
}
类名 方法名 参数 返回值
TimeTaskService getDateAndDepositInKafka topic:kafka topic名称 tableName:HBase中的表名 lastRowKey:起始行键 date:查询的飞行计划时间 family:HBase表的列族 column:查询HBase表的列 HBase本次分页查询记录最后的行键值 lastrowkey,该值将作为下次查询的起始行键
类引用/依赖 Constants:定义常量的工具类,例如,Kafka Topic的名称。 HBasePageModel:自定义的HBase表数据分页模型工具类。 HQueryFilterUtil:hbase查询fileter组合工具类。 HBaseUtils:HBase数据访问的工具类。 KafkaUtils:Kafka操作的工具类。 DateUtils:日期操作的工具类。 MultiRadar:雷达数据表对应的实体类。 MultiRadarMapper:读取雷达数据表的数据访问类。

2)编写com.qrsoft.common.Constants类,该类为当前项目中使用的常量的定义,包括当前任务阶段使用的Kafka Topic名称等:

代码所在位置src/main/java/com/qrsoft/common/Constants.java,核心代码如下:

package com.qrsoft.common;

public class Constants {
	//间隔时间
	public final static int INTERVAL_TIME_10MIN = 10*60*1000;// 10分钟
	public final static int INTERVAL_TIME_5MIN = 5*60*1000;// 5分钟
	public final static int INTERVAL_TIME_1MIN = 60*1000;// 1分钟
	public final static int INTERVAL_TIME_30SEC = 30*1000;// 30秒
	public final static int INTERVAL_TIME_10SEC = 10*1000;// 10秒
	public final static int INTERVAL_TIME_5SEC = 5*1000;// 5秒
	//每分钟读取条数
	public final static int READ_COUNT = 10;
	//kg_airport
	public final static String TABLE_AIRPORT = "kg_airport";
	//kg_airlinecompany
	public final static String TABLE_AIRLINECOMPANY = "kg_airlinecompany";
	//kg_PlanData计划数据
	public final static String TASK_PlANDATA = "task_PlanData";
	public final static String TABLE_PlANDATA = "Kg_PlanData";
	public final static String FAMILY_PlANDATA = "ReportHome";
	public final static String COLUMN_PlANDATA = "EXECUTE_DATE";
	//kg_MultiRadarData综合航迹数据 
	public final static String TASK_RADAR = "task_Radar";
	public final static String TABLE_RADAR = "Kg_MultiRadarData";
	public final static String FAMILY_RADAR = "RadarHome";
	public final static String COLUMN_RADAR = "EXECUTE_DATE";
	//kg_AFTN报文数据
	public final static String TASK_AFTN = "task_Aftn";
	public final static String TABLE_AFTN = "Kg_AFTN";
	public final static String FAMILY_AFTN = "AFTNHome";
	public final static String COLUMN_AFTN = "EXECUTE_DATE";
	//Kg_ATCDutyInfo管制值班人员数据
	public final static String TASK_ATCDUTY = "task_ATCDuty";
	public final static String TABLE_ATCDUTY = "Kg_ATCDutyInfo";
	public final static String FAMILY_ATCDUTY = "ATCDutyHome";
	public final static String COLUMN_ATCDUTY = "SEND_TIME";
	//Kg_WarnFlightHistory航班指令告警数据
	public final static String TASK_WARNFLIGHT = "task_WarnFlight";
	public final static String TABLE_WARNFLIGHT = "Kg_WarnFlightHistory";
	public final static String FAMILY_WARNFLIGHT = "WarnFlightHome";
	public final static String COLUMN_WARNFLIGHT = "GJ_DATE";
	//Kg_WarnSimilarHistory相似航班号告警数据
	public final static String TASK_WARNSIMILAR = "task_WarnSimilar";
	public final static String TABLE_WARNSIMILAR = "Kg_WarnSimilarHistory";
	public final static String FAMILY_WARNSIMILAR = "WarnSimilarHome";
	public final static String COLUMN_WARNSIMILAR = "GJ_DATE";
    //Kg_ATC扇区信息
    public final static String TASK_ATC = "task_ATC";
    public final static String TABLE_ATC = "Kg_ATC";
    public final static String FAMILY_ATC = "ATCHome";
    public final static String COLUMN_ATC = "EXECUTE_DATE";
	//Kg_CallSaturation 扇区通话饱和度信息
	public final static String TASK_CALLSATURATION = "task_CallSaturation";
	public final static String TABLE_CALLSATURATION = "Kg_CallSaturation";
	public final static String FAMILY_CALLSATURATION = "SaturationHome";
	public final static String COLUMN_CALLSATURATION = "SEND_TIME";
}

3)编写com.qrsoft.util.HBasePageModel类,该类是自定义的HBase表数据分页模型工具类,可参考以下代码实现分页功能,也可以根据这个思路自行查找资料,独立完成此类的编写。此类主要用于构建HBase分页模型:

代码所在位置src/main/java/com/qrsoft/util/HBasePageModel.java,核心代码如下:

package com.qrsoft.util;

import org.apache.hadoop.hbase.client.Result;
import java.io.Serializable;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class HBasePageModel implements Serializable {
    private static final long serialVersionUID = 330410716100946538L;
    private int pageSize = 100;
    private int pageIndex = 0;
    private int prevPageIndex = 1;
    private int nextPageIndex = 1;
    private int pageCount = 0;
    private int pageFirstRowIndex = 1;
    private byte[] pageStartRowKey = null;
    private byte[] pageEndRowKey = null;
    private boolean hasNextPage = true;
    private int queryTotalCount = 0;
    private long startTime = System.currentTimeMillis();
    private long endTime = System.currentTimeMillis();
    private List<Result> resultList = new ArrayList<Result>();
    private List<Map<String,String>> list= new ArrayList<Map<String,String>>();

    public HBasePageModel(int pageSize) {
        this.pageSize = pageSize;
    }
    //获取分页记录数量
    public int getPageSize() {
        return pageSize;
    }
    //设置分页记录数量
    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }
    //获取当前页序号
    public int getPageIndex() {
        return pageIndex;
    }
    //设置当前页序号
    public void setPageIndex(int pageIndex) {
        this.pageIndex = pageIndex;
    }
    //获取分页总数
    public int getPageCount() {
        return pageCount;
    }
    //设置分页总数
    public void setPageCount(int pageCount) {
        this.pageCount = pageCount;
    }
    //获取每页的第一行序号
    public int getPageFirstRowIndex() {
        this.pageFirstRowIndex = (this.getPageIndex() - 1) * this.getPageSize() + 1;
        return pageFirstRowIndex;
    }
    //获取每页起始行键
    public byte[] getPageStartRowKey() {
        return pageStartRowKey;
    }
    //设置每页起始行键
    public void setPageStartRowKey(byte[] pageStartRowKey) {
        this.pageStartRowKey = pageStartRowKey;
    }
    //获取每页结束行键
    public byte[] getPageEndRowKey() {
        return pageEndRowKey;
    }
    //设置每页结束行键
    public void setPageEndRowKey(byte[] pageEndRowKey) {
        this.pageEndRowKey = pageEndRowKey;
    }
    //获取上一页序号
    public int getPrevPageIndex() {
        if(this.getPageIndex() > 1) {
            this.prevPageIndex = this.getPageIndex() - 1;
        } else {
            this.prevPageIndex = 1;
        }
        return prevPageIndex;
    }
    //获取下一页序号
    public int getNextPageIndex() {
        this.nextPageIndex = this.getPageIndex() + 1;
        return nextPageIndex;
    }
    //获取是否有下一页
    public boolean isHasNextPage() {
//这个判断是不严谨的,因为很有可能剩余的数据刚好够一页。
        if(this.getResultList().size() == this.getPageSize()) {
            this.hasNextPage = true;
        } else {
            this.hasNextPage = false;
        }
        return hasNextPage;
    }
    //获取已检索总记录数
    public int getQueryTotalCount() {
        return queryTotalCount;
    }
    //获取已检索总记录数
    public void setQueryTotalCount(int queryTotalCount) {
        this.queryTotalCount = queryTotalCount;
    }
    //初始化起始时间(毫秒)
    public void initStartTime() {
        this.startTime = System.currentTimeMillis();
    }
    //初始化截止时间(毫秒)
    public void initEndTime() {
        this.endTime = System.currentTimeMillis();
    }
    //获取毫秒格式的耗时信息
    public String getTimeIntervalByMilli() {
        return String.valueOf(this.endTime - this.startTime) + "毫秒";
    }
    //获取秒格式的耗时信息
    public String getTimeIntervalBySecond() {
        double interval = (this.endTime - this.startTime)/1000.0;
        DecimalFormat df = new DecimalFormat("#.##");
        return df.format(interval) + "秒";
    }
    //获取HBase检索结果集合
    public List<Result> getResultList() {
        return resultList;
    }
    //设置HBase检索结果集合
    public void setResultList(List<Result> resultList) {
        this.resultList = resultList;
    }
	public List<Map<String, String>> getList() {
		return list;
	}
	public void setList(List<Map<String, String>> list) {
		this.list = list;
	}
}

4)编写com.qrsoft.util.HQueryFilterUtil类,该类是HBase查询过滤器的组合工具。

代码所在位置src/main/java/com/qrsoft/util/HQueryFilterUtil.java,核心代码如下:

package com.qrsoft.util;

import org.apache.hadoop.hbase.filter.*;
import org.apache.hadoop.hbase.util.Bytes;
import java.util.List;

public class HQueryFilterUtil {
	public static final int subStringFilter=1;
	public static Filter newFilter(int type, String family, String column, String value){
		switch (type) {
		case subStringFilter:
			return newSubStringFilter(family, column, value);
		default:
			throw new RuntimeException("filter type not exists");
		}
	}
	//family:column中包含字符串value
	public static Filter newSubStringFilter(String family, String column, String value){
		SubstringComparator comp = new SubstringComparator(value);
		return new SingleColumnValueFilter(Bytes.toBytes(family), Bytes.toBytes(column), CompareOp.EQUAL,comp);
	}
	//查询最多返回行数
	public static Filter rowLimitFilter(int rowcount){
		return new PageFilter(rowcount);
	}
	public  static FilterList or(List<Filter> filterList){
		return newList(FilterList.Operator.MUST_PASS_ONE,filterList);
	}
	public  static FilterList and(List<Filter> filterList){
		return newList(FilterList.Operator.MUST_PASS_ALL,filterList);
	}
	private static FilterList newList(FilterList.Operator oper, List<Filter> filterList){
		FilterList list = new FilterList(oper);
		for(Filter f:filterList){
			list.addFilter(f);
		}
		return list;
	}
}

5)编写com.qrsoft.util.KafkaUtils类,该类是Kafka操作的工具类。

代码所在位置src/main/java/com/qrsoft/util/KafkaUtils.java,核心代码如下:

package com.qrsoft.util;

import kafka.serializer.StringEncoder;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import java.io.IOException;
import java.util.Properties;

/***
 * kafka工具类
 */
public class KafkaUtils {
	@SuppressWarnings("rawtypes")
	private static KafkaProducer _producer;

	/**
	 * 获取kafka生产者消息
	 */
	@SuppressWarnings("rawtypes")
	private static KafkaProducer GetProducer() {
		if (_producer == null) {
			_producer = createProducer();
		}
		return _producer;
	}

	/**
	 * 向kafka中传入数据
	 *
	 * @param topic   topic名称
	 * @param message 消息
	 * @return true成功,false失败
	 */
	@SuppressWarnings({"rawtypes", "unchecked"})
	public static boolean SendMessage(String topic, String message) {
		// 创建一个producer的对象
		Producer producer = GetProducer();
		try {
			// 使用produer发送消息
			producer.send(new ProducerRecord(topic, "message" + message, message));
			//TimeUnit.SECONDS.sleep(5);
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	/***
	 * 创建Producer的实例
	 */
	@SuppressWarnings("rawtypes")
	private static KafkaProducer createProducer()  {

		Properties properties = null;
		try {
			properties = PropertiesLoaderUtils.loadAllProperties("kafka.properties");
		} catch (IOException e) {
			e.printStackTrace();
		}
		String bootstrap = properties.get("bootstrap.servers").toString();
		String zookeeper = properties.get("zookeeper.connect").toString();
		String metadata = properties.get("metadata.broker.list").toString();

		Properties props = new Properties();
		// 该地址是集群的子集,用来探测集群。
		props.put("bootstrap.servers", bootstrap);

		// 声明zk
		props.put("zookeeper.connect", zookeeper);
		props.put("serializer.class", StringEncoder.class.getName());

		// 声明Broker的地址
		props.put("metadata.broker.list", metadata);
		props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
		props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

		return new KafkaProducer(props);
	}
}

6)编写com.qrsoft.util.DateUtils类,该类是日期操作的工具类。

代码所在位置src/main/java/com/qrsoft/util/DateUtils.java,核心代码如下:

package com.qrsoft.util;

import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

/**
 * Title: DateUtils.java Description: 日期工具类
 */
public class DateUtils {
    public static String myDateUtils(String time){
        if(time.equals("20181231")){
            return "20180101";
        }
        SimpleDateFormat sd = new SimpleDateFormat("yyyyMMdd");
        String newDate = time;
        try {
            Date parse = sd.parse(time);
            long newTime = parse.getTime()+(1000*60*60*24);
            Date newDate1 = new Date(newTime);
            newDate = sd.format(newDate1);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return newDate;
    }
}

7)编写com.qrsoft.util.HBaseUtils类,该类是HBase数据访问的工具类。

代码所在位置src/main/java/com/qrsoft/util/HBaseUtils.java。

初始化HBase连接:

package com.qrsoft.util;

import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.filter.FilterList;
import org.apache.hadoop.hbase.filter.PageFilter;
import org.apache.hadoop.hbase.util.Bytes;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import java.io.IOException;
import java.util.*;

public class HBaseUtils {
	private static Configuration conf = null;
	private static Connection conn = null;
	static { getInstance(); }
	public static Connection getInstance() {
		try {
			Properties properties = 
PropertiesLoaderUtils.loadAllProperties("hbase.properties");
			String hbase = properties.get("hbase.zookeeper.quorum").toString();
			String zookeeper = properties.get("zookeeper.znode.parent").toString();
			if (null == conn) {
				conf = HBaseConfiguration.create();
				conf.setInt("hbase.rpc.timeout", 24000);
				conf.setInt("hbase.client.operation.timeout", 300000);
				conf.setInt("hbase.client.scanner.timeout.period", 240000);
				conf.set("hbase.zookeeper.quorum", hbase);
				conf.set("zookeeper.znode.parent", zookeeper);
				conn = ConnectionFactory.createConnection(conf);
				return conn;
			} else {
				return conn;
			}
		} catch (Exception e) {
			System.out.println("Hbase连接异常:" + e.getMessage());
		}
		return conn;
	}
// ... scanResultByPageFilter实现分页检索表数据 ...
// ... selectFirstResultRow 实现检索指定表的第一行记录 ...
}

8)添加scanResultByPageFilter方法实现分页检索表数据

/**
	 * (如果在创建表时为此表指定了非默认的命名空间,则需拼写上命名空间名称,格式为【namespace:tablename】)。
	 * @param tableName   表名称(*)。
	 * @param startRowKey 起始行键(可以为空,如果为空,则从表中第一行开始检索)。
	 * @param endRowKey   结束行键(可以为空)。
	 * @param filterList  检索条件过滤器集合(不包含分页过滤器;可以为空)。
	 * @param maxVersions 指定最大版本数【如果为最大整数值,则检索所有版本;如果为最小整数值,则检索最新版本;否则只检索指定的版本数】。
	 * @param pageModel   分页模型(*)。
	 * @return 返回HBasePageModel分页对象。
	 */
	public static HBasePageModel scanResultByPageFilter(String tableName, byte[] startRowKey, byte[] endRowKey,FilterList filterList, int maxVersions, HBasePageModel pageModel) {
		if (pageModel == null) {
			pageModel = new HBasePageModel(10);
		}
		if (maxVersions <= 0) {
			// 默认只检索数据的最新版本
			maxVersions = Integer.MIN_VALUE;
		}
		pageModel.initStartTime();
		pageModel.initEndTime();
		if (StringUtils.isBlank(tableName)) {
			return pageModel;
		}
		HTable table = null;

		try {
			// 根据HBase表名称,得到HTable表对象 
			table = (HTable) getInstance().getTable(TableName.valueOf(tableName.getBytes()));
			//getInstance().getTable(TableName.valueOf(tableName));
			//HBaseTableManageUtil.getHBaseTable(tableName);
			int tempPageSize = pageModel.getPageSize();
			boolean isEmptyStartRowKey = false;
			if (startRowKey == null) {
				// 则读取表的第一行记录,这里用到了笔者本人自己构建的一个表数据操作类。
				Result firstResult = selectFirstResultRow(tableName, filterList);
				if (firstResult == null) {
					return pageModel;
				}
				startRowKey = firstResult.getRow();
			}
			if (pageModel.getPageStartRowKey() == null) {
				isEmptyStartRowKey = true;
				pageModel.setPageStartRowKey(startRowKey);
			} else {
				if (pageModel.getPageEndRowKey() != null) {
					pageModel.setPageStartRowKey(pageModel.getPageEndRowKey());
				}
				// 从第二页开始,每次都多取一条记录,因为第一条记录是要删除的。
				tempPageSize += 1;
			}

			Scan scan = new Scan();
			scan.setStartRow(pageModel.getPageStartRowKey());
			if (endRowKey != null) {
				scan.setStopRow(endRowKey);
			}
			PageFilter pageFilter = new PageFilter(pageModel.getPageSize() + 1);
			if (filterList != null) {
				filterList.addFilter(pageFilter);
				scan.setFilter(filterList);
			} else {
				scan.setFilter(pageFilter);
			}
			if (maxVersions == Integer.MAX_VALUE) {
				scan.setMaxVersions();
			} else if (maxVersions == Integer.MIN_VALUE) {

			} else {
				scan.setMaxVersions(maxVersions);
			}
			ResultScanner scanner = table.getScanner(scan);
			List<Result> resultList = new ArrayList<Result>();
			int index = 0;

			Map<String, String> rowMap;
			List<Map<String, String>> rowList = new ArrayList<Map<String, String>>();
			for (Result rs : scanner.next(tempPageSize)) {
				if (isEmptyStartRowKey == false && index == 0) {
					index += 1;
					continue;
				}
				if (!rs.isEmpty()) {
					//默认存入集合
					resultList.add(rs);
					//制作每一行数据集合
					rowMap = new HashMap<String, String>();
					//存入主键
					rowMap.put("rowKey", Bytes.toString(rs.getRow()));
					//存入各字段
					NavigableMap<byte[], NavigableMap<byte[], byte[]>> rowMap1 = rs.getNoVersionMap();
					for (byte[] f : rowMap1.keySet()) {
						NavigableMap<byte[], byte[]> colMap = rowMap1.get(f);
						for (byte[] c : colMap.keySet()) {
							rowMap.put(Bytes.toString(c), Bytes.toString(colMap.get(c)));
						}
					}
					rowList.add(rowMap);
				}
				index += 1;
			}
			scanner.close();
			pageModel.setResultList(resultList);
			pageModel.setList(rowList);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				table.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		int pageIndex = pageModel.getPageIndex() + 1;
		pageModel.setPageIndex(pageIndex);
		if (pageModel.getResultList().size() > 0) {
			// 获取本次分页数据首行和末行的行键信息
			byte[] pageStartRowKey = pageModel.getResultList().get(0).getRow();
			byte[] pageEndRowKey = pageModel.getResultList().get(pageModel.getResultList().size() - 1).getRow();
			pageModel.setPageStartRowKey(pageStartRowKey);
			pageModel.setPageEndRowKey(pageEndRowKey);
		}
		int queryTotalCount = pageModel.getQueryTotalCount() + pageModel.getResultList().size();
		pageModel.setQueryTotalCount(queryTotalCount);
		pageModel.initEndTime();
		//pageModel.printTimeInfo();
		return pageModel;
	}

9)编写selectFirstResultRow方法, 实现检索指定表的第一行记录

/**
	 * 检索指定表的第一行记录。<br>
	 * (如果在创建表时为此表指定了非默认的命名空间,则需拼写上命名空间名称,格式为【namespace:tablename】)。
	 *
	 * @param tableName  表名称(*)。
	 * @param filterList 过滤器集合,可以为null。
	 * @return
 */
public static Result selectFirstResultRow(String tableName, FilterList filterList) {
		if (StringUtils.isBlank(tableName)) return null;
		HTable table = null;
		try {
			table = (HTable) getInstance().getTable(TableName.valueOf(tableName.getBytes()));
			//getTable(TableName.valueOf(tableName));
			Scan scan = new Scan();
			//scan.setCaching(20);
			if (filterList != null && filterList.getFilters().size() != 0) {
				scan.setFilter(filterList);
			}
			ResultScanner scanner = table.getScanner(scan);
			Iterator<Result> iterator = scanner.iterator();
			int index = 0;
			if(iterator.hasNext()){
				Result next = iterator.next();
				return  next;
			}
			scanner.close();
			return  null;
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				table.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return null;
}

10)编写com.qrsoft.entity.MultiRadar类,该类是访问雷达数据的数据实体类。

代码所在位置src/main/java/com/qrsoft/entity/MultiRadar.java,核心代码如下:

package com.qrsoft.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("multiradar_number")
public class MultiRadar implements Serializable {
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    @TableField(value = "TRACK_NUMBER")
    private String trackNumber;
    @TableField(value = "AREA_SOURCE")
    private String areaSource;
    @TableField(value = "SEND_RADAR_TIME")
    private String sendRadarTime;
    @TableField(value = "RADAR_TYPE")
    private String radarType;
    @TableField(value = "ACID")
    private String acid;
    @TableField(value = "SSR_CODE")
    private String ssrCode;
    @TableField(value = "ZHIJIAO_X")
    private String zhijiaoX;
    @TableField(value = "ZHIJIAO_Y")
    private String zhijiaoY;
    @TableField(value = "RADAR_LONGTITUDE")
    private String radarLongtitude;
    @TableField(value = "RADAR_LATITUDE")
    private String radarLatitude;
    @TableField(value = "RADAR_HEIGHT")
    private String radarHeight;
    @TableField(value = "SPEED_X")
    private String speedX;
    @TableField(value = "SPEED_Y")
    private String speedY;
    @TableField(value = "RADAR_SPEED")
    private String radarSpeed;
    @TableField(value = "DIRECTION")
    private String direction;
    @TableField(value = "RADAR_CFL")
    private String radarCfl;
    @TableField(value = "FCU")
    private String fcu;
    @TableField(value = "TIME")
    private String time;
    @TableField(value = "FLYSTATUS")
    private String flystatus;
    @TableField(value = "CLIMBORDOWN_SPEED")
    private String climbordownSpeed;
}

11)编写com.qrsoft.mapper.MultiRadarMapper接口,该接口是访问雷达数据的数据访问接口。

代码所在位置src/main/java/com/qrsoft/mapper/MultiRadarMapper.java。

package com.qrsoft.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qrsoft.entity.MultiRadar;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface MultiRadarMapper extends BaseMapper<MultiRadar> {
}
  • 回到com.qrsoft.service.TimeTaskService类中,继续编写**getDateAndDepositInKafka方法,**执行分页查询,每次查询指定数量的数据,将查询的HBase中的数据,推送到Kafka的topic中

    private static byte[] getDateAndDepositInKafka(String topic, String tableName, byte[] lastRowKey, String date, String family, String column)
    throws SQLException {
    // 从hbase中取出数据
    List<Filter> rowFilters = new ArrayList<Filter>();
    FilterList filterList = new FilterList(rowFilters);
    HBasePageModel pageModel = new HBasePageModel(selectLimit + 1);
    // if(!topic.equals(Constants.TASK_RADAR)) {
    if (date != null) {
    Filter filter = HQueryFilterUtil.newSubStringFilter(family, column, date);
    filterList.addFilter(filter);
    }
    // }
    HBasePageModel hbasePageModel = HBaseUtils.scanResultByPageFilter(
    tableName, lastRowKey, null, filterList, 0, pageModel);
    // 获取本次查询的最后一个rowKey
    int dataSize = hbasePageModel.getList().size();
    if (dataSize > 0) {
    Map<String, String> map = hbasePageModel.getList().get(dataSize - 1);
    String rowkey = map.get("rowKey");
    lastRowKey = rowkey.getBytes();
    // 把每一条数据放入Kafka
    for (int i = 0; i < hbasePageModel.getList().size() - 1; i++) {
    // System.out.println(JSONArray.fromObject(hbasePageModel.getList().get(i)).toString());
    KafkaUtils.SendMessage(topic, JSON.toJSONString(hbasePageModel.getList().get(i)));
    System.out.println("============" + JSON.toJSONString(hbasePageModel.getList().get(i)) + "==============");
    }
    System.out.println(tableName + "此批次最后一个rowkey:" + rowkey);
    }

          System.out.println(dataSize + "===================================" + selectLimit);
          if (dataSize < selectLimit) {
              if (topic.equals(Constants.TASK_ATC)) {
                  atcDataTime = DateUtils.myDateUtils(date);
                  System.out.println(Constants.TASK_ATC + "更新数据时间为" + atcDataTime);
              } else if (topic.equals(Constants.TASK_AFTN)) {
                  aftnDataTime = DateUtils.myDateUtils(date);
                  System.out.println(Constants.TASK_AFTN + "更新数据时间为" + aftnDataTime);
              } else if (topic.equals(Constants.TASK_PlANDATA)) {
                  planDataTime = DateUtils.myDateUtils(date);
                  System.out.println(Constants.TASK_PlANDATA + "更新数据时间为" + planDataTime);
              } else if (topic.equals(Constants.TASK_WARNFLIGHT)) {
                  warnFlightDataTime = DateUtils.myDateUtils(date);
                  System.out.println(Constants.TASK_WARNFLIGHT + "更新数据时间为" + warnFlightDataTime);
              } else if (topic.equals(Constants.TASK_WARNSIMILAR)) {
                  warnSimilarDataTime = DateUtils.myDateUtils(date);
                  System.out.println(Constants.TASK_WARNSIMILAR + "更新数据时间为" + warnSimilarDataTime);
              } else if (topic.equals(Constants.TASK_CALLSATURATION)) {
                  callDataTime = DateUtils.myDateUtils(date);
                  System.out.println(Constants.TASK_CALLSATURATION + "更新数据时间为" + callDataTime);
              } else if (topic.equals(Constants.TASK_ATCDUTY)) {
                  atcDutyDataTime = DateUtils.myDateUtils(date);
                  System.out.println(Constants.TASK_ATCDUTY + "更新数据时间为" + atcDutyDataTime);
              }
          }
          return lastRowKey;
    

    }

  • 在resources目录下创建或导入HBase和Kafka相关配置文件

1)创建或导入hbase.properties文件,内容如下:

#hbase.rpc.timeout=24000
#hbase.client.operation.timeout=300000
#hbase.client.scanner.timeout.period=240000
# 注意:zookeeper.quorum请根据你当前环境所安装zookeeper的服务器名称进行设置
hbase.zookeeper.quorum=node1,node2,node3
zookeeper.znode.parent=/hbase

2)创建或导入kafka.properties文件,内容如下:

# 注意:请根据你当前环境所安装zookeeper和kafka的服务器名称进行设置
bootstrap.servers=node1:9092,node2:9092,node3:9092
zookeeper.connect=node1:2181,node2:2181,node3:2181
metadata.broker.list=node1:9092,node2:9092,node3:9092
key.serializer=org.apache.kafka.common.serialization.StringSerializer
value.serializer=org.apache.kafka.common.serialization.StringSerializer

4、创建定时任务,启动数据获取,设置Topic名称

代码所在位置src/main/java/com/qrsoft/service/TimeTaskService.java。

  • 回到com.qrsoft.service.TimeTaskService类中,继续编写timingGetData方法(每间隔30秒执行一次定时任务),核心代码如下:

    @Scheduled(cron = "*/30 * * * * *")
    public void timingGetData() {
    System.out.println("定时任务启动获取数据");
    try {
    planDataLastRowKey = getDateAndDepositInKafka(Constants.TASK_PlANDATA, Constants.TABLE_PlANDATA, planDataLastRowKey, planDataTime, Constants.FAMILY_PlANDATA, Constants.COLUMN_PlANDATA);
    warnFlightLastRowKey = getDateAndDepositInKafka(Constants.TASK_WARNFLIGHT, Constants.TABLE_WARNFLIGHT, warnFlightLastRowKey, warnFlightDataTime, Constants.FAMILY_WARNFLIGHT, Constants.COLUMN_WARNFLIGHT);
    aTCDutyLastRowKey = getDateAndDepositInKafka(Constants.TASK_ATCDUTY, Constants.TABLE_ATCDUTY, aTCDutyLastRowKey, atcDutyDataTime, Constants.FAMILY_ATCDUTY, Constants.COLUMN_ATCDUTY);
    warnSimilarLastRowKey = getDateAndDepositInKafka(Constants.TASK_WARNSIMILAR, Constants.TABLE_WARNSIMILAR, warnSimilarLastRowKey, warnSimilarDataTime, Constants.FAMILY_WARNSIMILAR, Constants.COLUMN_WARNSIMILAR);
    aFTNLastRowKey = getDateAndDepositInKafka(Constants.TASK_AFTN, Constants.TABLE_AFTN, aFTNLastRowKey, aftnDataTime, Constants.FAMILY_AFTN, Constants.COLUMN_AFTN);
    aTCLastRowKey = getDateAndDepositInKafka(Constants.TASK_ATC, Constants.TABLE_ATC, aTCLastRowKey, atcDataTime, Constants.FAMILY_ATC, Constants.COLUMN_ATC);
    callSaturationLastRowKey = getDateAndDepositInKafka(Constants.TASK_CALLSATURATION, Constants.TABLE_CALLSATURATION, callSaturationLastRowKey, callDataTime, Constants.FAMILY_CALLSATURATION, Constants.COLUMN_CALLSATURATION);
    // 注意:
    //1)"实时飞行数据"对应的Topic,由于直接从HBase读取实时飞行数据推送到Kafka后,在使用时会因为实时飞行数据的时间隔太短,导致在地图上显示飞行状态和位置时,移动的不明显;
    //2)为了在演示的时候提高体验度,所以将下面一行代码注释掉,而这部分功能单独写了一个TimeTask2Service.java的类;
    //3)在TimeTask2Service.java的类中,使用的实时飞行数据是从HBase中读取的已经处理过的保存到本地的数据,实时飞行数据的时间间隔变大,在使用该数据时能明显感觉到飞机在移动;
    //radarLastRowKey=getDateAndDepositInKafka(Constants.TASK_RADAR,Constants.TABLE_RADAR,radarLastRowKey,multiRadarDataTime,Constants.FAMILY_RADAR,Constants.COLUMN_RADAR);

     } catch (SQLException e) {
          e.printStackTrace();
          log.info("获取数据失败!", e.getMessage());
     }
    

    }

5、单独处理"实时飞行数据",将数据推送到相应的Kafka Topic

|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 注意: 1)处理"实时飞行数据"推送对应的Topic。由于直接从HBase读取实时飞行数据推送到Kafka后,在使用时会因为实时飞行数据的时间隔太短,导致在地图上显示飞行状态和位置时,移动的不明显; 2)为了在演示的时候提高体验度,这部分功能单独写了一个TimeTask2Service.java的类; 3)在TimeTask2Service.java的类中,使用的实时飞行数据是从HBase中读取的已经处理过的保存到本地的数据,实时飞行数据的时间间隔变大,在使用该数据时能明显感觉到飞机在移动; |

  • 准备数据

将提供的数据文件放到/opt/data/目录(也可以放到任意目录下,但是需要修改下代码中读取文件的路径),可以查看一下数据是否存在,如图:

  • 代码所在位置src/main/java/com/qrsoft/service/TimeTask2Service.java

  • 创建com.qrsoft.service.TimeTask2Service类,核心代码如下:

    package com.qrsoft.service;

    import cn.hutool.core.io.file.FileReader;
    import com.qrsoft.util.KafkaUtils;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Service;

    import java.io.IOException;
    import java.net.URISyntaxException;
    import java.util.List;

    @Service
    @Slf4j
    public class TimeTask2Service {
    /**
    * 实时航线飞行,为了模拟数据飞行数据飞行,从历史数据取出文本文件,通过读取每行文件,得到飞行轨迹,每个文本文件代表一架飞机
    */
    public static List<String> str0 = null;
    public static int count0 = 0;
    public static List<String> str1 = null;
    public static int count1 = 0;
    public static List<String> str2 = null;
    public static int count2 = 0;
    public static List<String> str3 = null;
    public static int count3 = 0;
    public static List<String> str4 = null;
    public static int count4 = 0;
    public static List<String> str5 = null;
    public static int count5 = 0;
    public static List<String> str6 = null;
    public static int count6 = 0;
    public static List<String> str7 = null;
    public static int count7 = 0;
    public static List<String> str8 = null;
    public static int count8 = 0;
    public static List<String> str9 = null;
    public static int count9 = 0;
    public static List<String> str10 = null;
    public static int count10 = 0;
    public static List<String> str11 = null;
    public static int count11 = 0;

      /**
       * 获取文件位置,如果是linux服务器发布需要放在换成linux目录 如果是window可以使用data里的数据
       */
      public TimeTask2Service() throws URISyntaxException, IOException, InterruptedException {
          FileReader fileReader0 = new FileReader("/opt/data/part-00000");
          str0 = fileReader0.readLines();
          FileReader fileReader1 = new FileReader("/opt/data/part-00001");
          str1 = fileReader1.readLines();
          FileReader fileReader2 = new FileReader("/opt/data/part-00002");
          str2 = fileReader2.readLines();
          FileReader fileReader3 = new FileReader("/opt/data/part-00003");
          str3 = fileReader3.readLines();
          FileReader fileReader4 = new FileReader("/opt/data/part-00004");
          str4 = fileReader4.readLines();
          FileReader fileReader5 = new FileReader("/opt/data/part-00005");
          str5 = fileReader5.readLines();
          FileReader fileReader6 = new FileReader("/opt/data/part-00006");
          str6 = fileReader6.readLines();
          FileReader fileReader7 = new FileReader("/opt/data/part-00007");
          str7 = fileReader7.readLines();
          FileReader fileReader8 = new FileReader("/opt/data/part-00008");
          str8 = fileReader8.readLines();
          FileReader fileReader9 = new FileReader("/opt/data/part-00009");
          str9 = fileReader9.readLines();
          FileReader fileReader10 = new FileReader("/opt/data/part-00010");
          str10 = fileReader10.readLines();
          FileReader fileReader11= new FileReader("/opt/data/part-00011");
          str11 = fileReader11.readLines();
      }
    
      @Scheduled(cron = "*/10 * * * * *")
      public void timingGetData() {
          System.out.println("实时轨迹任务");
          if (count0 >= str0.size()) {
              count0 = 0;
              System.out.println("数据归零");
          }
          String s0 = str0.get(count0++);
    
          if (count1 >= str1.size()) {
              count1 = 0;
              System.out.println("数据归零");
          }
          String s1 = str1.get(count1++);
    
          if (count2 >= str2.size()) {
              count2 = 0;
              System.out.println("数据归零");
          }
          String s2 = str2.get(count2++);
          if (count3 >= str3.size()) {
              count3 = 0;
              System.out.println("数据归零");
          }
          String s3 = str3.get(count3++);
          if (count4 >= str4.size()) {
              count4 = 0;
              System.out.println("数据归零");
          }
          String s4 = str4.get(count4++);
          if (count5 >= str5.size()) {
              count5 = 0;
              System.out.println("数据归零");
          }
          String s5 = str5.get(count5++);
          if (count6 >= str6.size()) {
              count6 = 0;
              System.out.println("数据归零");
          }
          String s6 = str6.get(count6++);
    
          if (count7 >= str7.size()) {
              count7 = 0;
              System.out.println("数据归零");
          }
          String s7 = str7.get(count7++);
          if (count8 >= str8.size()) {
              count8 = 0;
              System.out.println("数据归零");
          }
          String s8 = str8.get(count8++);
    
          if (count9 >= str9.size()) {
              count9 = 0;
              System.out.println("数据归零");
          }
          String s9 = str9.get(count9++);
    
          if (count10 >= str10.size()) {
              count10 = 0;
              System.out.println("数据归零");
          }
          String s10 = str10.get(count10++);
    
          if (count11 >= str11.size()) {
              count11 = 0;
              System.out.println("数据归零");
          }
          String s11 = str11.get(count11++);
    
          KafkaUtils.SendMessage("task_Radar", s0);
          KafkaUtils.SendMessage("task_Radar", s1);
          KafkaUtils.SendMessage("task_Radar", s2);
          KafkaUtils.SendMessage("task_Radar", s3);
          KafkaUtils.SendMessage("task_Radar", s4);
          KafkaUtils.SendMessage("task_Radar", s5);
          KafkaUtils.SendMessage("task_Radar", s6);
          KafkaUtils.SendMessage("task_Radar", s7);
          KafkaUtils.SendMessage("task_Radar", s8);
          KafkaUtils.SendMessage("task_Radar", s9);
          KafkaUtils.SendMessage("task_Radar", s10);
          KafkaUtils.SendMessage("task_Radar", s11);
          System.out.println("----------------------航迹数据更新----------------------");
      }
    

    }

6、测试

  • 确保Hadoop、HBase、Kafka集群都已经正常启动,如果没有正常启动,请参考前面环境安装部署的相应任务完成集群的启动。
  • 启动SpringBoot项目
  • 在控制台显示类似下面的结果:
  • 在node1节点上,查看kafka中的topic是否有数据

    [root@node1 ~]# kafka-console-consumer.sh --bootstrap-server node1:9092 --from-beginning --topic task_CallSaturation

相关推荐
洛神灬殇1 小时前
彻底认识和理解探索分布式网络编程中的SSL安全通信机制
网络·分布式·ssl
龙哥·三年风水1 小时前
workman服务端开发模式-应用开发-vue-element-admin封装websocket
分布式·websocket·vue
李洋-蛟龙腾飞公司4 小时前
HarmonyOS Next 应用元服务开发-分布式数据对象迁移数据文件资产迁移
分布式·华为·harmonyos
技术路上的苦行僧7 小时前
分布式专题(10)之ShardingSphere分库分表实战指南
分布式·shardingsphere·分库分表
GitCode官方8 小时前
GitCode 光引计划投稿 | GoIoT:开源分布式物联网开发平台
分布式·开源·gitcode
小扳9 小时前
微服务篇-深入了解 MinIO 文件服务器(你还在使用阿里云 0SS 对象存储图片服务?教你使用 MinIO 文件服务器:实现从部署到具体使用)
java·服务器·分布式·微服务·云原生·架构
zquwei18 小时前
SpringCloudGateway+Nacos注册与转发Netty+WebSocket
java·网络·分布式·后端·websocket·网络协议·spring
道一云黑板报1 天前
Flink集群批作业实践:七析BI批作业执行
大数据·分布式·数据分析·flink·kubernetes
qq_5470261791 天前
Kafka 常见问题
kafka
core5121 天前
flink sink kafka
flink·kafka·sink