谷粒商城-商城业务(首页渲染和nginx)
部署架构
动静分离,把静态资源放在nginx中,这样每次请求静态资源,网关就不需要在把请求转发到微服务中了,分担了微服务的压力。
商品业务
首页
引入依赖
使用Thymeleaf渲染
-
导入
Thymeleaf
的依赖<!--不写版本号,版本交给spring进行管理--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
-
复制
静态资源
和页面
,static文件夹是存放静态资源的,template文件夹是存放页面模板的。 -
测试期间,先修改一下
Thymeleaf
的配置。spring: thymeleaf: cache: false
-
新建一个
web
包,用来开发web页面(页面跳转)。实际上这里的controller
包是做分离的接口。可以用来做app的对接。 -
刷新maven,重启product服务。打开
localhost:<端口>
。
渲染一级分类
-
创建一个首页控制器类。
@Controller public class IndexController { final private CategoryService categoryService; public IndexController(CategoryService categoryService) { this.categoryService = categoryService; } @GetMapping({"/", "/index.html"}) public String indexPage(Model model){ // 获取一级目录 List<CategoryEntity> level1Categories = categoryService.getLevel1Categories(); // 绑定属性 model.addAttribute("level1Categories", level1Categories); return "index"; } }
-
在
index.html
中使用Thymeleaf语法
,渲染出一级目录<div class="header_main_left"> <ul> <li class="header_li2" th:each=" category : ${level1Categories}"> <a href="#" class="header_main_left_a" th:attr="ctg-data=${category.catId}"><b th:text="${category.name}"></b></a> </li> </ul> </div>
-
渲染结果
在服务内使用Thymeleaf模板的话,可以启动devtools依赖,这样就可以快速部署了,每次修改页面后可以快速更新,就不需要总是重启服务了。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <!--不指定版本号,交给spring进行统一管理--> <optional>true</optional> </dependency>
渲染二级和三级分类
在鼠标移动到某个一级分类时,会在右边展示出二级和三级分类。
需要先找到二级和三级分类是在哪里处理的。
从 static/index/js/catalogLoader.js
中可以看到,目录结构是由一个接口返回得到的(现在是用一个json存放了假数据),所以需要分析一下它需要什么样结构的数据,然后我们编写一个接口,返回相同结构的数据。
{
"11": [
{
"catalog1Id": "11",
"catalog3List": [
{
"catalog2Id": "61",
"id": "610",
"name": "商务休闲鞋"
},
{
"catalog2Id": "61",
"id": "611",
"name": "正装鞋"
}
],
"id": "61",
"name": "流行男鞋"
}
],
"12": [...],
}
大致分析一下:首先,这个json
中的结构应该是一个map
: <一级分类id, 二级分类对象列表>
,然后二级分类实体中有4个属性:catalog1Id
, catalog3List
, id
, name
,三级分类实体中有3个属性:catalog2Id
, id
, name
。
/**
* 首页二级分类结构
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Level2CategoryVo {
/**
* 二级分类id
*/
private String id;
/**
* 二级分类名称
*/
private String name;
/**
* 所属的一级分类的id,也就是父id
*/
private String catalog1Id;
/**
* 该二级分类下的三级分类列表
*/
private List<Level3CategoryVo> catalog3List;
}
/**
* 首页三级分类结构
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Level3CategoryVo {
/**
* 三级分类id
*/
private String id;
/**
* 三级分类名称
*/
private String name;
/**
* 所属的二级分类id,父id
*/
private String catalog2Id;
}
我们编写一个接口,把所有的分类获取出来,也组装成这么一个结构,返回给首页,就可以了。
// Controller类
@ResponseBody
@GetMapping("/index/catalog.json")
public Map<String, List<Level2CategoryVo>> getCatalogJson(){
return categoryService.getCatalogJson();
}
// static/index/js/catalogLoader.js
$.getJSON("/index/catalog.json",function (data) {}
// serviceImpl,使用之前的listWithTree()获取树状分类结构,慢慢解析
@Override
public Map<String, List<Level2CategoryVo>> getCatalogJson() {
Map<String, List<Level2CategoryVo>> map = new HashMap<>();
List<CategoryEntityVo> categoryEntityLevel1VoList = this.listWithTree();
// 1.先获取所有一级分类的id,因为是要组合成:map<一级分类id, 二级分类对象列表>
for (CategoryEntityVo categoryEntityLevel1 : categoryEntityLevel1VoList) {
String category1Id = String.valueOf(categoryEntityLevel1.getCatId());
List<CategoryEntityVo> categoryEntityLevel2VoList = categoryEntityLevel1.getChildren();
List<Level2CategoryVo> catalog2List = new ArrayList<>();
// 2.获取一级分类下的二级分类
for (CategoryEntityVo categoryEntityLevel2 : categoryEntityLevel2VoList) {
String category2Id = String.valueOf(categoryEntityLevel2.getCatId());
String category2Name = categoryEntityLevel2.getName();
List<CategoryEntityVo> categoryEntityLevel3VoList = categoryEntityLevel2.getChildren();
List<Level3CategoryVo> catalog3List = new ArrayList<>();
// 3.获取二级分类下的三级分类
for (CategoryEntityVo categoryEntityLevel3 : categoryEntityLevel3VoList) {
String category3Id = String.valueOf(categoryEntityLevel3.getCatId());
String category3Name = categoryEntityLevel3.getName();
Level3CategoryVo level3CategoryVo = new Level3CategoryVo(category3Id, category3Name, category2Id);
catalog3List.add(level3CategoryVo);
}
Level2CategoryVo level2CategoryVo = new Level2CategoryVo(category2Id, category2Name, category1Id, catalog3List);
catalog2List.add(level2CategoryVo);
}
String catId = String.valueOf(categoryEntityLevel1.getCatId());
map.put(catId, catalog2List);
}
return map;
}
Nginx+Windows搭建域名访问环境
配置nginx
-
修改hosts文件,将
虚拟机ip
和一个域名
进行绑定(本地) -
尝试访问
-
配置
Nginx
做一个反向代理-
查看nginx的配置文件
-
到
conf.d
文件夹下添加gulimall.conf
的配置文件(复制一份默认配置,再做修改) -
修改
gulimall.conf
每一份的配置文件都是一个单独的
server
配置。修改前:
修改后:
注意,每条语句后面都需要分号。
请求商城主页的整个过程是:访问gulimall.com --> 访问192.168.121.138:80 --> 即nginx服务器 --> nginx识别请求头中的Host --> 匹配到指定(根据server_name匹配)的server块中 --> 通过配置的规则将请求转发到指定的位置(product服务)。
-
保存完配置后,重启nginx。
-
加入网关
现在的nginx配置是越过了网关,直接将请求转发到服务里面。
现在需要加上网关,让网关做统一的请求转发、负载均衡。
-
在
nginx.conf
中配置上游服务器,用于负载均衡上游服务器名称随意。
-
修改
gulimall.conf
中的server块 -
修改网关的配置,接收nginx转发过来的请求。
使用Host来判断。
-
测试页面
还是不行,404!
是由于:nginx代理给网关的时候,会丢失请求的host信息。所以需要在nginx里再设置。
注意:是在
gulimall.conf
里配置,只给gulimall的请求加上请求头的host。
注意!
这时又有一个非常需要注意的点!!!
网关配置中的路由规则,nginx转发的规则一定不能放在最前面,而且一定要放在最后面。
因为它的路由规则覆盖面太大了。每次用gulimall.com
请求,都会匹配成功。这种覆盖面广的规则一定要放在精确的规则的后面。
# nginx服务器
- id: gulimall-host-route
uri: lb://gulimall-product
predicates:
- Host=gulimall.com