电商日志分析项目 – 05 改用Spark进行日志分析

什么是 Spark

Apache Spark是一个快速通用的集群计算系统,是一种与Hadoop相似的开源集群计算环境,但是Spark在某些工作负载方面表现得更加优越。它提供了Java. Scala. Python和R的高级API,以及一个支持通用的执行图计算的优化引擎。它还支持一组丰富的高级工具,包括使用SQL进行结构化数据处理的Spark SQL. 用于机器学习的MLlib. 用于图处理的GraphX,以及用于实时流处理的Spark Streaming。

image-20220507004828231

Spark的主要特点:

  1. 快速
    与MapReduce相比,Spark可以支持包括Map和Reduce在内的更多操作,这些操作相互连接形成一个有向无环图(Directed Acyclic Graph,简称DAG),各个操作的中间数据则会被保存在内存中。因此处理速度比MapReduce更加快。Spark通过使用先进的DAG调度器. 查询优化器和物理执行引擎,从而能够高性能的实现批处理和流数据处理。

    image-20220507004710013

  2. 易用

    Spark可以使用Java. Scala. Python. R和SQL快速编写应用程序。
    Spark提供了超过80个高级算子(关于算子,在第3章将详细讲解),使用这些算子可以轻松构建并行应用程序,并且可以从Scala. Python. R和SQL的Shell中交互式地使用它们。

  3. 通用

    Spark拥有一系列库,包括SQL和DataFrame. 用于机器学习的MLlib. 用于图计算的GraphX. 用于实时计算的Spark Streaming。可以在同一个应用程序中无缝地组合这些库。

  4. 到处运行

    Spark可以使用独立集群模式运行(使用自带的独立资源调度器,称为Standalone模式),也可以运行在Amazon EC2. Hadoop YARN. Mesos(Apache下的一个开源分布式资源管理框架). Kubernetes之上,并且可以访问HDFS. Cassandra. HBase. Hive等数百个数据源中的数据。

Spark主要组件

Spark是由多个组件构成的软件栈,Spark 的核心(Spark Core)是一个对由很多计算任务组成的. 运行在多个工作机器或者一个计算集群上的应用进行调度. 分发以及监控的计算引擎。

image-20220507004643690

Spark运行时架构

Spark有多种运行模式,可以运行在一台机器上,称为本地(单机)模式;也可以以YARN或Mesos作为底层资源调度系统以分布式的方式在集群中运行,称为Spark On YARN模式;还可以使用Spark自带的资源调度系统,称为Spark Standalone模式。

本地模式通过多线程模拟分布式计算,通常用于对应用程序的简单测试。本地模式在提交应用程序后,将会在本地生成一个名为“SparkSubmit”的进程,该进程既负责程序的提交又负责任务的分配. 执行和监控等。

YARN集群架构

在学习Spark集群架构之前,先需要了解YARN集群的架构。YARN集群总体上是经典的主/从(Master/Slave)架构,主要由ResourceManager. NodeManager. ApplicationMaster和Container等几个组件构成。

image-20220507004930490

YARN集群中应用程序的执行流程:

image-20220507004952435

Spark Standalone架构

Spark Standalone模式为经典的Master/Slave架构,资源调度是Spark自己实现的。在Standalone模式中,根据应用程序提交的方式不同,Driver(主控进程)在集群中的位置也有所不同。应用程序的提交方式主要有两种:client和cluster,默认是client。

当提交方式为client时,运行架构:

file

当提交方式为cluster时,运行架构(注意Driver的位置):

image-20220507005119249

Spark On YARN架构

Spark On YARN模式,遵循YARN的官方规范,YARN只负责资源的管理和调度,运行哪种应用程序由用户自己实现,因此可能在YARN上同时运行MapReduce程序和Spark程序,YARN很好的对每一个程序实现了资源的隔离。这使得Spark与MapReduce可以运行于同一个集群中,共享集群存储资源与计算资源。Spark On YARN 模式与Standalone模式一样,也分为client和cluster两种提交方式。

client提交方式架构:

image-20220507005650409

cluster提交方式架构:

Spark On Yarn 环境搭建

Spark Standalone 集群搭建

Spark Standalone模式的搭建需要在集群的每个节点都安装Spark:

  1. 下载解压安装包
    访问Spark官网http://spark.apache.org/downloads.html下载预编译的Spark安装包,选择Spark版本为2.4.0,包类型为“Pre-built for Apache Hadoop 2.7 and later”(Hadoop2.7及之后版本的预编译版本)。
$ tar -zxvf spark-2.4.0-bin-hadoop2.7.tgz -C /opt/pkg/
  1. 修改配置文件
    修改slaves文件:

    $ cp slaves.template slaves
    $ vi slaves

    改为以下内容:

    hadoop100

    修改spark-env.sh文件:

    $ cp spark-env.sh.template spark-env.sh
    $ vi spark-env.sh

    改为以下内容:

    export JAVA_HOME=/opt/pkg/java
    export SPARK_MASTER_HOST=hadoop100
    export SPARK_MASTER_PORT=7077
    export SPARK_MASTER_WEBUI_PORT=8085

spark master web ui 默认端口为8080,当系统有其它程序(hadoop3.x版本的集群中的有的节点会启动jetty,用的就是8080端口)也在使用该接口时,启动master时就会报错,为了避免端口冲突,我们也可以自行设置端口号为8085

启动默认的log4j日志配置

cp log4j.properties.template log4j.properties
  1. 启动Spark集群
    在主节点(hadoop100)执行,启动Spark集群:
$ sbin/start-all.sh

使用jps查看启动进程,进程分别为:Master. Worker说明启动成功。
访问网址http://hadoop100:8085,查看Spark的WebUI:

Spark提供了一个客户端应用程序提交工具spark-submit,使用该工具可以将编写好的Spark应用程序提交到Spark集群:

$ bin/spark-submit [options] <app jar> [app options]

说明

  • [options]:表示传递给spark-submit的控制参数;
  • <app jar>:表示提交的程序jar包(或Python脚本文件)所在位置;
  • [app options]:表示jar程序需要传递的参数,例如main()方法中需要传递的参数。

例如:

本地模式,使用2个cpu核心:
本地模式不会提交给Spark Master,因此在Spark Master WebUI 看不到提交的任务信息。

$ bin/spark-submit --master local[2] --deploy-mode client --class org.apache.spark.examples.SparkPi  ./examples/jars/spark-examples_2.11-2.4.8.jar

在Standalone模式(Spark集群使用Spark自带的资源协调服务)下,将Spark自带的求圆周率的程序提交到集群:

$ bin/spark-submit \
--master spark://centos01:7077 \
--class org.apache.spark.examples.SparkPi \
./examples/jars/spark-examples_2.11-2.4.0.jar

说明:
--master参数指定了Master节点的连接地址。该参数根据不同的Spark集群模式,其取值也有所不同:

在输出的日志中间可以找到Pi的估算结果:

Pi is roughly 3.138195690978455

另外在Spark任务运行时,从Spark Master UI界面可以在Running Applications选项下查看当前运行的Spark任务的状态,任务执行完毕后, 在Completed Applications选项中可以找到执行完成的Spark任务,点击Application ID可以查看任务详情和日志信息,

取值 描述
spark://host:port Standalone模式下的Master节点的连接地址,默认端口为7077
yarn 连接到YARN集群。若YARN中没有指定ResourceManager的启动地址,则需要在ResourceManager所在的节点上进行应用程序的提交,否则将因找不到ResourceManager而提交失败
local 运行本地模式,使用1个CPU核心
local[N] 运行本地模式,使用N个CPU核心。例如,local[2]表示使用2个CPU核心运行程序
local[*] 运行本地模式,尽可能使用最多的CPU核心

spark-submit还提供了一些控制资源使用和运行时环境的参数:

参数 描述
--master Master节点的连接地址。取值为spark://host:port. mesos://host:port. yarn. k8s://https://host:port或local (默认为 local[*])
--deploy-mode 提交方式。取值为“client”或“cluster”。“client”表示在本地客户端启动Driver程序,“cluster”表示在集群内部的工作节点上启动Driver程序。默认为“client”
--class 应用程序的主类(Java或Scala程序)
--name 应用程序名称,会在Spark Web UI中显示
--jars 应用依赖的第三方的jar包列表,以逗号分隔
--files 需要放到应用工作目录中的文件列表,以逗号分隔。此参数一般用来放需要分发到各节点的数据文件
--conf 设置任意的SparkConf配置属性。格式为“属性名=属性值”
--properties-file 加载外部包含键值对的属性文件。如果不指定,默认将读取Spark安装目录下的conf/spark-defaults.conf文件中的配置
--driver-memory Driver进程使用的内存量。例如“512M”或“1G”,单位不区分大小写。默认为1024M
--executor-memory 每个Executor进程所使用的内存量。例如“512M”或“1G”,单位不区分大小写。默认为1G
--driver-cores 每个Executor进程所使用的内存量。例如“512M”或“1G”,单位不区分大小写。默认为1G
--executor-cores 每个Executor进程所使用的CPU核心数,默认为1
--num-executors Executor进程数量,默认为2。如果开启动态分配,则初始Executor的数量至少是此参数配置的数量。需要注意的是,此参数仅在Spark On YARN模式中使用

例如,在Standalone模式下,将Spark自带的求圆周率的程序提交到集群,并且设置Driver进程使用内存为512M,每个Executor进程使用内存为1G,每个Executor进程所使用的CPU核心数为1,提交方式为cluster(即Driver进程运行在集群的工作节点中),执行命令:

$ bin/spark-submit \
--master spark://centos01:7077 \
--deploy-mode cluster \
--class org.apache.spark.examples.SparkPi \
--driver-memory 512m \
--executor-memory 1g \
--executor-cores 1 \
./examples/jars/spark-examples_2.11-2.4.0.jar

说明:

查看UI界面可以得知每个Worker的CPU核心数,注意设置的executor-cores不能超过这个数量。

Spark带有交互式的Shell,可在Spark Shell中直接编写Spark任务,然后提交到集群与分布式数据进行交互,并且可以立即查看输出结果。

Spark Standalone模式启动Spark Shell终端:

$ bin/spark-shell --master spark://centos01:7077

Setting default log level to "WARN".
To adjust logging level use sc.setLogLevel(newLevel). For SparkR, use setLogLevel(newLevel).
Spark context Web UI available at http://hadoop100:4040
Spark context available as 'sc' (master = spark://centos01:7077, app id = app-20210727162737-0001).
Spark session available as 'spark'.
Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _\ \/ _ \/ _ `/ __/  '_/
   /___/ .__/\_,_/_/ /_/\_\   version 2.4.0:
      /_/

Using Scala version 2.11.12 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_281)
Type in expressions to have them evaluated.
Type :help for more information.

scala>

启动完成后,访问Spark Master WebUI 查看运行的Spark应用程序:

退出Spark Shell:(注意命令前面以冒号:开头,可以简写为:q)

scala>:quit

Spark On YARN 集群模式搭建

Spark On YARN模式下Spark Shell的启动与Standalone模式所不同的是:--master的参数值为yarn。例如以下启动命令:

$ bin/spark-shell --master yarn

如果之前没有配置 Spark On YARN 集群模式的环境的话,这一步铁定会遇到异常的,当我们解决这些异常之后, Spark On YARN 集群模式也就自然搭建完成了。

Spark On YARN模式启动Shell出现的问题

  1. Unable to load native-hadoop library for your platform

原因 这个只是警告,而不是错误,提示缺少对Hadoop的lib的引用。在环境变量里面进行设置即可。如果没有配置的话则会使用内建的Java类来实现,导致执行效率上有一定影响。

解决方法

编辑 /etc/profile,添加:

export LD_LIBRARY_PATH=$HADOOP_HOME/lib/native/:$LD_LIBRARY_PATH

使环境变量生效

source /etc/profile

也可以在需要执行的脚本中前面加上

#!/bin/bash
export LD_LIBRARY_PATH=$HADOOP_HOME/lib/native/
  1. either HADOOP_CONF_DIR or YARN_CONF_DIR must be set

解决方法
编辑spark-env.sh, 增加

export HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop
  1. Neither spark.yarn.jars nor spark.yarn.archive is set
$ hdfs dfs -mkdir -p /user/hadoop/spark/jars
$ hdfs dfs -put /opt/pkg/spark/jars/* /user/hadoop/spark/jars
$ hdfs dfs -chmod -R 755 /user/hadoop/spark/jars

spark-defaults.conf中写入 (注意后面的/*别漏加)

spark.yarn.jars  hdfs://hadoop100:8020/user/hadoop/spark/jars/*

说明:
hadoop100:8020 对应HDFS的NameNode的主机名和端口号(对于hadoop2.x的NameNode端口默认是9000,对于hadoop3.x的NameNode端口默认是8020)

最后重新启动Spark,然后运行Spark Shell:

$ bin/spark-shell --master yarn
Setting default log level to "WARN".
To adjust logging level use sc.setLogLevel(newLevel). For SparkR, use setLogLevel(newLevel).
Spark context Web UI available at http://centos01:4040
Spark context available as 'sc' (master = yarn, app id = application_1627371733487_0005).
Spark session available as 'spark'.
Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _\ \/ _ \/ _ `/ __/  '_/
   /___/ .__/\_,_/_/ /_/\_\   version 2.4.8
      /_/

Using Scala version 2.11.12 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_281)
Type in expressions to have them evaluated.
Type :help for more information.

这就是正常运行的样子。

  1. Container Killed on request.

出现这个错误说明Yarn给Spark任务的内存分配过小,Yarn最终直接将Container的进程杀掉了。

解决方法:
在Hadoop的配置文件yarn-site.xml中加入以下内容即可:

    <!--关闭物理内存检查-->
    <property>
        <name>yarn.nodemanager.pmem-check-enabled</name>
        <value>false</value>
    </property>
    <!--关闭虚拟内存检查-->
    <property>
        <name>yarn.nodemanager.vmem-check-enabled</name>
        <value>false</value>
    </property>

修改完毕后,将此文件分发到所有Yarn节点,重启Yarn集群。

Spark 三种提交模式测试

下面的测试环境使用三台虚拟机,系统是CentOS7_x64. 主机名分别使hadoop100. hadoop101. Hadoop102,安装了Java JDK8和hadoop3.1.4,Spark 2.4.8 (使用scala2.11编译的) built for Hadoop 2.7.3(用的hadoop的32位的库,因此有些兼容问题),其中Spark的活动master在hadoop100,备用master在hadoop101,worker节点是hadoop101和hadoop102。YARN的ResourceManager节点在hadoop101。

虚拟机环境介绍完了,下面编写一些脚本,使用官方提供的估算Pi值的程序,分别测试Local(本地). Standalone(提交给Spark集群)以及Spark on YARN(提交给YARN集群)这三种提交模式,除了Local提交模式之外,根据Driver的的部署方式还分为Client模式部署以及Cluster模式部署两种部署方法。

本地客户端提交:
仅使用当前节点计算,默认使用一个CPU核心。
由于客户端和Driver在一个进程,结果直接显示在控制台。

Local[2]表示使用2个核心, Local[*]表示使用尽可能多的核心

#!/bin/bash
/opt/pkg/spark/bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master local \
--name pi-local \
/opt/pkg/spark/examples/jars/spark-examples_2.11-2.4.8.jar \
100

Standalone-Client部署模式:
提交任务使用spark自带的资源管理机制
计算任务交给Spark配置的工作节点(工作节点配置在slaves中).
Drive和客户端在一个进程,直接在控制台输出结果

#!/bin/bash
# spark预编译的hadoop版本是32位的和虚拟机里的不兼容
# 因此需要指定hadoop的本地库
export LD_LIBRARY_PATH=$HADOOP_HOME/lib/native
/opt/pkg/spark/bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master spark://hadoop100:7077 \
--total-executor-cores 2 \
--name pi-cluster-client \
/opt/pkg/spark/examples/jars/spark-examples_2.11-2.4.8.jar \
100

提交Python编写的Spark任务到Spark集群(虚拟机中的python版本为2.75)
这里使用的是Standalone-Client模式

#!/bin/bash
# Run a Python application on a Spark standalone-client mode
# spark2.4.8预编译的hadoop版本是32位的和虚拟机里的不兼容
# 因此需要指定hadoop的本地库
export LD_LIBRARY_PATH=$HADOOP_HOME/lib/native
/opt/pkg/spark/bin/spark-submit \
  --master spark://hadoop100:7077 \
  /opt/pkg/spark/examples/src/main/python/pi.py \
  100

Standalone-Cluster提交,使用2个CPU核心
Standalone提交任务使用spark集群自带的资源管理机制
cluster部署模式:Master选择一个Workder节点(slaves.sh中配置)运行driver进程
注意这种提交方式要求应用程序使用的jar包和文件需要同步到所有worker节点(或放在HDFS上)
pi的计算结果可以访问Storm WebUI在driver的stdout日志中查找

export LD_LIBRARY_PATH=$HADOOP_HOME/lib/native
/opt/pkg/spark/bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master spark://hadoop100:6066 \
--deploy-mode cluster \
--total-executor-cores 2 \
--name pi-cluster-cluster \
/opt/pkg/spark/examples/jars/spark-examples_2.11-2.4.8.jar \
100

带监督模式的Standalone-Cluster部署

Cluster部署有个好处就是可以开启监督模式(supervise)
开启监督运行模式后当任务失败后可以自动重启

#!/bin/bath
# spark2.4.8预编译的hadoop版本是32位的和虚拟机里的不兼容
# 因此需要指定hadoop的本地库
export LD_LIBRARY_PATH=$HADOOP_HOME/lib/native
/opt/pkg/spark/bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master spark://hadoop100:6066 \
--deploy-mode cluster \
# --supervise 失败时自动重启driver
--supervise \
--total-executor-cores 2 \
--name pi-cluster-cluster \
/opt/pkg/spark/examples/jars/spark-examples_2.11-2.4.8.jar \
100

spark以client方式提交时,port应该设置为7077;以cluster方式提交时,port设置为6066,因为这种方式提交时,是以rest api方式提交application。

YARN-Client部署模式:
客户端运行driver,任务提交到YARN集群进行调度. 使用YARN集群的工作节点进行计算和(和slaves的配置无关)。可以在YARN的可视化界面(默认端口8088)查看任务执行情况,得到pi的计算结果再发送给Driver在控制台显示

#!/bin/bash
# spark预编译的hadoop版本是32位的和虚拟机里的不兼容
# 因此需要指定hadoop的本地库
export LD_LIBRARY_PATH=$HADOOP_HOME/lib/native
/opt/pkg/spark/bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master yarn \
--deploy-mode client \
--total-executor-cores 2 \
--name pi-yarn-yarn \
/opt/pkg/spark/examples/jars/spark-examples_2.11-2.4.8.jar \
100

YARN-cluster部署模式
客户端将任务交给YARN集群进行调度
使用YARN集群中的工作节点和spark中的slaves.sh配置无关
driver运行在YARN中的某个NM节点上
可以在YARN的可视化界面(默认端口8088)查看任务执行情况
结果在Application的stdout日志中查看(需提前启动JobHsotry Server)
或者使用yarn logs --applicationId <applicationId>查看

#!/bin/bash
# YARN-cluster部署模式:
# spark预编译的hadoop版本是32位的和虚拟机里的不兼容
# 因此需要指定hadoop的本地库
export LD_LIBRARY_PATH=$HADOOP_HOME/lib/native
/opt/pkg/spark/bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master yarn \
--deploy-mode cluster \
--total-executor-cores 2 \
--name pi-yarn-yarn \
/opt/pkg/spark/examples/jars/spark-examples_2.11-2.4.8.jar \
100

Spark Shell的使用

Spark Shell的使用也有三种模式

  1. 本地(单机)模式下启动Spark Shell

    即不加--master参数,直接使用bin/spark-shell命令启动 Spark Shell,在本地模式下,所有操作任务只是在本地,也就是当前节点运行,而不会分发到整个集群。

  2. Spark Standalone模式下启动Spark Shell
    使用任意Spark节点进入Spark安装目录,执行以下命令,自动Spark Shell客户端:

$ bin/spark-shell --master spark://hadoop100:7077

说明:
--master指定Master节点的访问地址,centos01为Master所在节点主机名,7077为Master默认端口。

在Spark Shell启动过程日志中可以看出,有个Spark的上下文变量叫做sc,这个变量可以在Spark Shell中直接使用,它也是Spark应用程序的入口,负责于Spark集群进行交互。
启动完成后可以在 http://centos01:8080/ 查看运行的Spark应用程序。

  1. Spark on YARN 模式下启动Spark Shell

前提,需要启动Hadoop的HDFS文件系统和Yarn的相关进程,并完成Spark on YARN 模式的Spark环境搭建。启动命令如下:

$ bin/spark-shell --master yarn

Spark项目创建

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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.delucia</groupId>
    <artifactId>ScalaSparkProject</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <scala.version>2.11.8</scala.version>
        <spark.version>2.4.8</spark.version>
        <hadoop.version>3.1.4</hadoop.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <encoding>UTF-8</encoding>
    </properties>

    <repositories>
        <repository>
            <id>scala-tools.org</id>
            <name>Scala-Tools Maven2 Repository</name>
            <url>http://scala-tools.org/repo-releases</url>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>scala-tools.org</id>
            <name>Scala-Tools Maven2 Repository</name>
            <url>http://scala-tools.org/repo-releases</url>
        </pluginRepository>
    </pluginRepositories>
    <dependencies>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-core_2.11</artifactId>
            <version>${spark.version}</version>
        </dependency>
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>${scala.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.4</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.specs</groupId>
            <artifactId>specs</artifactId>
            <version>1.2.5</version>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.scala-tools/maven-scala-plugin -->
        <dependency>
            <groupId>org.scala-tools</groupId>
            <artifactId>maven-scala-plugin</artifactId>
            <version>2.15.2</version>
        </dependency>
    </dependencies>

    <build>
        <sourceDirectory>src/main/scala</sourceDirectory>
        <testSourceDirectory>src/test/scala</testSourceDirectory>
        <plugins>
            <plugin>
                <groupId>org.scala-tools</groupId>
                <artifactId>maven-scala-plugin</artifactId>
                <version>2.15.2</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <scalaVersion>${scala.version}</scalaVersion>
                    <args>
                        <arg>-target:jvm-1.8</arg>
                    </args>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-eclipse-plugin</artifactId>
                <version>2.5.1</version>
                <configuration>
                    <downloadSources>true</downloadSources>
                    <buildcommands>
                        <buildcommand>ch.epfl.lamp.sdt.core.scalabuilder</buildcommand>
                    </buildcommands>
                    <additionalProjectnatures>
                        <projectnature>ch.epfl.lamp.sdt.core.scalanature</projectnature>
                    </additionalProjectnatures>
                    <classpathContainers>
                        <classpathContainer>org.eclipse.jdt.launching.JRE_CONTAINER</classpathContainer>
                        <classpathContainer>ch.epfl.lamp.sdt.launching.SCALA_CONTAINER</classpathContainer>
                    </classpathContainers>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <reporting>
        <plugins>
            <plugin>
                <groupId>org.scala-tools</groupId>
                <artifactId>maven-scala-plugin</artifactId>
                <configuration>
                    <scalaVersion>${scala.version}</scalaVersion>
                </configuration>
            </plugin>
        </plugins>
    </reporting>
</project>

src/mainsrc/test下面的java文件夹改为scala:

image-20220507000931715

cn.delucia.spark.rdd.LogAnalysis

package cn.delucia.spark.rdd

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

import scala.util.matching.Regex

/**
 * 从日志中抽取 32位UUID类型的ID 并进行计数, 倒序排序
 * Spark RDD 程序 - 打jar包上传到集群 Spark on YARN 模式
 * 注意运行前检查Spark集群中资源是否足够:
 *
 * 注意查看集群服务器中的spark目录下jars里面的jar包如果有很多是中间有_2.11或_2.12
 * 则表示是scala 2.11 或者 2.12版本编译的,所以当前项目的scala版本也要一样修改。
 *
 * spark deploy mode 的client和cluster的区别
 * 一般来说,如果提交任务的节点(即Master)和Worker集群在同一个网络内,此时client mode比较合适。
 * 如果提交任务的节点和Worker集群相隔比较远,就会采用cluster mode来最小化Driver和Executor之间的网络
 * 延迟。
 */
object LogAnalysis {
  // CLI Arguments: /flume/logs/22-04-29/11/56/20 /output/result
  def main(args: Array[String]): Unit = {
    if (args.length < 2) {
      println("Arguments must have both input-path and  output-path")
      System.exit(1)
    }
    val conf = new SparkConf().setAppName("LogAnalysisCluster")

    // Spark on Yarn 必须放到虚拟机运行
    // 可以先本地使用 local 模式测试
        conf.setMaster("local")
    // 测试OK后再注释掉
    // Spark on Yarn 模式不须设置 Master

    // 覆盖输出路径
    conf.set("spark.hadoop.validateOutputSpecs", "false")

    val sc = new SparkContext(conf)
    // hdfs://hadoop100:8020/flume/logs/22-04-18/*/*/*/*.log
    val lines: RDD[String] = sc.textFile("hdfs://hadoop100:8020" + args(0))

    def mapFuction(line: String): String = {
      val r = "[0-9a-z]{32}".r
      val matches: Iterator[Regex.Match] = r.findAllMatchIn(line)
      matches.mkString
    }

    val ids = lines.map(mapFuction).filter(_.nonEmpty)
    val pairs: RDD[(String, Int)] = ids.map((_, 1))
    val results = pairs.reduceByKey(_ + _).sortBy(_._2, ascending = false)

    // (id,count)
    //results.saveAsTextFile("hdfs://hadoop100:8020/" + args(1))
    val finalResult = results.map(x => (x._1 + "\t" + x._2))
    finalResult.foreach(println)
    finalResult.saveAsTextFile("hdfs://hadoop100:8020/" + args(1))

    sc.stop()
  }
}

脚本编写

/opt/bin/project exec-mapred-task.sh

#!/bin/bash

input_path=$(cat /tmp/project-mapred-input-path.txt)

#执行MapReduce程序
dataformat=<code>date +%y-%m-%d
#echo "/opt/pkg/hadoop/bin/hadoop jar /opt/data/log.jar com.niit.log.LogJob $input_path /output/result/$dataformat"
#/opt/pkg/hadoop/bin/hadoop jar /opt/data/log.jar com.niit.mr.LogAnalysisJob $input_path /output/result/$dataformat

#执行Spark程序

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$HADOOP_HOME/lib/native

/opt/pkg/spark/bin/spark-submit --master yarn \
--deploy-mode cluster \
--driver-memory 1g \
--executor-memory 1g \
--executor-cores 1 \
--class cn.delucia.spark.rdd.LogAnalysis \
/opt/data/log.jar \
$input_path /output/result/$dataformat

/opt/pkg/hadoop/bin/hdfs dfs -cat /output/result/$dataformat/part-* > /tmp/project_mr_result.txt
echo "======== Analysis Result ========"
echo $(cat /tmp/project_mr_result.txt)

最后进行联调测试。

Views: 389

电商日志分析项目 – 04 日志的抽取(Sqoop)

Sqoop 安装

参考SQOOP安装配置

编写Sqoop脚本

点击UI界面Sqoop导出时需要输入导出的源路径,存储位置:

/tmp/project-mapred-result.txt

然后编写Sqoop脚本如下:

#!/bin/bash
/opt/pkg/sqoop/bin/sqoop export \
--connect jdbc:mysql://192.168.186.100:3306/shop \
--username root \
--password niit1234 \
--input-fields-terminated-by '\t' \
--table t_mr_result \
--export-dir $(cat /tmp/project-mapred-result.txt) \
--class-name UpsertMrResult \
--update-key goodsId --update-mode allowinsert

等待一段时间执行完成,查看mysql数据是否发生更新

Views: 96

电商日志分析项目 – 03 日志的分析(MapReduce)

MapReduce工程代码

  1. 创建maven工程
    在项目根目录下创建input文件夹,创建一个文本文件access.log用来模拟nginx日志

    hadoop100,192.168.186.100 - - [21/Apr/2021:01:36:19 +0800] "GET /shop/detail.html?id=4028f00176e1aa620176e6aca6890003 HTTP/1.0" 200 4366 "-" "ApacheBench/2.3" "-"
    hadoop100,192.168.186.100 - - [21/Apr/2021:01:36:19 +0800] "GET /shop/detail.html?id=4028f00176e1aa620176e6aca6890003 HTTP/1.0" 200 4366 "-" "ApacheBench/2.3" "-"
    hadoop100,192.168.186.100 - - [21/Apr/2021:01:36:19 +0800] "GET /shop/detail.html?id=4028f00176e1aa620176e6aca6890003 HTTP/1.0" 200 4366 "-" "ApacheBench/2.3" "-"
    hadoop100,192.168.186.100 - - [21/Apr/2021:01:36:19 +0800] "GET /shop/detail.html?id=4028f00176e1aa620176e6aca6890003 HTTP/1.0" 200 4366 "-" "ApacheBench/2.3" "-"
    hadoop100,192.168.186.100 - - [21/Apr/2021:01:36:19 +0800] "GET /shop/detail.html?id=4028f00176e1aa620176e6aca6890003 HTTP/1.0" 200 4366 "-" "ApacheBench/2.3" "-"
  2. 引入maven依赖

    <?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">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>org.example</groupId>
        <artifactId>log</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <properties>
            <!--定义Hadoop版本-->
            <hadoop.version>3.1.4</hadoop.version>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
        </properties>
    
        <!---引入cdh的仓库-->
        <repositories>
            <repository>
                <id>cloudera</id>
                <url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
            </repository>
        </repositories>
    
        <dependencies>
            <!--添加Hadoop依赖包-->
            <dependency>
                <groupId>org.apache.hadoop</groupId>
                <artifactId>hadoop-client</artifactId>
                <version>${hadoop.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.hadoop</groupId>
                <artifactId>hadoop-common</artifactId>
                <version>${hadoop.version}</version>
            </dependency>
    
            <dependency>
                <groupId>org.apache.hadoop</groupId>
                <artifactId>hadoop-hdfs</artifactId>
                <version>${hadoop.version}</version>
            </dependency>
    
            <dependency>
                <groupId>org.apache.hadoop</groupId>
                <artifactId>hadoop-mapreduce-client-core</artifactId>
                <version>${hadoop.version}</version>
            </dependency>
            <!--添加JUnit4依赖包-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.11</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
    </project>
  3. 添加log4j.properties文件在资源目录下即resources,文件内容如下
    ### 配置根 ###
    log4j.rootLogger = debug,console,fileAppender
    ## 配置输出到控制台 ###
    log4j.appender.console = org.apache.log4j.ConsoleAppender
    log4j.appender.console.Target = System.out
    log4j.appender.console.layout = org.apache.log4j.PatternLayout
    log4j.appender.console.layout.ConversionPattern = %d{ABSOLUTE} %5p %c:%L - %m%n
    ### 配置输出到文件 ###
    log4j.appender.fileAppender = org.apache.log4j.FileAppender
    log4j.appender.fileAppender.File = /tmp/logs/log.log
    log4j.appender.fileAppender.Append = false
    log4j.appender.fileAppender.Threshold = DEBUG,INFO,WARN,ERROR
    log4j.appender.fileAppender.layout = org.apache.log4j.PatternLayout
    log4j.appender.fileAppender.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
  4. 编写MR程序之Mapper:LogMapper.java

    package com.niit.log;
    
    import org.apache.hadoop.io.IntWritable;
    import org.apache.hadoop.io.LongWritable;
    import org.apache.hadoop.io.Text;
    import org.apache.hadoop.mapreduce.Mapper;
    
    import java.io.IOException;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    public class LogMapper extends Mapper<LongWritable, Text,Text, IntWritable> {
        // 按指定模式在字符串查找
        String pattern = "\\?id=[0-9a-z]*";
        // 创建 Pattern 对象
        Pattern r = Pattern.compile(pattern);
        @Override
        protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
            String data = value.toString();
            // 现在创建 matcher 对象
            Matcher m = r.matcher(data);
            if (m.find()) {
                String idStr = m.group(0);
                String id = idStr.substring(4);
                context.write(new Text(id),new IntWritable(1));
            }
        }
    }   
  5. 编写MR程序之Mapper:LogReducer.java

    package com.niit.log;
    
    import org.apache.hadoop.io.IntWritable;
    import org.apache.hadoop.io.Text;
    import org.apache.hadoop.mapreduce.Reducer;
    
    import java.io.IOException;
    
    public class LogReducer extends Reducer<Text, IntWritable,Text, IntWritable> {
    
        @Override
        protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
            int sum = 0;
            for (IntWritable v: values) {
                sum += v.get();
            }
            context.write(key,new IntWritable(sum));
        }
    }
  6. 编写MR程序之Job:LogJob.java

    package com.niit.log;
    
    import org.apache.hadoop.conf.Configuration;
    import org.apache.hadoop.fs.FileSystem;
    import org.apache.hadoop.fs.Path;
    import org.apache.hadoop.io.IntWritable;
    import org.apache.hadoop.io.Text;
    import org.apache.hadoop.mapreduce.Job;
    import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
    import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
    
    public class LogJob {
        public static void main(String[] args) throws Exception {
    
            // Windows
            System.setProperty("HADOOP_USER_NAME", "hadoop");
    
            // Linux
            // if (args.length < 2) {
            //     System.out.println("Usage:hadoop jar Log.jar " + LogJob.class.getName() + " input ouput");
            //     System.exit(0);
            // }
    
            Configuration configuration = new Configuration();
            Job job = Job.getInstance(configuration);
            job.setJarByClass(LogJob.class);
    
            job.setMapperClass(LogMapper.class);
            job.setMapOutputKeyClass(Text.class);
            job.setMapOutputValueClass(IntWritable.class);
    
            job.setReducerClass(LogReducer.class);
            job.setOutputKeyClass(Text.class);
            job.setOutputValueClass(IntWritable.class);
    
            FileSystem fileSystem = FileSystem.get(configuration);
    
            // Windows
            Path inputPath = new Path("./input/access.log");
            Path outputPath = new Path("./output/");
    
            // Linux
            // Path inputPath = new Path(args[0]);
            // Path outputPath = new Path(args[1]);
    
            if (fileSystem.exists(outputPath)) {
                fileSystem.delete(outputPath, true);
            }
    
            // Windwos
            FileInputFormat.setInputPaths(job, inputPath);
            FileOutputFormat.setOutputPath(job, outputPath);
    
            // Linux
            // FileInputFormat.setInputPaths(job, new Path(args[0]));
            // FileOutputFormat.setOutputPath(job, new Path(args[1]));
    
            boolean completion = job.waitForCompletion(true);
            System.exit(completion ? 0 : -1);
        }
    }
  7. 本地运行代码,测试下结果正确与否
  8. 本地运行测试结果正确后,需要对Driver类输入输出部分代码进行修改,具体修改如下:

    // Windows
    // System.setProperty("HADOOP_USER_NAME", "hadoop");
    
    // Linux
    if (args.length < 2) {
        System.out.println("Usage:hadoop jar Log.jar " + LogJob.class.getName() + " input ouput");
        System.exit(0);
    }
    
    // Windows
    // Path inputPath = new Path("./input/access.log");
    // Path outputPath = new Path("./output/");
    
    // Linux
    Path inputPath = new Path(args[0]);
    Path outputPath = new Path(args[1]);
    
    // Windwos
    // FileInputFormat.setInputPaths(job, inputPath);
    // FileOutputFormat.setOutputPath(job, outputPath);
    
    // Linux
    FileInputFormat.setInputPaths(job, new Path(args[0]));
    FileOutputFormat.setOutputPath(job, new Path(args[1]));
  9. 打jar包,提交集群运行
    直接使用maven的生命周期的package工具构建jar包,并上传到服务器的/opt/data目录下,改名为Log.jar
  10. 为方便操作,编写脚本/opt/bin/project/exec-mapred-task.sh来执行MR程序

    
    #!/bin/bash
    
    input_path=$(cat /tmp/project-mapred-input-path.txt)
    
    #执行MapReduce程序
    dataformat=`date +%y-%m-%d`
    echo "/opt/pkg/hadoop/bin/hadoop jar /opt/data/log.jar com.niit.log.LogJob $input_path /output/result/$dataformat"
    /opt/pkg/hadoop/bin/hadoop jar /opt/data/log.jar com.niit.log.LogJob $input_path /output/result/$dataformat
    /opt/pkg/hadoop/bin/hdfs dfs -cat /output/result/$dataformat/part-r-00000 > /tmp/project_mr_result.txt
    
    echo "======== mapred task result ========"
    echo $(cat /tmp/project_mr_result.txt)

Views: 258

电商日志分析项目 – 02 日志的生成和采集(Flume)

Hadoop安装与配置

Hadoop3.1.4的单机安装参考

Flume的安装与配置

Flume的安装与配置参考

Apache httpd 安装

安装httpd的原因主要是使用它提供的ab压测工具.

  1. 安装httpd
yum install -y httpd
  1. 配置httpd,为了避免和ngixn端口冲突修改端口号为81,配置如下:
vi /etc/httpd/conf/httpd.conf

修改内容如下:

#Listen 80
Listen 81
  1. 启动服务
systemctl enable httpd # 开机自启动
systemctl start httpd # 启动httpd
  1. 查看启动状态
systemctl status httpd

注意:

  • 其实使用httpd只是为了使用ab工具,无需启动httpd服务

AB压测生成日志

访问http://hadoop100/university/ 登陆进入后台界面

后台UI界面生成商品链接

点击获取商品链接,页面打印生成的JSON格式数据
file

同时在服务器生成文件/tmp/project-urls.txt,里面保存着所有商品链接:

http://hadoop100/shop/detail.html?id=4028f00176e118120176e17d0e0f0000
http://hadoop100/shop/detail.html?id=4028f00176e1aa620176e2319b730000
http://hadoop100/shop/detail.html?id=4028f00176e1aa620176e6a991e60001
http://hadoop100/shop/detail.html?id=4028f00176e1aa620176e6ab478d0002
http://hadoop100/shop/detail.html?id=4028f00176e1aa620176e6aca6890003
http://hadoop100/shop/detail.html?id=4028f08176e0e2af0176e0e2bd600007

后台UI界面AB压测生成日志

编写压测脚本ab_test.sh,内容可自行修改,如下:

$ vi /opt/bin/project/ab-test.sh

#!/bin/bash
n=$(cat /tmp/project-urls.txt | wc -l)

for ((i=1;i<=$n;i++))
do
    echo "No.$i"
    url=$(shuf -n1 /tmp/project-urls.txt)
    r=$(shuf -i 6-100 -n 1)
    c=$(shuf -i 1-5 -n 1)

    echo "access url  -> $url"
    echo "requests    -> $r"
    echo "concurrency -> $c"

    ab -n $r -c $c $url >> /tmp/project-abtest-results.log &
done

说明:

  • 给与执行权限方便执行
  • shuf在指定范围生成随机数
  • ab apache benchmark工具,常用于压力测试
    • -n 请求数
    • -c 并发数
  • 压测结果可在/tmp/project-abtest-resuilts.log中找到

压测结果说明
file

点击AB压测生成日志
file

产生的日志在/var/log/nginx/access.log中。

使用FLume收集滚动日志上传到HDFS上

修改nginx生成的日志文件所有者

nginx默认生成的日志文件access.log的用户组和用户分别是adm和nginx,如下所示:

[hadoop@hadoop100 nginx]$ ll
-rw-r-----  1 nginx  adm    135470 Apr 21 01:22 access.log

而现在需要让另一用户读取该文件,做日志的分析监控. 应该修改日志文件的所属用户组和用户,避免出现权限问题。

修改Nginx运行时进程的用户与组

Nginx运行时进程需要有用户与组的支持,用以实现对网站文件读取时进行访问控制。主进程由 root创建,子进程由指定的用户与组创建, 一般默认为 nginx admnobody nobody

由于我们项目中使用的操作账户为hadoop,而这个用户对nginx用户生成的日志没有操作权限,因此使用Flume收集时会出现权限问题导致收集失败。

当然也可以简单粗暴的临时使用下面的命令解决问题

$ sudo chown hadoop:hadoop -R /var/log/nginx/

但是这样并不能从根本解决问题,因为下次滚动生成的日志所有者还会变成原来配置的。

修改 nginx 用户与组有两种办法。如果是编译安装,可以在编译前指定用户和组;也可以修改配置文件指定用户与组,然后重启服务器即可。

编译 nginx 时指定用户与组

即修改 ./configure 后面指定用户与组的参数

./configure \
--prefix=/usr/local/nginx \
--user=nginx \        //指定用户名是 nginx
--group=nginx \       //指定组名是 nginx
--with-http_stub_status_module

修改 nginx 配置文件指定用户与组

打开文件/etc/nginx/nginx.conf,我这里默认是

user nginx;   //修改用户为nginx,组为nginx

现在我们改成

user hadoop hadoop; // //修改用户为 hadoop,组为 hadoop

sudo service nginx restart重启服务后,查看进程情况,主进程由root创建,子进程则由nginx创建。

file

修改 Nginx 日志滚动策略

可以通过修改这个文件修改 Nginx 的日志滚动策略(如有需要)

$ sudo vi /etc/logrotate.d/nginx

/var/log/nginx/*.log {
        daily
        missingok
        rotate 52
        compress
        delaycompress
        notifempty
        create 640 nginx adm
        sharedscripts
        postrotate
                if [ -f /var/run/nginx.pid ]; then
                        kill -USR1 $(cat /var/run/nginx.pid)
                fi
        endscript
}

其中nginx adm就是滚动日志的权限拥有者及所在群组, 640表示滚动日志的权限。下面比如我们需要将滚动日志的用户组和用户改为hadoop:hadoop, 只需要修改此处:

#create 640 nginx adm
create 640 hadoop hadoop

滚动access.log日志文件

接着编写Linux Shell脚本实现/var/log/nginx/access.log日志滚动到flume-logs目录下。

/opt/bin/project目录下新建rolling-log.sh,并添加执行权限,内容如下:

#!/bin/bash
#定义日期格式
dataformat=$(date +%Y-%m-%d-%H-%M-%S)

#复制access.log并重命名,添加时间信息
cp /var/log/nginx/access.log /var/log/nginx/access_$dataformat.log

host=$(hostname)
sed -i 's/^/'${host}',&/g' /var/log/nginx/access_$dataformat.log
#统计日志文件行数
lines=$(wc -l < /var/log/nginx/access_$dataformat.log)

#将格式化的日志移动到flumeLogs目录下
mv /var/log/nginx/access_$dataformat.log /var/log/nginx/flume-logs

#清空access.log的内容
sed -i '1,'${lines}'d' /var/log/nginx/access.log

#重启nginx , 使access.log可以继续滚动
kill -USR1 $(cat /var/run/nginx.pid)

##返回给服务器信息
ls -al /var/log/nginx/flume-logs/

后台管理界面点击生成滚动日志

file

编写Flume Agent配置文件:

vi /opt/pkg/flume/conf/log2hdfs.conf`

# define the agent a1
a1.sources=r1
a1.channels=c1
a1.sinks=k1

# define the source
#上传目录类型
a1.sources.r1.type=spooldir
a1.sources.r1.spoolDir=/var/log/nginx/flume-logs
#定义自滚动日志完成后的后缀名
a1.sources.r1.fileSuffix=.FINISHED
#根据每行文本内容的大小自定义最大长度4096=4k
a1.sources.r1.deserializer.maxLineLength=4096

# define the sink
a1.sinks.k1.type = hdfs
#上传的文件保存在hdfs的/flume/logs目录下
a1.sinks.k1.hdfs.path = hdfs://hadoop100:8020/flume/logs/%y-%m-%d/
a1.sinks.k1.hdfs.filePrefix=access_log
a1.sinks.k1.hdfs.fileSufix=.log
a1.sinks.k1.hdfs.batchSize=1000
a1.sinks.k1.hdfs.fileType = DataStream
a1.sinks.k1.hdfs.writeFormat= Text
# roll 滚动规则:按照数据块128M大小来控制文件的写入,与滚动相关其他的都设置成0
#为了演示,这里设置成500k写入一次
a1.sinks.k1.hdfs.rollSize= 512000
a1.sinks.k1.hdfs.rollCount=0
a1.sinks.k1.hdfs.rollInteval=0
#控制生成目录的规则:一般是一天或者一周或者一个月一次,这里为了演示设置10秒
a1.sinks.k1.hdfs.round=true
a1.sinks.k1.hdfs.roundValue=10
a1.sinks.k1.hdfs.roundUnit= second
#是否使用本地时间
a1.sinks.k1.hdfs.useLocalTimeStamp=true

#define the channel
a1.channels.c1.type = memory
#自定义event的条数
a1.channels.c1.capacity = 500000
#flume事务控制所需要的缓存容量1000条event
a1.channels.c1.transactionCapacity = 1000

#source channel sink cooperation
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1

创建脚本flume_start.sh,用于启动Flume Agent

#!/bin/bash
/opt/pkg/flume-1.9.0/bin/flume-ng agent --conf ./conf/ -f ./conf/log2hdfs.conf --name a1 -Dflume.root.logger=INFO,console

编写停止Flume脚本 flume_stop.sh,停止Flume

#!/bin/bash

JAR="flume"

#停止flume函数
echo "begin to stop flume process.."
num=$(ps -ef|grep java|grep $JAR|wc -l)
echo "当前已经启动的flume进程数:$num"
if [ "$num" != "0" ];then
   #正常停止flume
   ps -ef|grep java|grep $JAR|awk '{print $2;}'|xargs kill -9
   echo "进程已经关闭..."
else
   echo "服务未启动,无须停止..."
fi

编写重启Flume脚本 flume-log2hdfs.sh,综合了前两个脚本

#!/bin/bash

#先停止正在启动的flume
./flume_stop.sh

nohup ./flume_start.sh > nohup_output.log 2>&1 &
echo "启动flume成功……"

后台管理界面点击启动FLume采集
file

查看滚动日志是否成功上传到HDFS系统

file

异常: 日志收集失败,报错:

2021-03-09 22:38:50,834 (SinkRunner-PollingRunner-DefaultSinkProcessor) [ERROR - org.apache.flume.sink.hdfs.HDFSEventSink.process(HDFSEventSink.java:459)] process failed
java.lang.NoSuchMethodError: com.google.common.base.Preconditions.checkArgument(ZLjava/lang/String;Ljava/lang/Object;)V

原因: flume/lib/guava-xxx.jar 和 hadoop自带的jar包发生冲突

解决: 将flume/lib下的guava包删除或者改名, 只保留hadoop的版本即可

Views: 143

电商日志分析项目 – 01 环境搭建和项目部署

一、CentOS7下MySQL-5.7使用yum方式安装:


Centos7的MySQL安装

卸载mariaDb

mysql被oracle收购后为了防止mysql有可能变成闭源,因此mysql创始人maria就开源做了一个mariaDb, centos7是自带使用了这个数据库的.因此安装mysql之前,应当首先卸载mariadb数据库:

$ sudo yum list installed | grep mariadb    #检查mariadb是否已安装

$ sudo yum list installed | grep mariadb
mariadb-libs.x86_64                     1:5.5.56-2.el7                 @anaconda

$ sudo yum -y remove mariadb*    #全部卸载

同理如果是已经安装过其他版本的MySQL,安装新的版本之前也需要按照如上方法进行卸载。

安装MySQL

下载mysql的YUM源: https://dev.mysql.com/downloads/repo/yum/

YUM源的官方安装说明:https://dev.mysql.com/doc/mysql-yum-repo-quick-guide/en/

下载源mysql的YUM源,官网提供的是最新的MySQL8.0的源,内含其他较低版本的源

wget -P /home/hadoop/softwares https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm  --no-check-certificate

由于我们是下载到/home/hadoop/softwares目录下,所以先切换到该目录下:

cd /home/hadoop/softwares

安装mysql的YUM源:

sudo rpm -ivh mysql80-community-release-el7-3.noarch.rpm

检查mysql的YUM源是否安装成功:

sudo yum repolist enabled | grep mysql

mysql-connectors-community/x86_64       MySQL Connectors Community           175
mysql-tools-community/x86_64            MySQL Tools Community                120
mysql80-community/x86_64                MySQL 8.0 Community Server           211

选择要启用的mysql版本

查看源里包含的mysql版本,执行:

sudo yum repolist all | grep mysql

mysql-cluster-7.5-community/x86_64 MySQL Cluster 7.5 Community      禁用
mysql-cluster-7.5-community-source MySQL Cluster 7.5 Community - So 禁用
mysql-cluster-7.6-community/x86_64 MySQL Cluster 7.6 Community      禁用
mysql-cluster-7.6-community-source MySQL Cluster 7.6 Community - So 禁用
mysql-cluster-8.0-community/x86_64 MySQL Cluster 8.0 Community      禁用
mysql-cluster-8.0-community-source MySQL Cluster 8.0 Community - So 禁用
mysql-connectors-community/x86_64  MySQL Connectors Community       启用:    175
mysql-connectors-community-source  MySQL Connectors Community - Sou 禁用
mysql-tools-community/x86_64       MySQL Tools Community            启用:    120
mysql-tools-community-source       MySQL Tools Community - Source   禁用
mysql-tools-preview/x86_64         MySQL Tools Preview              禁用
mysql-tools-preview-source         MySQL Tools Preview - Source     禁用
mysql55-community/x86_64           MySQL 5.5 Community Server       禁用
mysql55-community-source           MySQL 5.5 Community Server - Sou 禁用
mysql56-community/x86_64           MySQL 5.6 Community Server       禁用
mysql56-community-source           MySQL 5.6 Community Server - Sou 禁用
mysql57-community/x86_64           MySQL 5.7 Community Server       禁用
mysql57-community-source           MySQL 5.7 Community Server - Sou 禁用
mysql80-community/x86_64           MySQL 8.0 Community Server       启用:    211
mysql80-community-source           MySQL 8.0 Community Server - Sou 禁用

可以通过类似下面的语句来启动和禁用某些版本,比如这里是禁用默认启用的MySQL8.0的源,转而启用我们需要安装的MySQL5.7的源。

或者通过修改/etc/yum.repos.d/mysql-community.repo文件,改变默认安装的mysql版本。比如要安装5.7版本,将8.0源的enabled=1改成enabled=0,然后再将5.7源的enabled=0改成enabled=1即可。

sudo yum-config-manager --enable mysql57-community
sudo yum-config-manager --disable mysql80-community

要使用yum-config-manager命令, 需要先安装yum-utils: sudo yum install yum-utils

注意: 同一时间只允许enable一个MySQL版本。

如果只需要安装MySQL5.7的版本,也可以不看上面,直接本地安装指定的远程MySQL5.7源

yum localinstall https://dev.mysql.com/get/mysql57-community-release-el7-9.noarch.rpm

查看当前的启用的 MySQL 版本:

yum repolist enabled | grep mysql

mysql-connectors-community/x86_64       MySQL Connectors Community           175
mysql-tools-community/x86_64            MySQL Tools Community                120
mysql57-community/x86_64                MySQL 5.7 Community Server           464

安装MySQL

sudo yum install -y mysql-community-server

已加载插件:fastestmirror
mysql-connectors-community                                             | 2.6 kB  00:00:00     
mysql-tools-community                                                  | 2.6 kB  00:00:00     
mysql57-community                                                      | 2.6 kB  00:00:00     
mysql57-community/x86_64/prima FAILED                                          
http://repo.mysql.com/yum/mysql-5.7-community/el/7/x86_64/repodata/cae8c564a11e5fca11dbe958c273358f206f1bba-primary.sqlite.bz2: [Errno 14] curl#7 - "Failed connect to repo.mysql.com:80; Connection refused"
正在尝试其它镜像。
mysql57-community/x86_64/primary_db                                    | 247 kB  00:00:00     
Loading mirror speeds from cached hostfile
 * base: mirrors.aliyun.com
 * extras: mirrors.aliyun.com
 * updates: mirrors.aliyun.com
正在解决依赖关系
--> 正在检查事务
---> 软件包 mysql-community-server.x86_64.0.5.7.32-1.el7 将被 安装
--> 正在处理依赖关系 mysql-community-common(x86-64) = 5.7.32-1.el7,它被软件包 mysql-community-server-5.7.32-1.el7.x86_64 需要
--> 正在处理依赖关系 mysql-community-client(x86-64) >= 5.7.9,它被软件包 mysql-community-server-5.7.32-1.el7.x86_64 需要
--> 正在检查事务
---> 软件包 mysql-community-client.x86_64.0.5.7.32-1.el7 将被 安装
--> 正在处理依赖关系 mysql-community-libs(x86-64) >= 5.7.9,它被软件包 mysql-community-client-5.7.32-1.el7.x86_64 需要
---> 软件包 mysql-community-common.x86_64.0.5.7.32-1.el7 将被 安装
--> 正在检查事务
---> 软件包 mysql-community-libs.x86_64.0.5.7.32-1.el7 将被 安装
--> 解决依赖关系完成

依赖关系解决

==============================================================================================
 Package                      架构         版本                 源                       大小
==============================================================================================
正在安装:
 mysql-community-server       x86_64       5.7.32-1.el7         mysql57-community       173 M
为依赖而安装:
 mysql-community-client       x86_64       5.7.32-1.el7         mysql57-community        25 M
 mysql-community-common       x86_64       5.7.32-1.el7         mysql57-community       308 k
 mysql-community-libs         x86_64       5.7.32-1.el7         mysql57-community       2.3 M

事务概要
==============================================================================================
安装  1 软件包 (+3 依赖软件包)

总下载量:201 M
安装大小:875 M

....

已安装:
  mysql-community-server.x86_64 0:5.7.32-1.el7                                                

作为依赖被安装:
  mysql-community-client.x86_64 0:5.7.32-1.el7  
  mysql-community-common.x86_64 0:5.7.32-1.el7 
  mysql-community-libs.x86_64 0:5.7.32-1.el7 

启动MySQL服务

systemctl start mysqld.service # 或 service mysqld start

配置开机启动

systemctl enable mysqld.service

查看运行状态

systemctl status mysqld.service

mysql      2574      1  1 23:49 ?        00:00:00 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid

查看到进程信息

netstat -anpl  | grep mysql

查看端口,可以看出mysql server的进程mysqld所使用的默认端口即3306

$ sudo netstat -anpl | grep tcp
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1412/sshd         
tcp        0     52 192.168.186.103:22      192.168.186.1:54058     ESTABLISHED 2287/sshd: hadoop 
tcp6       0      0 :::3306                 :::*                    LISTEN      2574/mysqld       
tcp6       0      0 192.168.186.103:3888    :::*                    LISTEN      2060/java         
tcp6       0      0 :::22                   :::*                    LISTEN      1412/sshd         
tcp6       0      0 :::37791                :::*                    LISTEN      2060/java         
tcp6       0      0 :::2181                 :::*                    LISTEN      2060/java 

找到临时密码

但是要登陆mysql,我们需要root密码,这个密码是安装时随机生成在MySQL的服务器日志中的

grep "temporary password" /var/log/mysqld.log

2020-02-26T17:05:45.104999Z 1 [Note] A temporary password is generated for root@localhost: bl/!6qaU.wuX

登录MySQL

# 回车后输入在日志中找到的临时root登录密码登录
mysql -u roop -p 

问题: 日志里没有找到临时密码, 或者密码不能登录,可能的原因就是之前安装过MySQL

解决办法:

sudo rm -rf /var/lib/mysql

再重启服务后就可以在日志文件中找到重新生成的临时密码了

systemctl restart mysqld.service

$ cat /var/log/mysqld.log | grep "temporary password"
2020-02-26T17:05:45.104999Z 1 [Note] A temporary password is generated for root@localhost: bl/!6qaU.wuX
2020-08-01T16:07:26.941916Z 1 [Note] A temporary password is generated for root@localhost: x/ttqh=)_6Z/

也可以直接使用明文密码登陆,像这样

mysql -uroop -p"x/ttqh=)_6Z/"

初始密码我们可以这样操作,因为很快我们会将将其改为其他的密码。

但是正常使用时不建议这样 不安全

黑客可以使用history命令查看到你在命令行的输入获取到你的MySQL的root密码

另外MySQL报错在/var下面的log目录下可以查看


如果是虚拟机环境为了方便可以选择配置跳过密码验证

vim /etc/my.cnf

添加以下代码

default-authentication-plugin=mysql_native_password #配置文件本来就有去掉注释即可
symbolic-links=0
skip-grant-tables #跳过密码验证

第一次登陆后系统会很快提示你修改掉默认密码

mysql> show databases;
ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement.

尝试修改密码(虚拟机可以设置简单密码,如果是购买的服务器则需设置复杂密码并修改默认端口号)

mysql> alter user 'root'@'localhost' identified by 'niit1234';
ERROR 1819 (HY000): Your password does not satisfy the current policy requirements

密码安全策略

但是基于密码策略,所设置的密码必须要包含大小写字母、数字和字符。

修改策略并设置简单密码

将策略要求置为0(LOW),长度要求置为1

mysql> set global validate_password_policy=0;
mysql> set global validate_password_length=1;

关于密码安全策略:

通过show命令查看当前的策略

mysql> show variables like 'validate_password%';

+--------------------------------------+--------+
| Variable_name                        | Value  |
+--------------------------------------+--------+
| validate_password_check_user_name    | OFF    |
| validate_password_dictionary_file    |        |
| validate_password_length             | 8      |
| validate_password_mixed_case_count   | 1      |
| validate_password_number_count       | 1      |
| validate_password_policy             | MEDIUM |
| validate_password_special_char_count | 1      |
+--------------------------------------+--------+
7 rows in set (0.00 sec)

1) validate_password_policy:密码安全策略,默认MEDIUM策略

策略 检查规则
0 or LOW Length
1 or MEDIUM Length; numeric, lowercase/uppercase, and special characters
2 or STRONG Length; numeric, lowercase/uppercase, and special characters; dictionary file

2)validate_password_dictionary_file:密码策略文件,策略为STRONG才需要

3)validate_password_length:密码最少长度

4)validate_password_mixed_case_count:大小写字符长度,至少1个

5)validate_password_number_count :数字至少1个

6)validate_password_special_char_count:特殊字符至少1个

这样以后就可以使用新密码登陆了

开启远程登陆

mysql命令的-h选项可以设置需要远程登陆mysql的服务器的ip地址

如果是本地的话可以使用127.0.0.1或者直接省略

但是远程访问因为安全原因默认是关闭的

mysql> select Host,User from user;
+-----------+---------------+
| Host      | User          |
+-----------+---------------+
| localhost | mysql.session |
| localhost | mysql.sys     |
| localhost | root          |
+-----------+---------------+
3 rows in set (0.00 sec)

这里可以看到host都是localhost,这就是因为这些账号只有本地访问的权限

我们需要将root的访问权限扩大都允许任意方式访问(包括远程访问)

GRANT ALL PRIVILEGES ON *.* TO 'root'@'%'IDENTIFIED BY 'niit1234' WITH GRANT OPTION;

也可以创建一个专门用来支持远程访问的单独用户

GRANT ALL PRIVILEGES ON *.* TO 'remote'@'%' IDENTIFIED BY 'niit1234' WITH GRANT OPTION; 

如果你想允许用户myuser从ip为192.168.1.6的主机连接到mysql服务器,并使用mypassword作为密码

若你想限制能连接到mysql服务器的客户端所在的IP地址为'192.168.1.6'

并使用mypassword作为密码,则命令如下:

GRANT ALL PRIVILEGES ON . TO 'mysqluser'@'192.168.1.6'IDENTIFIED BY 'mypassword' WITH GRANT OPTION;

使修改生效

现在虽然修改了远程访问权限,但是还没有生效

我们需要刷新权限(或者重启服务),当然直接在mysql执行环境刷新权限是更好的方法

mysql>FLUSH PRIVILEGES;

然后我们可以试试使用navicat远程连接虚拟机中mysql试试

navicat虽然是收费的还是可以下载到破解的版本

最后一点

如果你的服务器开启了防火墙也可能导致远程无法连接

因为mysql默认使用3306端口,我们需要开放这个端口给mysql客户端连接

修改编码

默认安装的设置对中文的支持不好,一般需要修改成utf8,但这里我们修改成utf8mb4编码,因为它是uft8的超集,减少更多乱码的问题,也是推荐使用的编码,比如需要插入emoji字符在数据库中,utf8的显示就会出现问题,而utf8mb4则没有问题。

首先查看当前的编码:

mysql> show variables like '%char%';
+--------------------------------------+----------------------------+
| Variable_name                        | Value                      |
+--------------------------------------+----------------------------+
| character_set_client                 | utf8                       |
| character_set_connection             | utf8                       |
| character_set_database               | latin1                     |
| character_set_filesystem             | binary                     |
| character_set_results                | utf8                       |
| character_set_server                 | latin1                     |
| character_set_system                 | utf8                       |
| character_sets_dir                   | /usr/share/mysql/charsets/ |
| validate_password_special_char_count | 1                          |
+--------------------------------------+----------------------------+
9 rows in set (0.00 sec)

为了修改编码,我们需要修改mysql的核心配置文件如下:

[hadoop@hadoop000 download]$ sudo vim /etc/my.cnf

# For advice on how to change settings please see
# For advice on how to change settings please see
# http://dev.mysql.com/doc/refman/5.7/en/server-configuration-defaults.html

[mysqld]
#
# Remove leading # and set to the amount of RAM for the most important data
# cache in MySQL. Start at 70% of total RAM for dedicated server, else 10%.
# innodb_buffer_pool_size = 128M
#
# Remove leading # to turn on a very important data integrity option: logging
# changes to the binary log between backups.
# log_bin
#
# Remove leading # to set options mainly useful for reporting servers.
# The server defaults are faster for transactions and fast SELECTs.
# Adjust sizes as needed, experiment to find the optimal values.
# join_buffer_size = 128M
# sort_buffer_size = 2M
# read_rnd_buffer_size = 2M
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock

# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0

log-error=/var/log/mysqld.log

# 默认服务器内部操作字符集
character-set-server=utf8mb4
# 默认服务器内部操作字符集校对规则
collation-server=utf8mb4_general_ci
# 默认的存储引擎
default-storage-engine=InnoDB
# 初始化连接时设置以下字符集:
# character_set_client
# character_set_results
# character_set_connection
init_connect='set names utf8mb4'

[client]
default-character-set=utf8mb4

[mysql]
default-character-set=utf8mb4
  • 30行以下为手动添加的配置

验证

mysql> show variables like '%char%';
+--------------------------------------+----------------------------+
| Variable_name                        | Value                      |
+--------------------------------------+----------------------------+
| character_set_client                 | utf8mb4                    |
| character_set_connection             | utf8mb4                    |
| character_set_database               | utf8mb4                    |
| character_set_filesystem             | binary                     |
| character_set_results                | utf8mb4                    |
| character_set_server                 | utf8mb4                    |
| character_set_system                 | utf8                       |
| character_sets_dir                   | /usr/share/mysql/charsets/ |
| validate_password_special_char_count | 1                          |
+--------------------------------------+----------------------------+
9 rows in set (0.00 sec)
  • character_set_client 是指客户端发送过来的语句的编码
  • character_set_database 是指服务器内部使用的编码
  • character_set_connection 是指mysqld收到客户端的语句后,要转换到的编码
  • character_set_results 是指server执行语句后,返回给客户端的数据的编码
  • character_set_system是元数据编码,无需修改
  • character_set_filesystem是文件系统的编码,2进制存储最有效,不能修改

rpm-bundle的离线安装

下载rpm-bundle包


$ wget http://mirrors.163.com/mysql/Downloads/MySQL-5.7/mysql-5.7.33-1.el7.x86_64.rpm-bundle.tar
$  tar xvf mysql-5.7.33-1.el7.x86_64.rpm-bundle.tar
$  mkdir mysql-jars
$  mv mysql-comm*.rpm mysql-jars/
$  cd mysql-jars/
$ ls
mysql-community-client-5.7.33-1.el7.x86_64.rpm
mysql-community-common-5.7.33-1.el7.x86_64.rpm
mysql-community-libs-5.7.33-1.el7.x86_64.rpm
mysql-community-libs-compat-5.7.33-1.el7.x86_64.rpm
mysql-community-server-5.7.33-1.el7.x86_64.rpm

依次手动安装

sudo rpm -ivh mysql-community-common-5.7.33-1.el7.x86_64.rpm
sudo rpm -ivh mysql-community-libs-5.7.33-1.el7.x86_64.rpm
sudo rpm -ivh mysql-community-libs-compat-5.7.33-1.el7.x86_64.rpm
sudo rpm -ivh mysql-community-client-5.7.33-1.el7.x86_64.rpm
sudo rpm -ivh mysql-community-server-5.7.33-1.el7.x86_64.rpm

CentOS7下MySQL-5.7使用yum方式卸载

1、停止MySQL
命令:systemctl stop mysqld
2、查看已安装的mysql
命令:rpm -qa | grep -i mysql
3、卸载mysql,依次卸载第2步骤所列出的有关MySQL的安装包,如
命令:yum remove -y mysql-community-server-5.6.36-2.el7.x86_64
4、删除mysql相关目录
1)使用命令查看mysql相关的文件目录:find / -name mysql
2)依次删除所查到的目录,命令:rm -rf /xxx/xxx/mysql

二、CentOS7下Nginx的安装:


Nginx安装

安装前确认是否已经安装过

sudo yum search nginx

Nginx文档http://nginx.org/en/docs/

Installation on Linux, nginx packages from nginx.org can be used.

Installation instructions
RHEL/CentOS
Debian
Ubuntu
SLES
Alpine

以 CentOS为例进行安装

Install the prerequisites:

sudo yum install yum-utils

To set up the yum repository, create the file named /etc/yum.repos.d/nginx.repo with the following contents:

sudo vi /etc/yum.repos.d/nginx.repo

内容如下:

[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

By default, the repository for stable nginx packages is used. If you would like to use mainline nginx packages, run the following command:

sudo yum-config-manager --enable nginx-mainline

To install nginx, run the following command:

sudo yum install -y nginx

When prompted to accept the GPG key, verify that the fingerprint matches 573B FD6B 3D8F BC64 1079 A6AB ABF5 BD82 7BD9 BF62, and if so, accept it.

常用命令

解释 命令
安装服务 yum install nginx
启动服务 service nginx start
停止服务 service nginx stop
重载服务 service nginx reload

配置完成后启动服务

sudo service nginx start

如果服务器已经启动,当配置发生变化可以直接使用重载服务来更新配置,运维常用,因为不需要停止服务就可以重载新的配置。

三、部署前端网站到nginx下


电商网站的静态文件都放在shop文件夹中,将此文件夹上传到虚拟机的/opt/data/目录下,

对nginx默认配置文件进行改名

$ cd /etc/nginx/conf.d/
$ mv default.conf shop.conf

修改如下


server {
    listen       80;
    server_name  hadoop100;
    root   /opt/data/;

    location /shop/ {
        index  index.html;
        access_log  /var/log/nginx/access.log  main;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

}

重新加载nginx配置,执行:

$ sudo nginx -s reload

在浏览器中验证下是否可以访问到网站,输入:

http://hadoop100/shop/

效果如下:
file

四、Tomcat的安装


准备:安装Java JDK1.8

这里安装openjdk是因为比较简单, 工作场合一定要使用oracle提供的jdk1.8

sudo yum -y install java-1.8.0-openjdk*

这样安装的好处就是环境变量都配好了

可以直接查看版本 java -version

Tomcat安装

下载页面: https://tomcat.apache.org/download-90.cgi

文档:https://tomcat.apache.org/tomcat-9.0-doc/index.html

下载链接

wget https://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-9/v9.0.41/bin/apache-tomcat-9.0.41.tar.gz

解压到后改名tomcat9

tar -zxvf apache-tomcat-9.0.41.tar.gz
mv apache-tomcat-9.0.41 /opt/pkg/tomcat9

由于tomcat的端口8080和大数据环境中的jetty端口冲突,这里修改tomcat的端口位18080

[hadoop@hadoop100 ~]$ cat /opt/pkg/tomcat9/conf/server.xml

...
    <Connector port="18080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />

启动 进入 /bin目录下 运行startup.sh脚本文件

bin]$ ./startup.sh 
...
Tomcat started.

检查tomcat进程信息

$ bin]$ ps -ef | grep tomcat
hadoop    15142      1  1 14:41 pts/0    00:00:05 /home/hadoop/app/jdk1.8.0_211/bin/java -Djava.util.logging.config.file=/home/hadoop/app/tomcat9/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -Dignore.endorsed.dirs= -classpath /home/hadoop/app/tomcat9/bin/bootstrap.jar:/home/hadoop/app/tomcat9/bin/tomcat-juli.jar -Dcatalina.base=/home/hadoop/app/tomcat9 -Dcatalina.home=/home/hadoop/app/tomcat9 -Djava.io.tmpdir=/home/hadoop/app/tomcat9/temp org.apache.catalina.startup.Bootstrap start
hadoop    15296  15064  0 14:47 pts/0    00:00:00 grep --color=auto tomcat

检查对应的监听端口信息

netstat -anpt | grep 15142

修改conf/tomcat-users.xml添加tomcat用户

添加如下内容
<!--  
  <role rolename="tomcat"/>
  <role rolename="role1"/>
  <user username="tomcat" password="tomcat" roles="tomcat"/>
  <user username="both" password="tomcat" roles="tomcat,role1"/>
  <user username="role1" password="tomcat" roles="role1"/>
-->
  <role rolename="manager-gui"/>
  <role rolename="manager-script" />
  <user username="admin" password="123123" roles="manager-gui,manager-script" />
</tomcat-users>

为了能够正常访问tomcat管理界面, 修改webapps/manager/META-INF目录下的context.xml,在allow行的末尾加上\d+.\d+.\d+.\d+表示允许所有主机访问。

<Context antiResourceLocking="false" privileged="true" >
  <Valve className="org.apache.catalina.valves.RemoteAddrValve"
         allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1|\d+\.\d+\.\d+\.\d+" />
  <Manager sessionAttributeValueClassNameFilter="java\.lang\.(?:Boolean|Integer|Long|Number|String)|org\.apache\.catalina\.filters\.CsrfPreventionFilter\$LruCache(?:\$1)?|java\.util\.(?:Linked)?HashMap"/>
</Context>

重启tomcat9生效

bin/shutdown.sh
...
bin/startup.sh
6)验证,在浏览器输入: <code>http://hadoop100:8080/

或者在命令行里面输入:netstat -anop|grep 18080
file

五、电商后台系统部署到Tomcat服务器上


在MySQL创建数据库shop,将项目所需的sql数据导入,如:

mysql> source /opt/data/shop.sql

将代码导入到eclipse或者idea中

将代码数据库连接相关配置hibernate.properties做如下修改:

  • 修改数据库名及地址为服务器的数据库名及地址
  • 修改数据库用户明、密码为服务器的用户密码

file

选择export->war

上传war包至Linux服务器

修改war包名位university.war

将war包移动到Tomcat安装目录下的webapp目录下

启动或重启Tomcat

打开浏览器进行验证:
file

配置nginx反向代理

18080端口的访问方式不太方便,另外也需要解决nginx下部署的静态页面调用后台服务的跨域问题,这里需要设置nginx方向代理,修改nginx配置文件如下:

server {
    listen       80;
    server_name  hadoop100;
    root   /opt/data/;

    location /shop/ {
        index  index.html;
        access_log  /var/log/nginx/access.log  main;
    }

    location /university/ {
        proxy_pass http://hadoop100:18080/university/;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

}

重新加载nginx配置

sudo nginx -s reload

现在可以通过http://hadoop100/university/访问后台界面
file

如果访问失败可以查看错误日志

sudo vi /var/log/nginx/error.log 

如果发现502错误:

2020/11/18 16:12:39 [crit] 16376#16376: *1 connect() to 127.0.0.1:8080 failed (13: Permission denied) while connecting to upstream, client: 192.168.186.1, server: hadoop000, request: "GET /favicon.ico HTTP/1.1", upstream: "http://127.0.0.1:8080/favicon.ico", host: "hadoop000", referrer: "http://hadoop000/"

此时需要考虑把linux操作系统默认的强制访问安全限制设置为禁用。

关闭SElinux即可

  1. 临时关闭 SElinux

    sudo setenforce 0
  2. 永久关闭 SElinux

    sudo vim /etc/selinux/config
    SELINUX=disabled

修改之后就可以正常访问了

通过http://hadoop100/shop访问前端界面,可以看见数据可以正常加载了.

file

至此商城项目的前后端就配置完了, 商城的nginx服务器日志保存在/var/log/nginx/access.log ,接下来我们将使用httpd提供的ab压测工具来快速生成大量访问日志.

Views: 144

Index