版本:Spring3+Struts2+Hibernate3+Hadoop1.0.4+Mahout0.7+Mysql5
系统使用Spring3+Struts2+Hibernate3架构,可以在http://download.csdn.net/detail/fansy1990/6935279下载。运行参考:
1. 打开mysql,增加blog数据库;修改src下面的jdbc.properties文件的用户名和密码;
2. 修改blog.xml(和Readme.txt同目录)的docBase为本地目录,放在tomcat的conf\Catalina\localhost目录下面;
3. 修改applicationContext.xml文件中的自动生成表的配置(hibernate.hbm2ddl.auto),第一次启动为create,后面改为update;
4. 启动服务器,运行test目录下面的DataGenerator.java、运行src/data目录下面的UserGenerate.java
即可插入test用户信息以及云平台信息(需要修改UserGenerate.java中云平台相关配置);
5. 拿修改后的JobTracker.class替换Hadoop云平台中hadoop-core-1.0.4.jar中对应的文件(每个节点都需要),这个修改后的文件把集群的启动时间写入HDFS文件,方便后面的读取;
博客推荐系统是一个为用户推荐博客的系统,使用Mahout的FP关联规则算法进行博客相关信息挖掘,生成知识库。然后根据知识库以及用户订阅博客来进行其他博客推荐。
博客推荐系统思路如下:首先把http://fimi.ua.ac.be/data/网站上面的webdocs.dat.gz数据作为基础数据,这些基础数据是一种类似购物篮的数据,我们可以把每行数据看做是一个用户的博客订阅记录(实际上这个数据是用户浏览的网页id),里面的每一项是用户订阅的博客id。这样这份数据就可以使用fp关联规则来挖掘得到频繁项集,即可以得到博客id两两之间的关联度,类似这样的数据 [blogId101 blogId103 500],前面两项是博客id,后面的500是博客blogId101和blogid103的关联度。然后把这样的数据存入数据库中的knowledge表中。登录系统的用户会有一个用户表,这个用户表存储了用户的订阅记录以及推荐记录(默认都为空,当用户订阅博客后,会填充订阅记录一栏数据)。当要对用户进行博客推荐时,首先就会查找这个用户的订阅记录,然后根据这个订阅记录中的博客id在knowledge表中找出与之关联的博客id以及对应的关联度,存入一个map(博客id为key,关联度为value)中。如果博客101和103关联,且关联度为20,博客102和103关联,关联度是30,并且博客101和102都是当前用户订阅的,那么map中存储的博客103对应的关联度就是两者的叠加,即50。map变量的值就是按照这样的方式更新的,最后,map按照关联度(也就是value)从大到小排列,取前10个即为用户推荐的博客。
下面按照系统功能来分析实现思路:
1. 首先看系统登录后的界面:
图1
用户访问系统发布地址后,首先会打开登录界面,在登录界面用户可以输入用户名和密码进行登录,或者注册。登录会查找数据库用户名和密码是否匹配,如果匹配,那么就会在UserAction中的Session放入username和level(权限),level是为了在图1中的建立知识库模块使用过滤器过滤权限不够的请求。
注册直接检测用户名是否存在,不存在则正常更新用户表,同时用户表的权限值默认注册是为1,管理员权限级别为0。
2. 用户信息模块
用户信息模块包含两个功能:修改密码,订阅查看。
修改密码主要是修改用户表中的密码字段,不过修改的时候会要求用户提供当前密码,然后和数据库做比对,如果比对正确才给用户更改密码。
订阅查看则主要是显示用户表的订阅字段数据,由于订阅字段数据是以字符串存储的(类似[blogId101 blogid102 ...]),在前台展示之前需要把字符串分隔为数组,然后在前台展示,所以这里需要解析字符串。在前台展示使用struts2的标签,一个实例如下所示:
<table border="1"> <thead> <tr> <td id="blogId">博客ID</td> <td id="blogInfo">博客简要描述</td> </tr> </thead> <s:iterator value="orderedBlogs" id="array"> <tr> <td> <s:property value="array"/> </td> <td>******</td> </tr> </s:iterator> </table>3. 建立知识库模块
建立知识库模块首先会经过LoginFilter进行权限验证,验证不通过的请求将会返回无权限页面,LoginFilter需要要web.xml中配置,配置代码如下:
<filter> <filter-name>LoginFilter</filter-name> <filter-class>util.LoginFilter</filter-class> </filter> <filter-mapping> <filter-name>LoginFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>建立知识库模块分为两个部分,导入数据和调用算法进行监控。
导入数据功能,用户需要提供服务器(一般为运行tomcat的机器)的数据目录文件,然后即可上传。数据可以采用上面给出链接的数据,不过如果仅仅是为了测试,那么可以不用那么多数据的,可以对数据进行处理,随机抽取一部分数据即可。上传部分代码如下所示:
/** * 上传数据到云平台 * @param localFile * @param deltaOrAll * @return 上传路径; */ public static String upload(String localFile,String deltaOrAll){ FileSystem fs=null; String output=UPLOAD_PATH+System.currentTimeMillis(); Path in=new Path(localFile); Path out=new Path(output); try { fs = FileSystem.get(URI.create(output),getConf()); if("all".equals(deltaOrAll)){ HadoopUtil.delete(getConf(), new Path(UPLOAD_PATH)); } fs.copyFromLocalFile(in,out); }catch(Exception e){ System.err.println(e.getMessage()); try { HadoopUtil.delete(getConf(), out); } catch (IOException e1) { e1.printStackTrace(); } output=null; }finally{ try { if(fs!=null){ fs.close(); } } catch (IOException e) { System.err.println(e.getMessage()); } } return output; }调用算法模块仍旧采用之前系统的步骤:首先在参数界面输入参数,然后提到到一个action中,这个action会启动一个线程启动云平台Fp关联规则算法,然后连接到另外一个action中,另外的这个action会去根据云平台的信息,获得任务运行状态。然后返回给用户。
在获得任务运行状态的时候是参考下面的思路设计的。首先说明,任务运行状态设计了一个实体类(JobInfo),包含5个字段,分别是:jobId、jobName,mapProgerss、redProgress、runStatus,并且如果知道当前任务运行的jobId,那么就可以得到当前任务的jobName、mapProgress、redProgress、runStatus。在云平台任务列表中找出最后一次运行任务的jobId(如果云平台刚启动,那么这个列表是没有的,就需要通过其他方式来获得,后面细说)。然后来推算出下面任务运行的jobId,FP关联规则算法一共三个MR,所以把找出的最后一个jobId分别加3次即可得到一个List<JobInfo>的list,其jobId有值,其他默认为空。获得云平台最后一次任务jobId的代码如下:
MonitorUtil:
public static void initialMonitorJobs(int jobNums) throws IOException{ JobStatus[] jobStatusAll=HadoopUtils.getJobClient().getAllJobs(); JobStatus jobStatus=null; int id =0; String jobIden=""; /** * 防止当前云平台是第一次启动,这个时候没有任务列表,获取的jobStatus是空; */ if(jobStatusAll==null||jobStatusAll.length<=0){ //修改TaskTracker代码,把集群启动时间写入hdfs,然后在这里读取出来 id=0; jobIden=readJTStartTime(); }else{ jobStatus=jobStatusAll[jobStatusAll.length-1]; id=jobStatus.getJobID().getId(); jobIden=jobStatus.getJobID().getJtIdentifier(); } log.info("initial monitorJobs with the start jobID :"+id); monitorJobs=new LinkedHashMap<String,JobInfo>(); String jobId=""; for(int i=0;i<jobNums;i++){ jobId= new JobID(jobIden,id+1+i).toString(); monitorJobs.put(jobId, new JobInfo(jobId)); } lastJobId=jobId; log.info("initial monitor jobs map done !!!"); }HadoopUtils:
public static JobClient getJobClient(){ if(jobClient==null){ try { InetSocketAddress jobTracker=new InetSocketAddress(getHost(),getJobtrackerPort()); jobClient=new JobClient(jobTracker, getConf()); } catch (IOException e) { e.printStackTrace(); } } return jobClient; }
分享,成长,快乐
转载请注明blog地址:http://blog.csdn.net/fansy1990