一 数据加载服务一、目标二、步骤二 离线推荐服务2.1 基于统计性算法一、目标二、步骤2.2 基于隐语义模型(LFM)的协同过滤推荐算法(ALS)一、目标二、步骤2.3 基于 ElasticSearch 的内容推荐算法一、目标二、步骤2.4 基于内容的推荐服务--电影标签三 实时推荐服务3.1 推荐算法解析3.2 实时推荐算法的实现过程3.3 日志的预处理四 综合业务服务4.1 后台架构4.2 Spring 框架搭建4.3 API 接口规划五 用户可视化服务5.1 前端框架搭建5.2 建立与运行项目5.2.1 建立项目骨架5.2.2 添加项目依赖5.2.3 建立模块、组件与服务5.2.4 调试项目5.2.5 发布项目六 项目重构6.1 核心模型提取6.2 经过配置的方式来获取硬编码的值6.3 项目打包6.3.1 AngularJS 前端文件打包6.3.2 businessServer 下的 java web 项目的打包方式6.3.3 核心模型 项目的打包方式6.3.4 recommender 下的后端文件打包方式6.4 系统部署javascript
【MongoDB】
1)须要将 Movie【电影数据集】数据集加载到 MongoDB 数据库中的 Movie 表中。
2)须要将 Rating【用户对电影的评分数据集】数据集加载到 MongoDB 数据库中的 Rating 表中。
3)须要将 Tag【用户对电影的标签数据集】数据集加载到 MongoDB 数据库中的 Tag 表中。css
【ElasticSearch】
1)须要将 Movie【电影数据集】加载到 ElasticSearch 名叫 Movie 的 Index 中。
2)须要将 Tag 数据和 Movie 数据作左外链接。html
1)先新建一个 Maven 项目,将依赖添加好。
2)分析数据集 Movie、Rating、Tag。
3)新建 case class Movie、Rating、Tag、MongoConfig、ESConfig。
4)加载数据集。
5)将 RDD 数据集装换成 DataFrame。
6)将 DF 加载到 MongoDB 中:
1. 将原来的 Collection 所有删除
2. 经过 DF 的 write 方法将数据写入
3. 建立数据库索引
4. 关闭 MongoDB 链接
7)将 DF 加载到 ElasticSearch 中:
1. 将存在的 Index 删除掉,而后建立新的 Index
2. 经过 DF 的 write 方法将数据写入
8)关闭 Spark 集群前端
一、优质电影
1)获取全部历史数据中评分次数最多的电影的集合,统计每一个电影的评分数 --> RateMoreMoviesjava
二、热门电影
1)按照月来统计,这个月中评分次数量最多的电影咱们认为是热门电影,统计每月中的每一个电影的评分数量 --> RateMoreRecentlyMoviesjquery
三、统计电影的平均评分
1)将Rating数据集中全部的用户评分数据进行平均,计算每个电影的平均评分 --> AverageMoviesScoreangularjs
四、统计每种类别电影的 TOP10 电影
1)将每种类别的电影中评分最高的 10 个电影分别计算出来 --> GenresTopMovies web
一、新建一个项目 StaticticsRecommender,配置好你的依赖
二、统计全部历史数据中电影的评分个数
1)经过 Rating 数据集,用 mid 进行 group by 操做,count 计算总数
三、统计以月为单位的电影的评分个数
1)须要注册一个 UDF 函数,用于将 Timestamp 这种格式的数据转换成 yyyyMM 这个格式 --> SimpleDateFormat
2)须要将 RatingDF 转换成新的 RatingOfMouthDF【只有日期数据发生了转换】
3)经过 group by yearmonth, mid 来完成统计
四、统计每一个电影评分得分
1)经过 Rating 数据集,用户 mid 进行 group by 操做,avg 计算评分
五、统计每种类别中评分最高的 10 个电影
1)须要经过 JOIN 操做将电影的平均评分数据和 Movie 数据集进行合并,产生 MovieWithScore 数据集
2)须要将电影的类别数据转换成 RDD --> GenresRDD
3)将 GenresRDD 和 MovieWithScore 数据集进行笛卡尔积,产生一个 N * M 行的数据集
4)经过过滤操做,过滤掉电影的真实类别和 GenresRDD 中的类别不匹配的电影
5)经过 Genres 做为 Key,进行 groupByKey 操做,将相同电影类别的电影进行汇集
6)经过排序和提取,获取评分最高的 10 个电影 --> genresTopMoviesDF
7)将结果输出到 MongoDB 中redis
统计每种类别中评分最高的 10 个电影图解:算法
一、训练 ALS 推荐模型(ALS:交替最小二乘法)
二、计算用户电影推荐矩阵
三、计算电影类似度矩阵
一、训练 ALS 推荐模型
1)须要构建 RDD[Rating] 类型的训练集数据
2)直接经过 ALS.train 方法来进行模型训练
二、计算用户电影推荐矩阵
1)生成 userMovies --> RDD[(Int,Int)]
2)经过 ALS 模型的 predict 方法来预测评分
3)将数据经过 groupByKey 处理后排序,取前 N 个做为推荐结果
三、计算电影类似度矩阵
1)获取电影的特征矩阵,转换成 DoubleMatrix
2)电影的特征矩阵之间作笛卡尔积,经过余弦类似度计算两个电影的类似度
3)将数据经过 GroupBy 处理后,输出
四、ALS 模型的参数选择
1)经过计算 ALS 的均方根偏差来判断参数的优劣程度
基于内容的推荐一般是给定一篇文档信息,而后给用户推荐与该文档相识的文档。Lucene 的 api 中有实现查询文章类似度的接口,叫 MoreLikeThis。Elasticsearch 封装了该接口,经过 Elasticsearch 的 More like this 查询接口,咱们能够很是方便的实现基于内容的推荐。
在本项目中 ElasticSearch 除了提供基础的模糊检索功能外,主要提供了电影之间基 于More like this 查询类似度之间的功能,使电影依据演员、导演、名称、描述、标签等进行类似度计算,返回查询电影的类似电影集合。
因为该功能已有 ES 进行实现,故该功能不用提早计算或者实时计算,只是须要在业务服务器查询推荐集合的时候,将结果集按照业务规则进行合并便可。
核心算法以下:
// 基于内容的推荐算法
private List<Recommendation> findContentBasedMoreLikeThisRecommendations(int mid, int maxItems) {
MoreLikeThisQueryBuilder query = QueryBuilders.moreLikeThisQuery(
new String[]{"id"},
new String[]{"name", "descri", "genres", "actors", "directors", "tags"},
new MoreLikeThisQueryBuilder.Item[]{new MoreLikeThisQueryBuilder.Item(Constant.ES_INDEX, Constant.ES_MOVIE_TYPE, String.valueOf(mid))});
return parseESResponse(esClient.prepareSearch().setQuery(query).setSize(maxItems).execute().actionGet());
}
private List<Recommendation> parseRecs(Document document, int maxItems) {
List<Recommendation> recommendations = new ArrayList<>();
if (null == document || document.isEmpty())
return recommendations;
ArrayList<Document> recs = document.get("recs", ArrayList.class);
for (Document recDoc : recs) {
recommendations.add(new Recommendation(recDoc.getInteger("rid"), recDoc.getDouble("r")));
}
Collections.sort(recommendations, new Comparator<Recommendation>() {
@Override
public int compare(Recommendation o1, Recommendation o2) {
return o1.getScore() > o2.getScore() ? -1 : 1;
}
});
return recommendations.subList(0, maxItems > recommendations.size() ? recommendations.size() : maxItems);
}
原始数据中的 tag 文件,是用户给电影打上的标签,这部份内容想要直接转成评分并不容易,不过咱们能够将标签内容进行提取,获得电影的内容特征向量
,进而能够经过求取类似度矩阵
。这部分能够与实时推荐系统直接对接,计算出与用户当前评分电影的类似电影,实现基于内容的实时推荐。为了不热门标签对特征提取的影响,咱们还能够经过 TF-IDF 算法(词频-逆文档频率)
对标签的权重进行调整,从而尽量地接近用户偏好。
核心算法以下:
package com.atguigu.content
import org.apache.spark.SparkConf
import org.apache.spark.ml.feature.{HashingTF, IDF, Tokenizer}
import org.apache.spark.ml.linalg.SparseVector
import org.apache.spark.sql.SparkSession
import org.jblas.DoubleMatrix
// 须要的数据源是电影内容信息
case class Movie(mid: Int, name: String, descri: String, timelong: String, issue: String,
shoot: String, language: String, genres: String, actors: String, directors: String)
case class MongoConfig(uri: String, db: String)
// 定义一个基准推荐对象
case class Recommendation(mid: Int, score: Double)
// 定义电影内容信息提取出的特征向量的电影类似度列表
case class MovieRecs(mid: Int, recs: Seq[Recommendation])
object ContentRecommender {
// 定义表名和常量
val MONGODB_MOVIE_COLLECTION = "Movie"
val CONTENT_MOVIE_RECS = "ContentMovieRecs"
def main(args: Array[String]): Unit = {
val config = Map(
"spark.cores" -> "local[*]",
"mongo.uri" -> "mongodb://hadoop102:27017/recommender",
"mongo.db" -> "recommender"
)
// 建立一个 SparkConf 对象
val sparkConf = new SparkConf().setMaster(config("spark.cores")).setAppName("ContentRecommender")
// 建立一个 SparkSession 对象
val spark = SparkSession.builder().config(sparkConf).getOrCreate()
// 声明一个隐式的配置对象
implicit val mongoConfig = MongoConfig(config("mongo.uri"), config("mongo.db"))
// 在对 DataFrame 和 Dataset 进行许多操做都须要这个包进行支持
import spark.implicits._
// 加载数据,并做预处理
val movieTagsDF = spark.read
.option("uri", mongoConfig.uri)
.option("collection", MONGODB_MOVIE_COLLECTION)
.format("com.mongodb.spark.sql")
.load()
.as[Movie]
.map { // 提取 mid,name,genres 三项做为原始的内容特征,分词器默认分隔符是空格
x => (x.mid, x.name, x.genres.map(c => if (c == '|') ' ' else c))
}
.toDF("mid", "name", "genres")
.cache()
// TODO:从内容信息中提取电影特征的特征向量
// 建立一个分词器,默认按照空格分词
val tokenizer = new Tokenizer().setInputCol("genres").setOutputCol("words")
// 用分词器对原始数据进行转换,生成新的一列words
val wordsData = tokenizer.transform(movieTagsDF)
// 引入 HashingTF 工具,该工具能够将词语序列转换成对应的词频
val hashingTF = new HashingTF().setInputCol("words").setOutputCol("rawFeatures").setNumFeatures(50)
val featurizeData = hashingTF.transform(wordsData)
// 测试
// wordsData.show()
// featurizeData.show()
// featurizeData.show(truncate = false) // 不压缩显示
// 引入 IDF 工具,该工具能够获得 IDF 模型
val idf = new IDF().setInputCol("rawFeatures").setOutputCol("features")
// 训练 IDF 模型,获得每一个词的逆文档频率
val idfModel = idf.fit(featurizeData)
// 用 IDF 模型对原数据进行处理,获得文档中每一个词的 TF-IDF,做为新的特征向量
val rescaleData = idfModel.transform(featurizeData)
// 测试
// rescaleData.show(truncate = false) // 不压缩显示
val movieFeatures = rescaleData.map(
row => (row.getAs[Int]("mid"), row.getAs[SparseVector]("features").toArray)
).rdd.map(
x => (x._1, new DoubleMatrix(x._2))
)
// 测试
// movieFeatures.collect().foreach(println)
// 对全部电影两两计算它们的类似度,先作笛卡尔积
val movieRecs = movieFeatures.cartesian(movieFeatures)
.filter {
// 把本身跟本身的配对过滤掉
case (a, b) => a._1 != b._1
}
.map {
case (a, b) => {
val simScore = this.consinSim(a._2, b._2)
(a._1, (b._1, simScore))
}
}
.filter(_._2._2 > 0.6) // 过滤出类似度大于 0.6 的
.groupByKey()
.map {
case (mid, recs) => MovieRecs(mid, recs.toList.sortWith(_._2 > _._2).map(x => Recommendation(x._1, x._2)))
}
.toDF()
// 把结果写入对应的 MongoDB 表中
movieRecs.write
.option("uri", mongoConfig.uri)
.option("collection", CONTENT_MOVIE_RECS)
.mode("overwrite")
.format("com.mongodb.spark.sql")
.save()
spark.stop()
}
// 求两个向量的余弦类似度
def consinSim(movie1: DoubleMatrix, movie2: DoubleMatrix): Double = {
movie1.dot(movie2) / (movie1.norm2() * movie2.norm2()) // l1范数:向量元素绝对值之和;l2范数:即向量的模长(向量的长度),向量元素的平方和再开方
}
}
实时推荐算法的前提:
1.在 Redis 集群中存储了每个用户最近对电影的 K 次评分。实时算法能够快速获取。
2.离线推荐算法已经将电影类似度矩阵提早计算到了 MongoDB 中。
3.Kafka 已经获取到了用户实时的评分数据。
算法过程以下:
实时推荐算法输入为一个评分<userId, mid, rate, timestamp>
,而执行的核心内容包括:获取 uid 最近 K 次评分、获取 mid 最类似 K 个电影、计算候选电影的推荐优先级、更新对 uid 的实时推荐结果。
一、目标
1)根据埋点数据信息,格式化日志
二、步骤
1)构建 LogProcessor 实现 Processor 接口,实现对于数据的处理
2)构建 StreamsConfig 配置数据
3)构建 TopologyBuilder 来设置数据处理拓扑关系
4)构建 KafkaStreams 来启动整个处理
![]()
后台服务经过 Spring 框架进行建立,主要负责后台数据和前端业务的交互。项目主要分为 REST 接口服务层、业务服务层、业务模型以及工具组件层等组成。
REST 接口服务层:主要经过 Spring MVC 为 UI 提供了通信接口,主要包括用户接口、推荐接口、评分接口、查询接口、标签接口以及统计接口。
业务服务层:主要实现了总体系统的业务逻辑,提供了包含电影相对应操做的服务、评分层面的服务、推荐层面的服务、标签层面的服务以及用户层面的服务。
业务模型方面:将推荐、业务请求以及具体业务数据进行模型建立。
工具组件层面:提供了对 Redis、ES、MongoDB 的客户端以及项目常量定义。
一、添加相对应对的依赖包。
二、建立 application.xml 配置文件,配置 application context。
三、建立 application-servlet.xml 配置文件,用于配置 web application context。
四、配置 web.xml 将 application context 和 web application context 整合。
在 MovieRecommendSystem 项目下新建一个 Module 项目 businessServer,而后在 MovieRecommendSystem\businessServer\src\main 目录下新建 webapp 目录,此时可能会出现一个问题:新建的 webapp 文件夹不能被识别(没有小蓝点),解决问题连接:https://www.cnblogs.com/chenmingjun/p/10920548.html
注意
:若是导入他人已经写好的项目时,发现导入的项目与本身的整个项“格格不入”时,这时能够删除整个项目在 IDEA 中的配置数据,其文件夹是 .idea,而后删除缓存索引数据并重启 IEDA(File -> Invalidate Caches / Restart…),再从新加载该项目,从新生成新的 .idea 文件 和 缓存索引数据。便可解决问题!
依赖 pom.xml 文件内容以下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>MovieRecommendSystem</artifactId>
<groupId>com.atguigu</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>businessServer</artifactId>
<packaging>war</packaging><!-- 说明是一个 web 项目,打的包不是 jar,而是 war 包 -->
<properties>
<log4j2.version>2.9.1</log4j2.version>
<spring.version>4.3.6.RELEASE</spring.version>
<spring.data.jpa.version>1.11.0.RELEASE</spring.data.jpa.version>
<jackson.version>2.8.6</jackson.version>
<servlet.version>3.0.1</servlet.version>
<es.version>5.6.2</es.version>
<mongo.version>3.5.0</mongo.version>
<jedis.version>2.9.0</jedis.version>
</properties>
<dependencies>
<dependency>
<!-- log4j2 -->
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<!-- 用于 Servlet 的开发 -->
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>transport</artifactId>
<version>${es.version}</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver</artifactId>
<version>${mongo.version}</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${jedis.version}</version>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Spring End -->
<!-- fasterxml 用于 JSON 和对象之间的转换 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- fasterxml end -->
</dependencies>
<build>
<finalName>BusinessServer</finalName>
<plugins>
<plugin>
<!-- 该插件用于在 Maven 中提供 Tomcat 运行环境,你还可使用 Jetty 插件 -->
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<!-- 指定运行后能够访问的端口 -->
<port>8888</port>
<!-- 指定了运行时的根目录 -->
<path>/</path>
<!-- 当你更改了代码后,tomcat 自动从新加载 -->
<contextReloadable>true</contextReloadable>
</configuration>
</plugin>
</plugins>
</build>
</project>
整个目录结构以下图所示:
配置文件内容以下:(注意:本博主配置文件的内容是整个项目的)
MovieRecommendSystem\businessServer\src\main\resources\application.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd">
<!-- 用于让 Spring 去扫描整个 com.atguigu.business 目录下的代码 -->
<context:component-scan base-package="com.atguigu.business"/>
<context:property-placeholder location="classpath:recommend.properties" ignore-unresolvable="true"/>
<!-- 用于 JSON 和对象之间的转换,这里是父容器 -->
<bean id="objectMapper" class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="featuresToEnable">
<array>
<util:constant static-field="com.fasterxml.jackson.databind.SerializationFeature.CLOSE_CLOSEABLE"/>
</array>
</property>
<property name="featuresToDisable">
<array>
<util:constant static-field="com.fasterxml.jackson.databind.SerializationFeature.FAIL_ON_EMPTY_BEANS"/>
</array>
</property>
</bean>
</beans>
MovieRecommendSystem\businessServer\src\main\resources\log4j.properties
log4j.rootLogger=INFO, file, stdout
# write to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %5p --- [%50t] %-80c(line:%5L) : %m%n
# write to file
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=D:\\learn\\JetBrains\\workspace_idea\\MovieRecommendSystem\\businessServer\\src\\main\\log\\agent.log
log4j.appender.file.MaxFileSize=1024KB
log4j.appender.file.MaxBackupIndex=1
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %5p --- [%50t] %-80c(line:%6L) : %m%n
#log4j.appender.syslog=org.apache.log4j.net.SyslogAppender
#log4j.appender.syslog=com.c4c.dcos.commons.logger.appender.SyslogAppenderExt
#log4j.appender.syslog.SyslogHost= 192.168.25.102
#log4j.appender.syslog.Threshold=INFO
#log4j.appender.syslog.layout=org.apache.log4j.PatternLayout
#log4j.appender.syslog.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %5p --- [%20t] %-130c:(line:%4L) : %m%n
#demo|FATAL|2014-Jul-03 14:34:34,194|main|com.c4c.logdemo.App:(line:15)|send a log
MovieRecommendSystem\businessServer\src\main\resources\log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="error">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
businessServer\src\main\resources\recommend.properties
#elasticSearch
es.host=hadoop102
es.port=9300
es.cluster.name=my-application
#Mongodb
mongo.host=hadoop102
mongo.port=27017
#Redis
redis.host=hadoop102
MovieRecommendSystem\businessServer\src\main\webapp\WEB-INF\application-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd">
<!-- 用于让 Spring 去扫描整个 com.atguigu.business.rest 目录下的代码 -->
<context:component-scan base-package="com.atguigu.business.rest"/>
<!-- 可以让 web 应用启用 web 方面的注解 -->
<mvc:annotation-driven>
<!-- 给 MVC 框架注册消息装换器 -->
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
<bean class="org.springframework.http.converter.BufferedImageHttpMessageConverter"/>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
<!-- 用于 JSON 的转换 -->
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
</list>
</property>
<!-- 子容器引用父容器中的 bean 便可,不用子容器本身建立了,父容器 application.xml -->
<property name="objectMapper" ref="objectMapper" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!-- 内容协商解析器 -->
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="contentNegotiationManager" ref="contentNegotiationManager"/>
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
</list>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>
</list>
</property>
<property name="useNotAcceptableStatusCode" value="true"/>
</bean>
<bean id="contentNegotiationManager"
class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="defaultContentType" value="application/json"/>
<property name="mediaTypes">
<value>
json=application/json
xml=text/xml
html=text/html
</value>
</property>
</bean>
<!-- 用于处理静态文件用的 -->
<mvc:default-servlet-handler/>
<!-- 跨域访问的支持 -->
<mvc:cors>
<mvc:mapping path="/**" allowed-origins="*" allow-credentials="true" max-age="1800" allowed-methods="GET,POST,OPTIONS"/>
</mvc:cors>
</beans>
MovieRecommendSystem\businessServer\src\main\webapp\WEB-INF\web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name>recommender</display-name>
<!-- 用于指定 Spring Application Context 的配置文件路径 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application.xml</param-value>
</context-param>
<listener>
<!-- Spring 用于监听 Serlvet 容器的启动,进而启动 Spring Application Context -->
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置你的 Servlet -->
<servlet>
<servlet-name>recommend</servlet-name>
<!-- spring 提供的 Servlet 的实现 -->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<!-- 指定了 Web Application Context 的配置文件 -->
<param-value>/WEB-INF/application-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>recommend</servlet-name>
<!-- 要让 recommend 这个 Servlet 处理全部的 URL -->
<url-pattern>/</url-pattern>
</servlet-mapping>
<welcome-file-list>
<!-- 定义你的欢迎文件 -->
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
MovieRecommendSystem\businessServer\src\main\webapp\index.html
<!--这是别人访问你的网站是看到的主页面的HTML文件。大多数状况下你都不用编辑它。
在构建应用时,CLI 会自动把全部 js 和 css 文件添加进去,因此你没必要在这里手动添加任何 <script> 或 <link> 标签。-->
<!DOCTYPE html>
<html>
<head>
<base href="/">
<title>MovieLens海外电影推荐系统</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<movie-app>加载中...</movie-app>
<script type="text/javascript" src="inline.bundle.js"></script>
<script type="text/javascript" src="polyfills.bundle.js"></script>
<script type="text/javascript" src="scripts.bundle.js"></script>
<script type="text/javascript" src="styles.bundle.js"></script>
<script type="text/javascript" src="vendor.bundle.js"></script>
<script type="text/javascript" src="main.bundle.js"></script>
</body>
<script type="application/javascript">
document.oncontextmenu = function () {
return false;
}
document.onkeydown = function (event) {
var e = event || window.event || arguments.callee.caller.arguments[0];
if (e && e.keyCode == 116) {
return false;
}
}
</script>
</html>
AngularJS 框架:
电影推荐系统前端框架:
详细文档参考:https://angular.cn/guide/quickstart
在 CMD 中相对应的目录中执行:ng new my-app,my-app 为项目的名称,能够任意起名称。
【Src主文件夹】
你的应用代码位于 src 文件夹中。全部的 Angular 组件、模板、样式、图片以及你的应用所需的任何东西都在那里。
这个文件夹以外的文件都是为构建应用提供支持用的。
【根目录文件夹】
src/ 文件夹是项目的根文件夹之一。 其它文件是用来帮助你构建、测试、维护、文档化和发布应用的。它们放在根目录下,和 src/ 平级。
在 CMD 中项目目录中执行:npm install bootstrap --save,添加 bootstrap 依赖。
在 CMD 中项目目录中执行:npm install jquery --save,添加bootstrap 依赖。
在 CMD 中项目目录中执行:npm install systemjs --save,添加 bootstrap 依赖。
在 CMD 中项目目录中执行:ng g module AppRouting,来建立新模块。
在 CMD 中项目目录中执行:ng g component home,来建立新组件。
在 CMD 中项目目录中执行:ng g service service/login,来建立新服务组件。
在 CMD 中项目目录中执行:ng serve –p 3000,启动整个应用程序。
访问:http://localhost:4200
当你修改了后台代码的时候,浏览器自动 Reload。
在 CMD 中项目目录中执行:ng build,来打包发布整个应用程序。
会在目录下生成 dist 文件夹,该文件夹就是最终的发布程序。
一、提取公共的模型
1)将全部模型和共有的常量定义提取到一个 Module 里面。
2)将包含模型和常量定义的 Module 引入到相应的模块里面。
3)使用模型 Module 里面的定义替代模块中的相应定义。
二、修改程序中的硬编码
1)经过配置的方式来获取硬编码的值。
一、针对每个项目分别编辑相对应的打包过程
二、运行 Root 项目下的 package 阶段,进行打包
maven 调用 cmd 命令的的插件
website 的 pom.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>MovieRecommendSystem</artifactId>
<groupId>com.atguigu</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>website</artifactId>
<build>
<finalName>website</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<finalName>website</finalName>
<descriptors>
<descriptors>assembly/dependencies.xml</descriptors>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<id>ng-build</id>
<phase>prepare-package</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>ng</executable>
<arguments>
<argument>build</argument>
</arguments>
<workingDirectory>${basedir}/website/</workingDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
dependencies.xml
<assembly>
<id>release</id>
<formats>
<format>tar.gz</format>
</formats>
<!-- 设置要打包的文件 -->
<fileSets>
<fileSet>
<directory>images</directory>
<outputDirectory>./images</outputDirectory>
<includes>
<include>**</include>
</includes>
</fileSet>
</fileSets>
<fileSets>
<fileSet>
<directory>website/dist</directory>
<outputDirectory>./</outputDirectory>
<includes>
<include>**</include>
</includes>
</fileSet>
</fileSets>
</assembly>
在要打包的项目中的 pom.xml 文件中只须要添加如下内容:
MovieRecommendSystem\businessServer\pom.xml
<packaging>war</packaging><!-- 说明是一个 web 项目,打的包不是 jar,而是 war 包 -->
不用任何设置,默认打的是 jar 包,若是单独打 recommender 下项目的包,须要先打核心模型包。
在每个要打包的子项目中的 pom.xml 文件中添加如下内容:
例如:MovieRecommendSystem\recommender\DataLoader\pom.xml
<build>
<finalName>dataLoader</finalName><!-- 名字任意起 -->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>com.atguigu.recommender.DataLoader</mainClass><!-- 修改成对应的主类 -->
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
在 父 的 pom.xml 文件中,对于不须要打进 jar 中的依赖,使用 <scope>provided</scope>
配置便可。以下:
MovieRecommendSystem\recommender\pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>MovieRecommendSystem</artifactId>
<groupId>com.atguigu</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>recommender</artifactId>
<packaging>pom</packaging>
<modules>
<module>DataLoader</module>
<module>StatisticsRecommender</module>
<module>OfflineRecommender</module>
<module>StreamingRecommender</module>
<module>ContentRecommender</module>
<module>KafkaStreaming</module>
</modules>
<!-- 仅申明子项目共有的依赖,并不引入,若是子项目须要此依赖,那么子项目须要声明方可引入 -->
<dependencyManagement>
<dependencies>
<!-- 引入 Spark 相关的 Jar 包 -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>${spark.version}</version>
<!-- provided 若是存在,那么运行时该 jar 包不存在,也不会打包到最终的发布版本中,只是编译期有效 -->
<!--<scope>provided</scope>-->
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.11</artifactId>
<version>${spark.version}</version>
<!-- provided 若是存在,那么运行时该 jar 包不存在,也不会打包到最终的发布版本中,只是编译期有效 -->
<!--<scope>provided</scope>-->
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming_2.11</artifactId>
<version>${spark.version}</version>
<!-- provided 若是存在,那么运行时该 jar 包不存在,也不会打包到最终的发布版本中,只是编译期有效 -->
<!--<scope>provided</scope>-->
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-mllib_2.11</artifactId>
<version>${spark.version}</version>
<!-- provided 若是存在,那么运行时该 jar 包不存在,也不会打包到最终的发布版本中,只是编译期有效 -->
<!--<scope>provided</scope>-->
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-graphx_2.11</artifactId>
<version>${spark.version}</version>
<!-- provided 若是存在,那么运行时该 jar 包不存在,也不会打包到最终的发布版本中,只是编译期有效 -->
<!--<scope>provided</scope>-->
</dependency>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
<!-- provided 若是存在,那么运行时该 jar 包不存在,也不会打包到最终的发布版本中,只是编译期有效 -->
<!--<scope>provided</scope>-->
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<!-- 父项目已声明该 plugin,子项目在引入的时候,不用声明版本和已经声明的配置 -->
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
一、项目的打包
二、前端的部署
1)将前端的包 website-release.tar.gz 部署在 /var/www/html 中
三、业务服务器的部署
1)将后台的代码解压复制到 tomcat/webapps/ROOT 下
四、流式计算的部署
1)启动 Kafka
2)编辑 Flume 配置文件
3)启动 Flume
4)启动 StreamingRecommender 程序
五、离线计算的部署 1)azkaban 启动 2)打包统计服务和离线推荐服务,并放到 Linux 目录下 3)编写 azkaban 的 job 文件,去调度两个 jar 包