2.5.2.2 可视化项目, 招聘人数, 热门行业统计, 热门行业招聘对比, 应聘者信息统计, 全国招聘信息, 项目部署nginx, 反向代理, 负载均衡

目录

第4章 行业信息模块

1.行业需求描述

2.表结构分析

3.统计各个城市招聘人数

3.1 统计各个城市招聘人数需求分析

3.2 统计各城市招聘接口

3.3 前后台联调

4.热门行业统计

4.1 热门行业统计需求分析

4.2 热门行业招聘信息接口

4.3 前后台联调

5.热门行业招聘对比统计

5.1 热门行业招聘对比统计需求分析

​5.2 热门行业招聘对比统计接口

5.3 前后台联调

第5章 应聘者信息统计

1 各个学历下的岗位统计需求分析

2 统计学历对应岗位接口

3 前后台联调

第6章 全国招聘信息

1.全国各省招聘需求分析

2. 统计各全国各省招聘接口

3. 前后台联调

4.首页大屏展示图表

第7章 项目部署

1.nginx介绍及使用

1.1 什么是nginx?

1.2 应用场景

1.3 Nginx安装

1.4 配置虚拟主机

1.5 反向代理

1.6 负载均衡


第4章 行业信息模块

1.行业需求描述

从多个维度统计行业信息

1. 统计各个城市招聘人数,以饼状图方式展示个城市人数比例
2. 统计热门行业信息,以柱状图的方式展示前10个热门行业的招聘人数
3. 统计销售行业、服务业、生产制造行业招聘情况,使用面积图参考招聘这3个行业情况。

2.表结构分析

position_type_info_v2 职位类型信息表


position_info_v2 职位信息表

3.统计各个城市招聘人数

3.1 统计各个城市招聘人数需求分析

统计各个城市招聘人数,以饼状图方式展示个城市人数比例

3.2 统计各城市招聘接口

请求路径:countCityPosition
请求方法:post
请求接口 : http://localhost:8080/lg_visualization/industry/countCityPosition.do
请求参数

响应参数


成功响应数据

{
    "status": 0,
    "data": {
        "thridList": [
            {
                "name": "北京",
                "value": "255"
            },
            {
                "name": "广州",
                "value": "201"
            },
            {
                "name": "杭州",
                "value": "190"
            }],

        "dataList": [
            {
                "name": "北京",
                "value": "255"
            },
            {
                "name": "广州",
                "value": "201"
            },
            {
                "name": "杭州",
                "value": "190"
            },
            {
                "name": "深圳",
                "value": "162"
            },
            {
                "name": "天津",
                "value": "150"
            },
            {
                "name": "上海",
                "value": "121"
            },
            {
                "name": "南京",
                "value": "116"
            },
            {
                "name": "济南",
                "value": "107"
            },
            {
                "name": "成都",
                "value": "95"
            },
            {
                "name": "武汉",
                "value": "94"
            }],

        "cityNamesList": [
            "北京",
            "广州",
            "杭州",
            "深圳",
            "天津",
            "上海",
            "南京",
            "济南",
            "成都",
            "武汉"
        ]
    }
}

SQL

-- 统计各个城市职位的数量, 最终找到前十名
SELECT position_city, COUNT(*) cityPositionNum FROM position_info_v2
WHERE position_city != ''
GROUP BY position_city ORDER BY cityPositionNum DESC
LIMIT 10;

Controller

package com.lagou.controller;

@Controller
@RequestMapping("/industry")
public class IndustryController {

    @Autowired
    private IIdustryService iIdustryService;


    /**
     * 1.统计个各个城市招聘人数情况
     * @return
     */
    @RequestMapping("countCityPosition.do")
    @ResponseBody
    public ServerResponse<Map<String,Object>> countCityNums(){

        ServerResponse<Map<String,Object>> response =  iIdustryService.countCityData();
        return response;
    }
}

Service

package com.lagou.service;

public interface IIdustryService {
    /**
     * 1.统计各个城市招聘人数
     * @return
     */
    public ServerResponse<Map<String, Object>> countCityData() ;

}
package com.lagou.service.impl;

@Service
public class IdustryServiceImpl implements IIdustryService {

    @Autowired
    private IdustryMapper mapper;

    /**
     * 统计各个城市招聘情况
     * @return
     */
    @Override
    public ServerResponse<Map<String, Object>> countCityData() {
        //1.查询各个城市招聘情况 , 创建一个对象封装查询的数据 list
        // ConverterVo属于转换对象, 存放数据库查询结果视图中的对象
        List<ConverterVo> cityPositionList = mapper.countCityPosition();

        //2.创建一个Map<String,Object> 存放总数据
        Map<String , Object> map = new HashMap<>();

        //3.创建3个List集合  cityNamesList 存放城市名称  ; thridList 前三名城市数据  dataList 存放十个城市数据
        List<String> cityNamesList = new ArrayList<>();
        List<ConverterVo> thridList = new ArrayList<>();
        List<ConverterVo> dataList = new ArrayList<>();

        //4.循环list ,给cityNamesList   thridList  dataList  封装数据
        for (ConverterVo converterVo : cityPositionList) {
            //设置城市名称
            cityNamesList.add(converterVo.getName());
            //设置thridList
            if(thridList.size() < 3 ){
                thridList.add(converterVo);
            }
            //设置dataList
            dataList.add(converterVo);
        }

        //5.把3个list的数据存到map中
        map.put("cityNamesList",cityNamesList);
        map.put("thridList",thridList);
        map.put("dataList",dataList);

        //6.把map中的数据放到serverresponse中返回
        return ServerResponse.createBySuccessData(map);
    }
}

ConverterVo

package com.lagou.vo;

/**
 * 转换对象:  把数据库中封装好的数据, 转换成  name:xx   value:xxx
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ConverterVo {
    private String name;
    private String value;
}

Mapper

package com.lagou.mapper;

public interface IdustryMapper {
    /**
     * 统计各个城市招聘情况
     * @return
     */
    public List<ConverterVo> countCityPosition() ;
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="com.lagou.mapper.IdustryMapper">

    <!--  1.统计各个城市招聘人数情况 top10 -->
    <!-- 下面的position_city, COUNT(*) 要起别名, 才能适配Vo对象中的属性 -->
    <select id="countCityPosition" resultType="ConverterVo">
        SELECT position_city as name ,COUNT(*) as value FROM position_info_v2
        WHERE position_city != ''
        GROUP BY position_city  ORDER BY value DESC
        LIMIT 10
    </select>

</mapper>

3.3 前后台联调

编写页面recruitment.html

1)引入echats.js文件

<script src="dist/js/echarts.min.js"></script>

2)创建一个div展示区域

<div id="main" style="height: 700px"></div>

3)前台发送异步ajax请求

	<script>
		//1.创建echats对象
		var myChart = echarts.init(document.getElementById("main"));

		//2.发送ajax请求 获取后台的数据
		$.post("/lg_visualization/industry/countCityPosition.do",function(data){
			//3.在回调函数中设置option, 其中内容复制于echarts网站
			myChart.setOption({
				tooltip: {     								//悬浮提示组件
				trigger: 'item',  							//触发类型:  item 如果是散点图 饼图,有触发效果
					formatter: '{a} <br/>{b}: {c} ({d}%)'  	//代表提示悬浮窗的格式  {a} {b} {c} 分别代表 系列名  数据名 数值
				},
				legend: {   						//图例组件
					orient: 'vertical',  			//代表布局方式  vertical 水平布局
					left: 10,   					//图例举例容器左边10像素
					data: data.data.cityNamesList
				},
				series: [  //系列列表
					{
						name: '各个城市招聘人数',   	//系列名称
						type: 'pie',             	//图表类型  pie饼图
						selectedMode: 'single',  	//选中模式 : single 选择单个
						radius: [0, '30%'],     	//代表图表的半径

						label: {        			//饼图标签上的信息
							position: 'inner'   	// 饼图扇区内部展示信息
						},
						labelLine: {   				//标签视觉的引导线
							show: false   			//关闭视觉引导线
						},
						data: data.data.thridList   //内部饼图的数据
					},
					{
						name: '各个城市招聘人数',
						type: 'pie',
						radius: ['40%', '55%'],  	//外层的饼图
						label: {
							formatter: '{a|{a}}{abg|}\n{hr|}\n  {b|{b}:}{c}  {per|{d}%}  ',
							backgroundColor: '#eee',
							borderColor: '#aaa',
							borderWidth: 1,
							borderRadius: 4,
							rich: {    				//自定义富文本样式
								a: {
									color: '#999',
									lineHeight: 22,
									align: 'center'
								},
								hr: {
									borderColor: '#aaa',
									width: '100%',
									borderWidth: 0.5,
									height: 0
								},
								b: {
									fontSize: 16,
									lineHeight: 33
								},
								per: {
									color: '#eee',
									backgroundColor: '#334455',
									padding: [2, 4],
									borderRadius: 2
								}
							}
						},
						data: data.data.dataList  		//外层饼图的数据
					}
				]
			})
		},"json");
	</script>

4.热门行业统计

4.1 热门行业统计需求分析

同一个各个行业招聘人数top10 ,显示柱状图,具体如下:

4.2 热门行业招聘信息接口

请求路径:industryTop.do
请求方法:post
请求接口: http://localhost:8080/lg_visualization/industry/industryTop.do
请求参数

响应参数


{
    "status": 0,
    "data": {
        "names": [
            "销售",
            "技术",
            "运营",
            "教育培训",
            "服务业",
            "设计",
            "人事/财务/行政",
            "生产制造",
            "传媒",
            "房地产/建筑"
        ],
        "count": [
            538522,
            533074,
            367759,
            338040,
            290336,
            261589,
            258828,
            211471,
            165769,
            160022
        ]
    }
}

SQL

-- 统计热门行业top10  行业名称, 招聘数量
SELECT DISTINCT position_type_name, SUM(position_num) AS num
FROM position_type_info_v2
GROUP BY position_type_name ORDER BY num DESC
LIMIT 10;

Controller

package com.lagou.controller;

@Controller
@RequestMapping("/industry")
public class IndustryController {

    @Autowired
    private IIdustryService iIdustryService;


    /**
     * 2.热门行业top10
     */
    @RequestMapping(value = "industryTop.do",method = RequestMethod.POST)
    @ResponseBody
    public ServerResponse<Map<String,Object>> getIndustryTop(){
        ServerResponse<Map<String,Object>> response =  iIdustryService.getIndustryTop10();
        return response;
    }
}

Service

package com.lagou.service;

public interface IIdustryService {

    /**
     * 2.热门行业top10
     */
    ServerResponse<Map<String, Object>> getIndustryTop10();
}
package com.lagou.service.impl;

@Service
public class IdustryServiceImpl implements IIdustryService {

    @Autowired
    private IdustryMapper mapper;

    /**
     * 2.热门行业top10
     */
    @Override
    public ServerResponse<Map<String, Object>> getIndustryTop10() {
        //1.查询最热门的10个行业  List<IndustryVo>
        List<IndustryVo> list =  mapper.countIndustryTop();

        //2.创建map 封装所有数据
        Map<String,Object> map = new HashMap<>();

        //3.创建两个list  namesList 放行业名称   countList 行业数量
        List<String> industryNames = new ArrayList<>();
        List<Long> countList = new ArrayList<>();

        //4.循环给list设置数据
        for (IndustryVo industryVo : list) {
            industryNames.add(industryVo.getPositionName());
            countList.add(industryVo.getNum());
        }

        //5.list存在map中
        map.put("names",industryNames);
        map.put("count",countList);

        //6.map放到ServerResponse
        return ServerResponse.createBySuccessData(map);
    }
}

IndustryVo

package com.lagou.vo;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class IndustryVo {
    private String positionName; //行业名称
    private long  num; //行业招聘数量

    //TODO
}

Mapper

package com.lagou.mapper;

public interface IdustryMapper {

    List<IndustryVo> countIndustryTop();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="com.lagou.mapper.IdustryMapper">

    <!-- 2.统计热门行业top10   -->
    <select id="countIndustryTop" resultType="IndustryVo">
        SELECT DISTINCT position_type_name  positionName, SUM(position_num)  AS num
        FROM position_type_info_v2
        GROUP BY position_type_name ORDER BY num DESC LIMIT 10;
    </select>

</mapper>

4.3 前后台联调

编写页面industryTop.html

1)引入echats.js文件

<script src="dist/js/echarts.min.js"></script>

2)创建一个div展示区域

<div class="panel-body">
    <div id="main" style="height: 700px"></div>
</div>

3)前台发送异步ajax请求

	<script>
		//1.创建echarts对象
		var mychart = echarts.init(document.getElementById("main"));

		//2.发送ajax请求
		$.post("/lg_visualization/industry/industryTop.do",function(data){
			//3.在回调函数中设置 option
			mychart.setOption({
				tooltip: {    				//提示组件
					trigger: 'axis',  		//如果是柱状图或折线图触发
					axisPointer: {    		//坐标轴指示配置项
						type: 'shadow'  	// 默认值 line    shadow 阴影指示器
					}
				},
				grid: {     				//直角坐标系网络 ,网络中绘制折线图  柱状图 ...
					left: '3%',   			//组件举例左侧容器距离
					right: '4%',
					bottom: '3%',
					containLabel: true 		//grid 区域是否有坐标轴的刻度
				},
				xAxis: {
					type: 'value',  		//数值轴
					boundaryGap: [0, 0.01]
				},
				yAxis: {
					type: 'category', 		//类目轴
					data: data.data.names
				},
				series: [
					{
						name: '招聘数量',
						type: 'bar',  					//柱状图
						data: data.data.count,
						label: {  						//图形上添加文字标签
							show:true , 				//是否在标签上展示
							position: 'insideRight' 	//标签数据展示位置
						}
					}
				]
			});
		},"json")
	</script>

5.热门行业招聘对比统计

5.1 热门行业招聘对比统计需求分析

使用面积图展示销售业、服务业、生产制造业的招聘数量图表


5.2 热门行业招聘对比统计接口

请求路径:industryCompare.do
请求方法:post
请求接口 : http://localhost:8080/lg_visualization/industry/industryCompare.do
请求参数

响应参数


成功响应数据

{
    "status": 0,
    "data": {
        "dateTime": [
            "2020-06-04",
            "2020-06-05",
            "2020-06-06",
            "2020-06-07",
            "2020-06-08",
            "2020-06-09"
        ],
        "industryNameList": [
            "销售",
            "服务业",
            "生产制造"
        ],
        "server": [
            20963,
            66650,
            76178,
            72691,
            52483,
            1371
        ],
        "sell": [
            39908,
            114492,
            132010,
            155163,
            92080,
            4869
        ],
        "pro": [
            15965,
            49549,
            52494,
            56354,
            36934,
            175
        ]
    }
}

SQL

-- 统计三个行业(销售, 服务, 生产制造)  在2020-6-4到2020-6-9 招聘人数情况
SELECT position_type_name NAME, SUM(position_num) num, DATE_FORMAT(update_time, '%Y-%m-%d') AS theDay
FROM position_type_info_v2
WHERE position_type_name = '销售'
AND `update_time` >= '2020-6-4' AND `update_time` <= '2020-6-9'
GROUP BY theDay;

SELECT position_type_name NAME, SUM(position_num) num, DATE_FORMAT(update_time, '%Y-%m-%d') AS theDay
FROM position_type_info_v2
WHERE position_type_name = '服务业'
AND `update_time` >= '2020-6-4' AND `update_time` <= '2020-6-9'
GROUP BY theDay;

SELECT position_type_name NAME, SUM(position_num) num, DATE_FORMAT(update_time, '%Y-%m-%d') AS theDay
FROM position_type_info_v2
WHERE position_type_name = '生产制造'
AND `update_time` >= '2020-6-4' AND `update_time` <= '2020-6-9'
GROUP BY theDay;

Controller

package com.lagou.controller;

@Controller
@RequestMapping("/industry")
public class IndustryController {

    @Autowired
    private IIdustryService iIdustryService;

    /**
     * 3.热门行业对比图
     */
    @RequestMapping(value = "industryCompare.do",method = RequestMethod.POST)
    @ResponseBody
    public ServerResponse<Map<String,Object>> getIndustryCompare(){
        ServerResponse<Map<String,Object>> response =  iIdustryService.industryCompare();
        return response;
    }
}

Service

package com.lagou.service;

public interface IIdustryService {

    /**
     * 3.热门行业招聘对比图
     * @return
     */
    ServerResponse<Map<String, Object>> industryCompare();
}
package com.lagou.service.impl;

@Service
public class IdustryServiceImpl implements IIdustryService {

    @Autowired
    private IdustryMapper mapper;

    /**
     * 统计 热门行业(销售 服务业 生产制造) 在2020-6-4 到2020-6-9 招聘信息对比
     * @return
     */
    @Override
    public ServerResponse<Map<String, Object>> industryCompare() {
        //1.创建Map集合,封装最终的数据
        Map<String,Object> map = new HashMap<>();
        //3.循环行业
        // 行业定义成常量
        for(String industryName : Const.INDUSTRY){
            //创建List<Long> 记录行业每天招聘情况
            List<Long> countNumList = new ArrayList<>();
            List<IndustryVo> dataList = mapper.getIndustryData(industryName,Const.START_DATE,Const.END_DATE);

            //4.循环dataList ,目的是给countNumList 设置数据
            for (IndustryVo industryVo : dataList) {
                countNumList.add(industryVo.getNum());
            }

            //5.判断行业,给map中设置值
            if(industryName.equals(Const.INDUSTRY[0])){
                map.put("sell",countNumList);
            }else if(industryName.equals(Const.INDUSTRY[1])){
                map.put("server",countNumList);
            }else if(industryName.equals(Const.INDUSTRY[2])){
                map.put("pro",countNumList);
            }
        }
        map.put("dateTime",Const.DATE_TIME);
        map.put("industryNameList",Const.INDUSTRY);

        return ServerResponse.createBySuccessData(map);
    }
}

Const

package com.lagou.common;

public class Const {
    
    // 传递参数给SQL
    public static final String[] INDUSTRY = {"销售","服务业","生产制造"};

    // 数据筛选的起始/结束时间
    public static final String START_DATE ="2020-06-04 00:00:00";
    public static final String END_DATE ="2020-06-09 23:23:59";

    // 可视化图标的横坐标
    public static final String[] DATE_TIME = {"2020-06-04","2020-06-05","2020-06-06","2020-06-07","2020-06-08","2020-06-09"};
}

IndustryVo

package com.lagou.vo;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class IndustryVo {
    private String positionName; //行业名称
    private long  num; //行业招聘数量
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date theDay;
}

Mapper

package com.lagou.mapper;

public interface IdustryMapper {

    /**
     * 统计热门行业对比
     * @param industryName
     * @param startDate
     * @param endDate
     * @return
     */
    // 多个参数传递, 用@Param注解
    List<IndustryVo> getIndustryData(@Param("industryName") String industryName,
                                     @Param("startDate") String startDate,
                                     @Param("endDate") String endDate);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="com.lagou.mapper.IdustryMapper">

    <!-- 3.统计热门行业对比情况   -->
    <!--下面的 &gt; 是大于号   &lt; 是小于号-->
    <select id="getIndustryData" resultType="IndustryVo">
        SELECT position_type_name  positionName, SUM(position_num) num , DATE_FORMAT(update_time,'%Y-%m-%d') AS theDay
        FROM position_type_info_v2
        WHERE position_type_name = #{industryName}
        AND update_time &gt;= #{startDate}
        AND update_time &lt;= #{endDate}
        GROUP BY theDay
    </select>

</mapper>

5.3 前后台联调

编写页面industry.html
1)创建一个div展示区域

<div class="panel-body">
    <div id="main" style="height: 670px"></div>
</div>

2)前台发送异步ajax请求

	<script>
		//1.创建echart对象
		var mychart = echarts.init(document.getElementById("main"));

		//2.发送ajax请求
		$.post("/lg_visualization/industry/industryCompare.do",function(data){
			//3.设置option
			mychart.setOption({
				title: {
					text: '统计行业招聘对比'
				},
				tooltip: {
					trigger: 'axis',
					axisPointer: {
						type: 'cross',
						label: {
							backgroundColor: '#6a7985'
						}
					}
				},
				legend: {
					data: data.data.industryNameList
				},
				toolbox: {
					feature: {
						saveAsImage: {}
					}
				},
				grid: {
					left: '3%',
					right: '4%',
					bottom: '3%',
					containLabel: true
				},
				xAxis: [
					{
						type: 'category',
						boundaryGap: false,
						data: data.data.dateTime
					}
				],
				yAxis: [
					{
						type: 'value'
					}
				],
				series: [
					{
						name: '销售',
						type: 'line',
						stack: '总量',
						smooth : true, 				//是否平滑展示
						areaStyle: {},
						data: data.data.sell,
						label: {  					//给图形上添加文本标签
							normal :{
								show : true, 		//是否显示标签
								position : 'top' 	//数据展示的位置
							}
						}
					},
					{
						name: '服务业',
						type: 'line',
						stack: '总量',
						smooth : true, 				//是否平滑展示
						areaStyle: {},
						data: data.data.server,
						label: {  					//给图形上添加文本标签
							normal :{
								show : true, 		//是否显示标签
								position : 'top' 	//数据展示的位置
							}
						}
					},
					{
						name: '生产制造',
						type: 'line',
						stack: '总量',
						smooth : true, 				//是否平滑展示
						areaStyle: {},
						data: data.data.pro,
						label: {  					//给图形上添加文本标签
							normal :{
								show : true, 		//是否显示标签
								position : 'top' 	//数据展示的位置
							}
						}
					}
				]
			});
		},"json")
	</script>

第5章 应聘者信息统计

1 各个学历下的岗位统计需求分析

通过柱状图和饼状图统计公司需要的就业人员学历情况,及各个学历占比情况

2 统计学历对应岗位接口

请求路径:eduCount .do
请求方法:post
请求接口 : http://localhost:8080/lg_visualization/edu/eduCount.do
请求参数

响应参数
成功响应数据

{
    "status": 0,
    "data": {
        "names": [
            "中专/中技",
            "初中及以下",
            "大专",
            "本科",
            "硕士",
            "高中"
        ],
        "countNum": [
            204,
            30,
            1273,
            1021,
            21,
            267
        ],
        "eduData": [
            {
                "name": "中专/中技",
                "value": "204"
            },
            {
                "name": "初中及以下",
                "value": "30"
            },
            {
                "name": "大专",
                "value": "1273"
            },
            {
                "name": "本科",
                "value": "1021"
            },
            {
                "name": "硕士",
                "value": "21"
            },
            {
                "name": "高中",
                "value": "267"
            }
        ]
    }
}

SQL

-- 统计不同学历下发布了多少岗位
SELECT edu, COUNT(*)
FROM position_info_v2
WHERE edu IN ('中专/中技', '初中及以下', '博士', '大专', '本科', '硕士', '高中')
GROUP BY edu;

Controller

package com.lagou.controller;

@Controller
@RequestMapping("/edu")
public class EduController {

    @Autowired
    private IEduService service;

    /**
     * 获取不同学历下的岗位数量
     */
    @RequestMapping("/eduCount")
    @ResponseBody
    public ServerResponse<Map<String,Object>> getEduCount(){
        ServerResponse<Map<String,Object>> response = service.getEduData();
        return response;
    }
}

Service

package com.lagou.service;

public interface IEduService {
    /**
     * 统计不同学历下的岗位数量
     * @return
     */
    ServerResponse<Map<String, Object>> getEduData();
}
package com.lagou.service.impl;

@Service
public class IdustryServiceImpl implements IIdustryService {

    @Autowired
    private IdustryMapper mapper;

    /**
     * 统计不同学历对应的岗位数量
     * @return
     */
    @Override
    public ServerResponse<Map<String, Object>> getEduData() {

        //1.创建一个Map集合存放数据
        Map<String,Object> dataMap = new HashMap<>();
        //2.查询数据库, List<ConverterVo>
        // 结果视图封装到ConverterVo中
        List<ConverterVo> list = mapper.getEdu();

        //3.创建两个List集合  namesList 存放名称集合    countList 存放不同的学历招聘个数
        // 下面的数量, 对应ConverterVo中的属性类型, 写成String类型, Mybatis会自动转换
        List<String> namesList = new ArrayList<>();
        List<String> countList = new ArrayList<>();

        //4.循环List<ConverterVo> ; 把数据给nameList countList设置
        for (ConverterVo vo : list) {
            namesList.add(vo.getName());
            countList.add(vo.getValue());
        }

        //5.把3个list存放到map
        dataMap.put("names",namesList);
        dataMap.put("countNum",countList);
        dataMap.put("eduData",list);

        //6.把map放到ServerResponse返回
        return ServerResponse.createBySuccessData(dataMap);
    }
}

ConverterVo

package com.lagou.vo;

/**
 * 转换对象:  把数据库中封装好的数据, 转换成  name:xx   value:xxx
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ConverterVo {
    private String name;
    private String value;
}

Mapper

package com.lagou.mapper;

public interface EduMapper {
    /**
     * 统计不同学历下的岗位
     * @return
     */
    List<ConverterVo> getEdu();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="com.lagou.mapper.EduMapper">
    <!-- 1.同步不同学历下的岗位数量   -->
    <!--下面的 edu 和 count 必须要分别起别名, 和ConverterVo中的属性名一致, 才自动封装-->
    <select id="getEdu" resultType="ConverterVo">
        SELECT edu name, COUNT(*) value
        FROM position_info_v2
        WHERE edu IN ('中专/中技','初中及以下','博士','大专','本科','硕士','高中')
        GROUP BY edu
    </select>
</mapper>

3 前后台联调

编写页面edu.html
1)创建一个div展示区域

<div id="main1" style="height: 700px"></div>

<div id="main2" style="height: 700px"></div>

2)前台发送异步ajax请求

<script>
	//1.创建2个echarts对象
	var myChart1 = echarts.init(document.getElementById("main1"));
	var myChart2 = echarts.init(document.getElementById("main2"));

	//2.发送ajax请求
	$.post("/lg_visualization/edu/eduCount.do",function(data){
		//3.回调函数中,设置两个echarts option
		myChart1.setOption({
			color: ['#3398DB'],
			tooltip: {
				trigger: 'axis',
				axisPointer: {            		// 坐标轴指示器,坐标轴触发有效
					type: 'shadow'        		// 默认为直线,可选为:'line' | 'shadow'
				}
			},
			grid: {
				left: '3%',
				right: '4%',
				bottom: '3%',
				containLabel: true 				//grid 区域是否包含坐标轴的刻度标签
			},
			xAxis: [
				{
					type: 'category',
					data: data.data.names,
					axisTick: {   				//配置坐标轴刻度
						alignWithLabel: true  	//true 有效,保证刻度线和标签对齐
					}
				}
			],
			yAxis: [
				{
					type: 'value'
				}
			],
			series: [
				{
					name: '岗位人数',
					type: 'bar',  				//柱状图
					label: {     				//图上添加文字
						show: true,
						position: 'inside'
					},
					barWidth: '60%',
					data: data.data.countNum
				}
			]
		});

		myChart2.setOption({
			tooltip: {
				trigger: 'item',
				formatter: '{a} <br/>{b} : {c} ({d}%)'
			},
			legend: {
				orient: 'vertical',
				left: 'left',
				data: data.data.names
			},
			series: [
				{
					name: '学历占比',
					type: 'pie',
					radius: '55%',  			//代表饼图的半径
					center: ['50%', '60%'],  	//饼图圆心的坐标
					data: data.data.eduData,
					emphasis: {   				//高亮扇区和标签的样式
						itemStyle: {
							shadowBlur: 10,
							shadowOffsetX: 0,
							shadowColor: 'rgba(0, 0, 0, 0.5)'
						}
					}
				}
			]
		});
	},"json")
</script>

第6章 全国招聘信息

1.全国各省招聘需求分析

以地图的方式展示每个省份的招聘人数

2. 统计各全国各省招聘接口

请求路径:mapData
请求方法:post
请求接口
http://localhost:8080/lg_visualization/map/mapData.do
请求参数 无

响应参数

成功响应数据

{
	"status":0,
	"data": [
		{
			"name": "北京",
			"value": 255
		},
		{
			"name": "上海",
			"value": 121
		},
		{
			"name": "天津",
			"value": 150
		},
		{
			"name": "重庆",
			"value": 85
		},
		{
			"name": "黑龙江",
			"value": 16
		},
		{
			"name": "吉林",
			"value": 29
		},
		{
			"name": "辽宁",
			"value": 80
		},
		{
			"name": "内蒙古",
			"value": 15
		},
		{
			"name": "宁夏",
			"value": 17
		},
		{
			"name": "新疆",
			"value": 18
		},
		{
			"name": "青海",
			"value": 6
		},
		{
			"name": "甘肃",
			"value": 15
		},
		{
			"name": "陕西",
			"value": 65
		},
		{
			"name": "河北",
			"value": 144
		},
		{
			"name": "河南",
			"value": 142
		},
		{
			"name": "山东",
			"value": 369
		},
		{
			"name": "山西",
			"value": 54
		},
		{
			"name": "湖北",
			"value": 102
		},
		{
			"name": "湖南",
			"value": 101
		},
		{
			"name": "安徽",
			"value": 162
		},
		{
			"name": "江苏",
			"value": 315
		},
		{
			"name": "浙江",
			"value": 300
		},
		{
			"name": "江西",
			"value": 56
		},
		{
			"name": "广东",
			"value": 617
		},
		{
			"name": "广西",
			"value": 84
		},
		{
			"name": "福建",
			"value": 187
		},
		{
			"name": "四川",
			"value": 155
		},
		{
			"name": "云南",
			"value": 24
		},
		{
			"name": "贵州",
			"value": 12
		},
		{
			"name": "西藏",
			"value": 1
		},
		{
			"name": "海南",
			"value": 13
		},
		{
			"name": "香港",
			"value": 0
		},
		{
			"name": "澳门",
			"value": 0
		},
		{
			"name": "台湾",
			"value": 0
		}
	]
}

将各个市的招聘人数汇总为个省招聘人数

java

SQL

-- 1. 查询每个城市有多少招聘岗位
SELECT position_city cName, COUNT(*) num
FROM position_info_v2
WHERE position_city != ''
GROUP BY position_city
ORDER BY num DESC;

-- 2. 查询每个省有多少个城市
SELECT p.`ProvinceID`, p.`NAME` NAME, c.`NAME` cName
FROM Province p
LEFT JOIN City c
ON c.`ProvinceID`=p.`ProvinceID`;

Controller

package com.lagou.controller;

@Controller
@RequestMapping("/map")
public class CountryMapController {

    @Autowired
    private ICountryMapService service;

    /**
     * 统计各省的招聘人数
     */
    @RequestMapping(value = "mapData.do",method = RequestMethod.POST)
    @ResponseBody
    public ServerResponse<List<ProvinceVo>>  countProvinceData(){
        ServerResponse<List<ProvinceVo>> response = service.getMapData();
        return response;
    }
}

ProvinceVo

package com.lagou.vo;

/**
 * 省的视图对象
 * 作用1: 查找数据库的时候,数据映射
 * 作用2: 把省份 对应的招聘人数,返回给前台  long 招聘人数
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProvinceVo {
    @JsonIgnore             // 序列化给前台, 加了该注解的字段, 自动忽略
    private int pid;        //省编号
    private String name;    //省名称
    private long value;     //该省份的招聘人数
    @JsonIgnore
    private List<CityVo> city;  // 省中的市
}

CityVo

package com.lagou.vo;

/**
 * 城市的视图对象
 *
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CityVo {
    private int cid;        //城市的id
    private String cName;   //城市名称
    private long num;       //城市招聘数量
}

Service

package com.lagou.service;

public interface ICountryMapService {
    /**
     * 统计各个省份的招聘人数
     * @return
     */
    ServerResponse<List<ProvinceVo>> getMapData();
}
package com.lagou.service.impl;

@Service
public class CountryMapServiceImpl  implements ICountryMapService {

    @Autowired
    private CountryMapMapper mapper;

    /**
     * 获取各省招聘人数
     * @Return
     */
    @Override
    public ServerResponse<List<ProvinceVo>> getMapData() {
        //1.查询各个省份有哪些城市  List<ProvinceVo>   没有城市, 招聘数量0
        List<ProvinceVo>  provinceVoList = mapper.queryProvinceCity();

        //2.查询每个城市招聘数量 List<CityVo>  有城市招聘数量
        List<CityVo>  cityList = mapper.queryCityNum();

        //3.调用转换类的转换方法,传递 List<ProvinceVo>  List<CityVo>  --->  CityVo中的人数累加到ProvinceVo中
        List<ProvinceVo> provinceVos = CityProvinceConverter.converter(cityList,provinceVoList);

        //4.返回ServerResponse ,把List<ProvinceVo>放到response中
        return ServerResponse.createBySuccessData(provinceVos);
    }
}

Mapper

package com.lagou.mapper;

public interface CountryMapMapper {
    /**
     * 通过省份的城市
     * @return
     */
    List<ProvinceVo> queryProvinceCity();

    /**
     * 通过各个城市的招聘数量
     * @return
     */
    List<CityVo> queryCityNum();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="com.lagou.mapper.CountryMapMapper">

    <!-- autoMapping属性表示, 属性和字段名相同时, 下面可以不进行该映射配置 -->
   <resultMap id="provinceResultMap" type="ProvinceVo" autoMapping="true"  >
        <!-- 主键映射      -->
        <id column="ProvinceID" property="pid"></id>
       <!-- 普通字段映射      -->
       <result column="name" property="name"/>
       <!-- 映射集合数据类型       -->
       <collection property="city" javaType="list"  ofType="CityVo" autoMapping="true">
           <!-- 主键映射  -->
           <id column="CityID" property="cid" />
       </collection>
   </resultMap>

    <!--  1.城市有多少个招聘岗位-->
    <select id="queryCityNum" resultType="CityVo">
        SELECT position_city cName ,COUNT(*) num
        FROM position_info_v2
        WHERE position_city != ''
        GROUP BY position_city  ORDER BY num DESC;
    </select>

    <!--  2.查询省份有多少个城市  -->
    <!-- 一对多查询, 所以要手动映射 -->
    <select id="queryProvinceCity" resultMap="provinceResultMap">
        SELECT p.ProvinceID , p.Name  NAME , c.NAME cName
        FROM Province p
        LEFT JOIN City c
        ON c.ProvinceID = p.ProvinceID
    </select>
    
</mapper>

Converter

package com.lagou.common;

/**
 * 转换器类
 */
public class CityProvinceConverter {

    /**
     * 作用:  cityList 中的招聘数 ===> provinceVoList 中的value值中
     * @param cityList
     * @param provinceVoList
     * @return
     */
    public static List<ProvinceVo> converter(List<CityVo> cityList, List<ProvinceVo> provinceVoList) {

        //cityList  [ {北京  255  }, {上海 300 , 杭州 100} ]

        //provinceVoList  [{浙江  0 [杭州,宁波..]} ,{浙江  0 [杭州,宁波..]}]

        //1.循环省
        flag:   for (ProvinceVo provinceVo : provinceVoList) {
            //统计每一个省的招聘数量
            long sumCount = 0;
            String provinceVoName = provinceVo.getName();
            List<CityVo> pCityList = provinceVo.getCity();
            //用来装省中的城市的名称
            List<String> pCityNameList = new ArrayList<>();
            for (CityVo cityVo : pCityList) {
                //填充城市的名称
                pCityNameList.add(cityVo.getCName());
            }

            //循环查询到的cityList
            // cityList.for 可以直接获取该集合的增强循环
            for (CityVo cityVo : cityList) {
                //判断有些省的名称不要是 北京  上海  还有天津 重庆...
                if(provinceVoName.equals("北京") && cityVo.getCName().equals("北京")){
                    //给北京设置招聘人数
                    provinceVo.setValue(cityVo.getNum());
                    // 省市名都是 北京 时, 直接跳出到flag进行下一个省的循环
                    continue flag;
                } else if(provinceVoName.equals("上海") && cityVo.getCName().equals("上海")){
                    provinceVo.setValue(cityVo.getNum());
                    continue flag;
                } else if(provinceVoName.equals("天津") && cityVo.getCName().equals("天津")){
                    provinceVo.setValue(cityVo.getNum());
                    continue flag;
                } else if(provinceVoName.equals("重庆") && cityVo.getCName().equals("重庆")){
                    provinceVo.setValue(cityVo.getNum());
                    continue flag;
                } else {
                    //如果省份的 城市名称集合 包含 当前城市 的名字,累加sumCount 设置给provinceVo
                    if(pCityNameList.contains(cityVo.getCName())){
                        sumCount += cityVo.getNum();
                    }
                }
                provinceVo.setValue(sumCount);
            }
        }
        return provinceVoList;
    }
}

3. 前后台联调

编写页面map.html

1)创建一个div展示区域

<div id="main" style="width: 800px; height: 700px; margin-left: 300px" ></div>

2)前台发送异步ajax请求

	<script>
		var myChart = echarts.init(document.getElementById('main'));
		//执行图表的配置和数据
		var titleName = "2020年全国各个城市招聘数量";
		var mapName = "china";

		$.post("/lg_visualization/map/mapData.do",function(data){
			myChart.setOption({
				title: {
					text: titleName,
					x: 'center',
					textStyle: {
						fontSize: 24
					}
				},
				tooltip: {    						//提示框组件
					trigger: "item",  				//数据项图形触发,主要在散点图,饼图等无类目轴的图表中使用
					formatter: function (params) {	//提示框浮层内容格式器,支持字符串模板和回调函数两种形式。
						return params.name + "招聘人数:" + params.value + "人";
					}
				},
				toolbox: {
					feature: {   				//各工具配置项
						saveAsImage: {}  		//保存为图片
					}
				},
				visualMap: {  					//是视觉映射组件
					show: true,  				//是否显示 visualMap-piecewise 组件
					left: "left",				//visualMap 组件离容器左侧的距离
					top: "bottom",   			//visualMap 组件离容器上侧的距离
					seriesIndex: [0],  			//指定取哪个系列的数据,即哪个系列的
					type: "piecewise",  		//分段型视觉映射组件
					pieces: [ 					//分段式视觉映射组件的每一段的范围
						{ge : 30000},  			//设置大于10000人数
						{ge :10000 , lte: 29999}, //设置人数1000-9999
						{ge :  5000, lte : 9999} ,
						{ge : 1000 , lte :  4999},
						{ge : 0 , lte : 999}
					],
					inRange : { 				//定义 在选中范围中 的视觉元素
						color : ['rgb(168,188,180)','rgb(141,188,180)', 'rgb(200,200,160)' , 'rgb(249,207,178)' , 'rgb(254,57,101)']
					},
					textStyle: {
						color: "#000000"
					}
				},
				geo: {  				//地理坐标系组件
					show: true,  		//是否显示地理坐标系组件
					map: mapName,  		//地图类型。
					label: { 			//图形上的文本标签,可用于说明图形的一些数据信息
						normal: {
							show: true,
							fontSize: 12
						},
						emphasis: {
							show: true
						}
					},
					roam: false, 		//是否开启鼠标缩放和平移漫游。默认不开启
					itemStyle : {
						normal: { 		//普通状态下样式
							areaColor: "#FFFFFF",
							borderColor: "#666666",
						},
						emphasis: { 	//高亮状态下的多边形和标签样式。
							areaColor: "#0099CC"  //地图区域的颜色。
						}
					}
				},
				series: [
					{
						type: "map",
						map: mapName,
						geoIndex: 0, 		// geoIndex 指定一个 geo 组件
						animation: false,  	//是否开启动画
						data:data.data
					},
				]
			})
		})

	</script>

4.首页大屏展示图表

实现方式:
创建bigSreen.html ,在该页面使用iframe引入 recruitment2.html map2.html industryCompare2.html
下面是bigSreen.html 代码细节

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
	<title>Elmer I Fast build Admin dashboard for any platform</title>
	<meta name="description" content="Elmer is a Dashboard & Admin Site Responsive Template by hencework." />
	<meta name="keywords" content="admin, admin dashboard, admin template, cms, crm, Elmer Admin, Elmeradmin, premium admin templates, responsive admin, sass, panel, software, ui, dwh, web app, application" />
	<meta name="author" content="hencework"/>

	<!-- Morris Charts CSS -->
    <link href="vendors/bower_components/morris.js/morris.css" rel="stylesheet" type="text/css"/>
	<!-- Calendar CSS -->
	<link href="vendors/bower_components/fullcalendar/dist/fullcalendar.css" rel="stylesheet" type="text/css"/>
	<!-- bootstrap-select CSS -->
	<link href="vendors/bower_components/bootstrap-select/dist/css/bootstrap-select.min.css" rel="stylesheet" type="text/css"/>
	<!-- switchery CSS -->
	<link href="vendors/bower_components/switchery/dist/switchery.min.css" rel="stylesheet" type="text/css"/>
	<!-- Data table CSS -->
	<link href="vendors/bower_components/datatables/media/css/jquery.dataTables.min.css" rel="stylesheet" type="text/css"/>
	<!-- Custom CSS -->
	<link href="dist/css/style.css" rel="stylesheet" type="text/css">
</head>

<body>
	<!--Preloader-->
	<div class="preloader-it">
		<div class="la-anim-1"></div>
	</div>
	<!--/Preloader-->
    <div class="wrapper  theme-1-active pimary-color-blue" id="app">
        <!-- Main Content -->
				<!-- Row -->
				<div class="row">
					<div class="col-lg-6 col-md-12 col-sm-12 col-xs-12">
						<div class="panel panel-default card-view panel-refresh">
							<div class="refresh-container">
								<div class="la-anim-1"></div>
							</div>
							<div class="panel-heading">
								<div class="pull-left">
									<h6 class="panel-title txt-dark">各个城市招聘统计</h6>
								</div>
								<div class="pull-right">
									<a href="#" class="pull-left inline-block refresh mr-15">
										<i class="zmdi zmdi-replay"></i>
									</a>
								</div>
								<div class="clearfix"></div>
							</div>
							<div class="panel-wrapper collapse in">
								<div class="panel-body">
									<!-- 1.引入recruitment2 各个城市招聘信息 -->
									<iframe src="recruitment2.html" style="width: 600px;height: 378px" scrolling="no"></iframe>
								</div>
							</div>
						</div>
					</div>
					<div class="col-lg-6 col-md-12 col-sm-12 col-xs-12">
						<div class="panel panel-default card-view panel-refresh">
							<div class="refresh-container">
								<div class="la-anim-1"></div>
							</div>
							<div class="panel-heading">
								<div class="pull-left">
									<h6 class="panel-title txt-dark">全国各省招聘人数统计</h6>
								</div>
								<div class="pull-right">
									<a href="#" class="pull-left inline-block refresh mr-15">
										<i class="zmdi zmdi-replay"></i>
									</a>
								</div>
								<div class="clearfix"></div>
							</div>
							<div class="panel-wrapper collapse in">
								<div class="panel-body">
									<!-- 2.引入各个城市招聘地图 -->
									<iframe src="map2.html" style="width: 600px;height: 378px" scrolling="no"></iframe>
								</div>
							</div>
						</div>
					</div>
				</div>
				<!-- /Row -->

				<!-- Row -->
				<div class="row">
					<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
						<!-- 3.引入热门行业对比 -->
						<iframe src="industryCompare2.html" style="width: 1350px;height: 280px" scrolling="no"></iframe>
					</div>
				</div>
				<!-- /Row -->
        <!-- /Main Content -->

    </div>
    <!-- /#wrapper -->
	<!-- JavaScript -->
    <!-- jQuery -->
    <script src="vendors/bower_components/jquery/dist/jquery.min.js"></script>
    <!-- Bootstrap Core JavaScript -->
    <script src="vendors/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
	<!-- Calender JavaScripts -->
	<script src="vendors/bower_components/moment/min/moment.min.js"></script>
	<script src="vendors/jquery-ui.min.js"></script>
	<script src="vendors/bower_components/fullcalendar/dist/fullcalendar.min.js"></script>
	<script src="dist/js/fullcalendar-data.js"></script>
	<!-- Progressbar Animation JavaScript -->
	<script src="vendors/bower_components/waypoints/lib/jquery.waypoints.min.js"></script>
	<script src="vendors/bower_components/jquery.counterup/jquery.counterup.min.js"></script>
	<!-- Data table JavaScript -->
	<script src="vendors/bower_components/datatables/media/js/jquery.dataTables.min.js"></script>
	<!-- Slimscroll JavaScript -->
	<script src="dist/js/jquery.slimscroll.js"></script>
	<!-- ChartJS JavaScript -->
	<script src="vendors/chart.js/Chart.min.js"></script>
	<!-- Fancy Dropdown JS -->
	<script src="dist/js/dropdown-bootstrap-extended.js"></script>
	<!-- Sparkline JavaScript -->
	<script src="vendors/jquery.sparkline/dist/jquery.sparkline.min.js"></script>
	<script src="vendors/bower_components/jquery.easy-pie-chart/dist/jquery.easypiechart.min.js"></script>
	<!-- Morris Charts JavaScript -->
    <script src="vendors/bower_components/raphael/raphael.min.js"></script>
    <script src="vendors/bower_components/morris.js/morris.min.js"></script>
    <script src="dist/js/morris-data.js"></script>
	<!-- Owl JavaScript -->
	<script src="vendors/bower_components/owl.carousel/dist/owl.carousel.min.js"></script>
	<!-- Bootstrap Select JavaScript -->
	<script src="vendors/bower_components/bootstrap-select/dist/js/bootstrap-select.min.js"></script>
	<!-- Switchery JavaScript -->
	<script src="vendors/bower_components/switchery/dist/switchery.min.js"></script>
	<!-- Init JavaScript -->
	<script src="dist/js/init.js"></script>
	<script src="dist/js/widgets-data.js"></script>
</body>

</html>

第7章 项目部署

1.nginx介绍及使用

1.1 什么是nginx?

Nginx是一款轻量级的 Web服务器 ,  反向代理服务器 电子邮件代理服务器

优点:
    1. 占用内存少,并发能力强.
    2. Nginx专为性能优化而开发, 在高连接并发的情况下,能够支持高达 50,000 个并发连接数的响应.
    3. Nginx支持热部署, 可以在不间断服务的情况下,对软件版本进行升级.

1.2 应用场景

    1. http服务器: Nginx是一个http服务可以独立提供http服务。可以做网页静态服务器。
    2. 虚拟主机: 可以实现在一台服务器虚拟出多个网站。例如个人网站使用的虚拟主机。
    3. 反向代理,负载均衡 : 当网站的访问量达到一定程度后,单台服务器不能满足用户的请求时,需要用多台服务器集群可以使用nginx做反向代理。并且多台服务器可以平均分担负载,不会因为某台服务器负载高宕机而某台服务器闲置的情况。

1.3 Nginx安装

下载nginx, 官方网站:http://nginx.org/en/download.html
我们使用的版本是1.17.8版本。


Nginx在Linux下安装,只提供了源代码,所以我们需要进行编译.

安装环境配置
1.因为Nginx是C语言编写的,所以需要配置C语言编译环境 (一定要在联网状态下安装)
需要安装gcc的环境。执行命令:

yum install gcc-c++

2.第三方的开发包, 在编译之前需要安装这些第三方包。

PCRE
nginx的http模块使用pcre来解析正则表达式,所以需要在linux上安装pcre库

安装命令:

yum install -y pcre pcre-devel

zlib
nginx使用zlib对http包的内容进行gzip,所以需要在linux上安装zlib库。
安装命令:

yum install -y zlib zlib-devel

openssl
OpenSSL 是一个强大的安全套接字层密码库,nginx不仅支持http协议,还支持https,所以需要在linux安装openssl库。
安装命令:

yum install -y openssl openssl-devel

安装Nginx 步骤

1. 将Nginx的源码包上传到 Linux
2. 解压Nginx

tar -xvf nginx-1.17.8.tar


1. 进入到解压之后的目录 nginx-1.17.8


1. 创建一个临时目录

mkdir /var/temp/nginx/client -p

2. 执行命令 configure,生成 Mikefile 文件

./configure \
--prefix=/usr/local/nginx \
--pid-path=/var/run/nginx/nginx.pid \
--lock-path=/var/lock/nginx.lock \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--with-http_gzip_static_module \
--http-client-body-temp-path=/var/temp/nginx/client \
--http-proxy-temp-path=/var/temp/nginx/proxy \
--http-fastcgi-temp-path=/var/temp/nginx/fastcgi \
--http-uwsgi-temp-path=/var/temp/nginx/uwsgi \
--http-scgi-temp-path=/var/temp/nginx/scgi

 执行命令后, 生成了MakeFile文件

执行make命令,进行编译

make

安装

make install

启动并访问 Nginx

1. 进入到nginx 安装目录

cd /usr/local/nginx/

1. 进入到 sbin目录,执行  nginx 命令

./nginx                        启动
./nginx -s stop            关闭
./nginx -s reload         重启
ps aux | grep nginx    查看进程

1. 通过浏览器进行访问 ,默认端口 80 (注意:是否关闭防火墙。)

1.4 配置虚拟主机

虚拟主机指的是,在一台服务器中,我们使用Nginx,来配置多个网站.
如何区分不同的网站:
    1. 端口不同
    2. 域名不同

通过端口区分不同的虚拟主机
Nginx配置文件

1.Nginx配置文件的位置

cd /usr/local/nginx/conf
nginx.conf  就是Nginx的配置文件

2.Nginx核心配置文件说明

worker_processes  1; #work的进程数,默认为1
#配置 影响nginx服务器与用户的网络连接
events {
    worker_connections  1024; #单个work 最大并发连接数
}
# http块是配置最频繁的部分 可以嵌套多个server,配置代理,缓存,日志定义等绝大多数功能
http {
     # 引入mime类型定义文件
     include    mime.types;
     default_type application/octet-stream;
     sendfile    on;
     keepalive_timeout  65; # 超时时间

     #server 配置虚拟主机的相关参数 可以有多个,一个server就是一个虚拟主机
     server {
        # 监听的端口
         listen    80;
        #监听地址
         server_name localhost;    

        # 默认请求配置
         location / {
             root  html; # 默认网站根目录
             index index.html index.htm; # 欢迎页
         }

        # 错误提示页面
         error_page  500 502 503 504 /50x.html;
         location = /50x.html {
             root  html;
         }
    }
}

使用Notpad,连接Linux

使用notepad++来连接linux,好处是使用notepad++来编辑linux中文件的批量文字,会比直接在linux中操作方便快捷很多.
1. Notepad 插件中安装NppFTP

1. 选择设置


1. 配置连接信息


1. 连接

配置nginx.conf

1. 使用Notpad 在nginx.conf 中添加一个 新的server

http {
     include    mime.types;
     default_type application/octet-stream;
     sendfile    on;
     #tcp_nopush   on;
     #keepalive_timeout 0;
     keepalive_timeout  65;
     #gzip on;
     server {
         listen    80;
         server_name localhost;
         location / {
             root  html;
             index index.html index.htm;
         }
     }
    # 配置新的server
     server {
         listen    81; # 修改端口
         server_name localhost;
         location / {
              root  html81; # 重新制定一个目录
              index index.html index.htm;
         }
     }
}

1. 复制一份 html目录

cp -r html html81

1. 重新加载配置文件

sbin/nginx -s reload

1. 访问
http://192.168.80.100 访问第一个server


http://192.168.81.100:81/ 访问第二个server

通过域名区分不同的虚拟主机

配置域名映射

1. 本地测试可以修改hosts文件。修改window的hosts文件:(C:\Windows\System32\drivers\etc)
    可以配置域名和ip的映射关系,如果hosts文件中配置了域名和ip的对应关系,不需要走dns服务器。

配置一下nginx的映射
182.168.80.100 www.ng.com


在window系统中配置host文件

192.168.80.100 www.taobao.com
192.168.80.100 www.jd.com

配置nginx.conf

#通过域名区分虚拟主机
    server {
        listen 80;
        server_name www.taobao.com;
        location / {
            root taobao;
            index taobao.html;
        }
    }
    server {
        listen 80;
        server_name www.jd.com;
        location / {
            root jd;
            index jd.html;
    }
}


修改一下index.html 中,刷新

sbin/nginx -s reload

访问

虽然只有一台服务器,但是这台服务器上运行着多个网站,访问不同的域名 就可访问到不同的网站内容

1.5 反向代理

正向代理

比如我们国内访问谷歌,直接访问访问不到,我们可以通过一个正向代理服务器,先将请求发送到到代理服,代理服务器能够访问谷歌,这样由代理去谷歌取到返回数据,再返回给我们,这样我们就能访问谷歌了'

Nginx实现反向代理

Nginx作为反向代理服务器安装在服务端,Nginx的功能就是把请求转发给后面的应用服务器.

配置步骤

第一步:简单的使用2个tomcat实例模拟两台http服务器,分别将tomcat的端口改为8080和8081

第二步:启动两个tomcat。

./bin/startup.sh

访问两个tomcat
http://192.168.52.100:8080/
http://192.168.52.100:8081/

第三步:反向代理服务器的配置

#反向代理配置
#upstream中的server是真正处理请求的应用服务器地址
upstream lagou1{
#用server定义HTTP地址
    server 192.168.80.100:8080;
}

server {
    listen    80;
    server_name www.lagou1.com;
    location / {
    # 利用 proxy_ pass可以将请求代理到upstream命名的HTTP服务
        proxy_pass http://lagou1;  #转发到的地址
        index index.html index.htm;
    }
}

upstream lagou2{
    #用server定义HTTP地址
    server 192.168.80.100:8081;
}

server {
    listen    80;
    server_name www.lagou2.com;
    location / {
        proxy_pass http://lagou2;
        index index.html index.htm;
    }
}

第四步:nginx重新加载配置文件

nginx -s reload

第五步:配置域名, 在hosts文件中添加域名和ip的映射关系

192.168.80.100 www.lagou1.com
192.168.80.100 www.lagou2.com

通过浏览器输入域名, 访问Nginx代理服务器, Nginx根据域名将请求转发给对应的目标服务器,作为用户我们看到的是服务器的响应结果页面,在整个过程中目标服务器相对于客户端是不可见的,服务端向外暴露的就是Nginx的地址.

1.6 负载均衡

什么是负载均衡
当一个请求发送过来的时候,Nginx作为反向代理服务器,会根据请求找到后面的目标服务器去处理请求,这就是反向代理. 那么, 如果目标服务器有多台的话,找哪一个服务器去处理当前请求呢 ? 这个合理分配请求到服务器的过程就叫做负载均衡.

为什么用负载均衡

当系统面临大量用户访问,负载过高的时候,通常会使用增加服务器数量来进行横向扩展, 负载均衡主要是为了分担访问量,将请求合理分发给不同的服务器, 避免临时的网络堵塞

负载均衡策略

轮询

默认策略, 每个请求按照时间顺序逐一分配到不同的服务器,如果某一个服务器下线,能自动剔除

配置方式

负载均衡

#配置负载均衡
upstream lagouedu{ 

	# 用server定义 HTTP地址 
	server 192.168.80.100:8080; 
	server 192.168.80.100:8081; 
}

server { 
	listen 80; 

	server_name www.lagouedu.com;

	location / {
		# 利用 proxy_ pass可以将请求代理到upstream命名的HTTP服务
		proxy_pass http://lagouedu;
		index index.html;
	}
}


weight

可以根据服务器的实际情况调整服务器权重。权重越高分配的请求越多,权重越低,请求越少。默认是都是1.
#负载均衡

upstream lagouServer{
    # 用server定义 HTTP地址
    server 192.168.52.100:8080 weight=1;
    server 192.168.52.100:8081 weight=10;
}


2.发布web项目

1.项目打包

2.发布web项目到tomcat下

复制到linux中tomcat1 和tomcat2的webapps目录下

启动tomcat1 tomcat2

[root@centos7-1 bin]# ./startup.sh && tail -100f ../logs/catalina.out

3.配置nginx负载均衡

upstream vis{ 
    # 用server定义 HTTP地址 
    server 192.168.80.100:8080; 
    server 192.168.80.100:8081; 
}
    
server { 
    listen 80; 
    server_name www.vis.com;
    location / {
        # 利用 proxy_ pass可以将请求代理到upstream命名的HTTP服务
        proxy_pass http://vis;
        index login.html ;
    }
}

猜你喜欢

转载自blog.csdn.net/chengh1993/article/details/111407031