[置顶] spring+phoenix+hbase实现

公司的一个监控项目需要改版,原因是因为监控源太多、频率也高、数据量超大,以致传统的关系型数据库查询、存储压力很大,需要改用hbase来存储。

之前只用过SSH/SSI这种架构,对phoenix和hbase也只是一知半解,通过多方求助,改版也完成了,在这里记录一下,方便大家。


首先需要phoenix+hbase的环境

我是使用虚拟机安装的,phoenix是4.2.2,hbase是0.98.11,还要求安装hadoop。

启动顺序是:首先启动hadoop,然后是hbase,再是phoenix。


然后在web项目中导入相应的jar包。jar包的位置在你安装的phoenix、hbase里面可以拿到。可以不需要全部都拿过来,主要的jar包如下:

[置顶] spring+phoenix+hbase实现_第1张图片


再是配置数据源:

<context:annotation-config />
<context:component-scan base-package="com.coreware.action,com.coreware.service,com.coreware.dao" />
<!-- phoenix jdbc -->
	<bean id="phoenixJdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<constructor-arg ref="phoenixDataSource" />
		<qualifier value="phoenixJdbcTemplate"></qualifier>
	</bean>
	
	<bean id="hBaseDao" class="com.coreware.dao.impl.HBaseBaseDAOImpl">
		<property name="jdbcTemplate" ref="phoenixJdbcTemplate" />
	</bean>

	<bean id="phoenixDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		<property name="driverClassName" value="org.apache.phoenix.jdbc.PhoenixDriver" />
		<property name="url" value="jdbc:phoenix:hduxin" />
		<property name="username" value="" />
		<property name="password" value="" />
		<property name="initialSize" value="10" />
		<property name="maxActive" value="5" />
		<!--因为Phoenix进行数据更改时不会自动的commit,必须要添加defaultAutoCommit属性,否则会导致数据无法提交的情况 -->
		<property name="defaultAutoCommit" value="true" />
	</bean>

看到上面的配置是不是很熟悉,就像配置普通的数据源一样?

使用phoenix的好处就是,可以像使用JDBC一样来操作hbase。除了一些比较复杂的查询及多表查询的支持不够外,其他都像使用JDBC一样简单。具体的语法请看:

http://phoenix.apache.org/language/index.html  这个是phoenix官网。

需要注意的就是在hbase里面没有更新的概念,如果id(rowkey)一样,直接会覆盖。所以它的更新及新增语法都是同一个  upsert into table (...) values (...) 。


第四步就是写dao层了

接口HBaseDao:

import java.util.List;

import org.springframework.jdbc.core.RowMapper;

import com.coreware.model.AgentInfo;
import com.coreware.model.ConfigInfo;
import com.coreware.model.FlumeInfo;

public interface HBaseDao {
	public List<AgentInfo> queryAgentList(String sql, Object[] params, RowMapper<AgentInfo> mapper);

	public List<ConfigInfo> queryConfigList(String sql, Object[] params, RowMapper<ConfigInfo> mapper);

	public void update(String sql, Object[] params);

	public void batchUpdate(List<String> sqls);
	
	public FlumeInfo getFlumeInfo(String querySql, Object[] params, RowMapper<FlumeInfo> mapper);
}


接口实现HBaseBaseDAOImpl:
package com.coreware.dao.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;

import com.coreware.dao.HBaseDao;
import com.coreware.model.AgentInfo;
import com.coreware.model.ConfigInfo;
import com.coreware.model.FlumeInfo;

@Repository
public class HBaseBaseDAOImpl implements HBaseDao {
	private JdbcTemplate jdbcTemplate;

	public HBaseBaseDAOImpl(JdbcTemplate template) {
		this.jdbcTemplate = template;
	}

	public HBaseBaseDAOImpl() {
		super();
	}

	@Override
	public List<AgentInfo> queryAgentList(String querySql, Object[] params, RowMapper<AgentInfo> mapper) {
		if (params != null && params.length > 0) {
			return jdbcTemplate.query(querySql, params, mapper);
		} else {
			return jdbcTemplate.query(querySql, mapper);
		}
	}

	@Override
	public List<ConfigInfo> queryConfigList(String querySql, Object[] params, RowMapper<ConfigInfo> mapper) {
		if (params != null && params.length > 0) {
			return jdbcTemplate.query(querySql, params, mapper);
		} else {
			return jdbcTemplate.query(querySql, mapper);
		}
	}

	@Override
	public void update(String updateSQL, Object[] params) {
		// System.out.println("##########UPDATE SQL:" + updateSQL);
		jdbcTemplate.update(updateSQL, params);
	}

	@Override
	public void batchUpdate(List<String> updateSQL) {
		for (String sql : updateSQL) {
			// System.out.println("##########UPDATE SQL:" + sql);
			jdbcTemplate.update(sql);
		}
	}

	@Override
	public FlumeInfo getFlumeInfo(String querySql, Object[] params, RowMapper<FlumeInfo> mapper) {
		if (params != null && params.length > 0) {
			return jdbcTemplate.queryForObject(querySql, params, mapper);
		} else {
			return jdbcTemplate.queryForObject(querySql, mapper);
		}
	}

	public JdbcTemplate getJdbcTemplate() {
		return jdbcTemplate;
	}

	@Autowired
	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}
}

service层内容太多,我在这就举个查询的例子:

public List<AgentInfo> getStatusList(Map<String, String> map) {
		StringBuffer agentSql = new StringBuffer("SELECT DISTINCT hostName,IP,sendEvents,startTime,status,sendFlow,sendFlow2G,createTime FROM biz_monitor_agent tb WHERE createTime in (SELECT MAX(createTime) FROM biz_monitor_agent GROUP BY hostName)");
		if (map != null && !map.isEmpty()) {
			if (map.containsKey("hostName")) {
				agentSql.append(" AND LOWER(hostName) like '%" + map.get("hostName").toLowerCase() + "%'");
			}
			if (map.containsKey("address")) {
				agentSql.append(" AND IP like '%" + map.get("address") + "%'");
			}
			if (map.containsKey("startTime") && map.containsKey("endTime")) {
				agentSql.append(" AND createTime BETWEEN '" + map.get("startTime") + "' AND '" + map.get("endTime") + "'");
			}
			if (map.containsKey("status")) {
				String status = map.get("status");
				if ("0".equals(status)) {
					agentSql.append(" AND createTime < '" + format() + "'");
				} else if ("1".equals(status)) {
					agentSql.append(" AND createTime > '" + format() + "' AND status='" + status + "'");
				} else {
					agentSql.append(" AND status='" + status + "'");
				}
			}
		}
		List<AgentInfo> agents = (List<AgentInfo>) hBaseDao.queryAgentList(agentSql.toString(), null, new RowMapper<AgentInfo>() {
			@Override
			public AgentInfo mapRow(ResultSet rs, int rowNum) throws SQLException {
				AgentInfo agent = new AgentInfo();
				agent.setHostName(rs.getString("hostName"));
				agent.setIP(rs.getString("IP"));
				agent.setAddress(rs.getString("IP"));
				agent.setSendEvents(rs.getString("sendEvents"));
				agent.setStartTime(rs.getString("startTime"));
				agent.setStatus(rs.getString("status"));
				agent.setSendFlow(rs.getString("sendFlow"));
				agent.setSendFlow2G(rs.getString("sendFlow2G"));
				agent.setCreateTime(rs.getString("createTime"));
				return agent;
			}
		});
		return (agents != null && agents.size() > 0) ? agents : null;
	}

实现基本跟JDBC一样,比较容易。查询到结果就可以在JSP页面显示了。


就像很多web项目一样,有很多数据源共存的情况,在这里也是可以的。我的项目使用到了mybatis ,大家看下是如何配置的:

<!-- <context:property-placeholder location="classpath:config/jdbc.properties" /> -->
	
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		<!-- <property name="driverClassName" value="${jdbc.driverClassName}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" /> -->
		<property name="driverClassName" value="com.ibm.db2.jcc.DB2Driver" />
		<property name="url" value="jdbc:db2://PC201306291646:50000/emsdb:currentSchema=EMSUSR;" />
		<property name="username" value="db2admin" />
		<property name="password" value="db2" />
	</bean>
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="configLocation" value="classpath:config/mybatis.xml"></property>
	</bean>
	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>
	<tx:annotation-driven transaction-manager="transactionManager" />
	<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg index="0" ref="sqlSessionFactory" />
	</bean>
	<mybatis:scan base-package="com.coreware.dao" />





然后接下来就是配置phoenix数据源的步骤,不知道为何两个数据源共存的话,通过配置文件jdbc.properties来配置数据库接口是获取不到的,只能在这里写明

${jdbc.driverClassName} 是获取不到值的,只能写 com.ibm.db2.jcc.DB2Driver。具体原因还有待研究。


好了,spring+phoenix+hbase实现就介绍到这里!



你可能感兴趣的:(spring,hbase,Phoenix)