前言
基于上一篇博客静态资源的重复利用问题,这篇博客就利用动静分离把静态资源分开。本篇博客用到的所有资源博主都会分享到博主主页资源里面
一、Nginx实现动静分离
实现思路:首先配置二级域名来模仿基本业务的跳转,然后把项目中所有的静态资源放到Nginx目录下的html文件夹中进行统一管理(需要把项目中的静态资源删除),在就是在Nginx中的conf文件夹中的nginx.conf加入映射(前提是通过域名管理工具进行域名的映射(SwitchHosts)),然后启动Nginx测试静态资源是否访问成功。接着就是修改项目中全部引入的头部文件,要把文件映射成二级域名以至于访问,访问的基本原理就是Nginx启动访问项目,项目通过网关配置的断言去找到网关中对应的微服务,然后找到微服务中的头部文件,头部文件里面放的是Nginx的二级域名模式,这样就直接通过二级域名去访问Nginx中配置的html文件夹,而html文件夹中放入的是项目中所有的静态资源,这样就实现的动静分离!!!
- 通过SwitchHosts新增二级域名:images.zmall.com
- 将本次项目的易买网所有静态资源js/css/images复制到nginx中的html目录下
- 在nginx的核心配置文件nginx.conf中新增二级域名images.zmall.com访问映射,用于实现nginx动静分离
注意:修改成功之后,重启nginx服务使其配置生效!!
server{
listen 80;
server_name images.zmall.com;
location / {
root html;
index index.html;
}
}
- 检测静态资源服务器配置成功
http://images.zmall.com/css/style.css
-
删除zmall-product商品服务和zmall-gateway网关服务以及zmall-user下的static静态资源,改用nginx中配置的静态资源
-
修改zmall-product商品服务中的templates/common/head.html
<#assign ctx>
<#--域名,动态请求时需加入该前缀-->
http://product.zmall.com
</#assign>
<#--采用H5方式的base标签,在整个页面的url地址前加入,用于访问nginx中的静态资源-->
<base href="http://images.zmall.com/"/>
以及其他页面引入的head.html也需要修改成对应的,如服务中的网关微服务
<#assign ctx>
<#--域名,动态请求时需加入该前缀-->
http://gateway.zmall.com
</#assign>
<#--采用H5方式的base标签,在整个页面的url地址前加入,用于访问nginx中的静态资源-->
<base href="http://images.zmall.com/"/>
- 分别重启zmall-product、zmall-gateway以及nginx后输入请求地址:zmall.com/product-serv/index.html访问商品服务首页,如下所示:
如果出现IIS7,那么cmd窗口执行下列指令
net stop w3svc
二、服务调用
2.1 创建配置zmall-cart购物车模块
- 基于Spring initializr创建zmall-cart购物车模块
- 修改pom文件,将zmall-cart购物车模块配置到主模块中
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.zking.zmall</groupId>
<artifactId>zmall</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>zmall-cart</artifactId>
<dependencies>
<dependency>
<groupId>com.zking.zmall</groupId>
<artifactId>zmall-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
- 配置application.yml(端口:8030)
server:
port: 8030
spring:
application:
name: zmall-cart
datasource:
#type连接池类型 DBCP,C3P0,Hikari,Druid,默认为Hikari
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/zmall?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
username: root
password: 123456
freemarker:
suffix: .html
template-loader-path: classpath:/templates/
cloud:
nacos:
config:
server-addr: localhost:8848
#mybatis-plus配置
mybatis-plus:
#所对应的 XML 文件位置
mapper-locations: classpath*:/mapper/*Mapper.xml
#别名包扫描路径
type-aliases-package: com.zking.zmall.model
configuration:
#驼峰命名规则
map-underscore-to-camel-case: true
#日志配置
logging:
level:
com.zking.zmall.mapper: debug
-
在启动类上加入
@EnableDiscoveryClient
-
分别将购物车页面和common/head.html导入到templates目录,并修改head.html中的ctx局部变量
<#assign ctx>
<#--一级域名,动态请求时需加入该前缀-->
http://cart.zmall.com
</#assign>
<#--采用H5方式的base标签,在整个页面的url地址前加入,用于访问nginx中的静态资源-->
<base href="http://images.zmall.com/"/>
- 在zmall-gateway网关服务中配置购物车的路由转发规则(重启gateway网关服务)
# 缺哪kopi哪,一般来说只需要kopi id那一部分
spring:
application:
name: zmall-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
routes:
...
- id: cart_route
uri: lb://zmall-cart # lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
predicates:
- Path=/cart-serv/**
filters:
- StripPrefix=1
#此过滤器设置路由过滤器检查的请求属性,以确定是否应发送原始主机头,而不是由 HTTP 客户端确定的主机头
- PreserveHostHeader
注意:这里要配置过滤器PreserveHostHeader,用于处理重定向时依然已原始主机头发送请求。直白点就是处理二级域名的请求
- 创建CartController并定义请求方法
@Controller
public class CartController {
@RequestMapping("/cart.html")
public String toCart(){
return "buyCar";
}
@RequestMapping("/addCart")
public String addCart(Integer pid,Integer num){
return "redirect:/cart.html";
}
}
注意:这里使用redirect重定向方式跳转页面,在SpringCloud gateway路由转发过程中会导致域名跳转变成了http请求方式,所以必须在Gateway网关服务中进行相关的配置。具体请参考第8步的gateway网关路由配置。
- 在zmall-product模块中修改加入购物车的请求方法,定向到购物车
<td><a href="http://cart.zmall.com/addCart?pid=${(product.id)!}&num=3" class="b_sure">去购物车结算</a><a href="#" class="b_buy">继续购物</a></td>
2.2 创建配置zmall-cart购物车模块
- 基于Spring initializr创建zmall-order订单模块
-
将zmall-order订单模块配置到主模块中
-
修改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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.zking.zmall</groupId>
<artifactId>zmall</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>zmall-order</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>com.zking.zmall</groupId>
<artifactId>zmall-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
- 配置application.yml(端口:8040)
server:
port: 8040
spring:
application:
name: zmall-order
datasource:
#type连接池类型 DBCP,C3P0,Hikari,Druid,默认为Hikari
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/zmall?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
username: root
password: 123456
freemarker:
suffix: .html
template-loader-path: classpath:/templates/
cloud:
nacos:
config:
server-addr: localhost:8848
#mybatis-plus配置
mybatis-plus:
#所对应的 XML 文件位置
mapper-locations: classpath*:/mapper/*Mapper.xml
#别名包扫描路径
type-aliases-package: com.zking.zmall.model
configuration:
#驼峰命名规则
map-underscore-to-camel-case: true
#日志配置
logging:
level:
com.zking.zmall.mapper: debug
- 在启动类上加入
@EnableDiscoveryClient
和@MapperScan({"com.zking.zmall.mapper"})
- 代码生成器生成代码
生成表zmall_order与zmall_order_detail
-
把生成出的mapper文件与model放入到公共模块中,把service文件放入到zmall-order模块中
-
创建OrderController并定义请求接口
@Controller
public class OrderController {
@Autowired
private IOrderService orderService;
@RequestMapping("/orderUserList")
@ResponseBody
public List<Order> orderUserList(){
return orderService.list(new QueryWrapper<Order>()
.eq("userId",18));
}
}
- 在zmall-gateway网关服务中配置购物车的路由转发规则(重启gateway网关服务)
spring:
application:
name: zmall-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
routes:
...
- id: order_route
uri: lb://zmall-order # lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
predicates:
- Path=/order-serv/**
filters:
- StripPrefix=1
- PreserveHostHeader
2.3 服务调用
在zmall-user中通过openfeign方式访问order服务接口
- 定义openfeign接口
@FeignClient("zmall-order")
public interface IOrderFeignService {
@RequestMapping("/orderUserList")
List<Order> orderUserList();
}
-
在zmall-user启动类上设置
@EnableDiscoveryClient
和@EnableFeignClients
-
调用接口并测试接口
@Controller
public class UserController {
@Autowired
private IOrderFeignService orderFeignService;
@RequestMapping("/login.html")
public String toLogin(){
return "login";
}
@RequestMapping("/order.html")
@ResponseBody
public List<Order> orderUserList(){
return orderFeignService.orderUserList();
}
}
- 配置nginx的二级域名(这里把后面所需的域名全部配置完)(改完记得重启Nginx)
server
{
listen 80;
server_name user.zmall.com;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 0;
chunked_transfer_encoding on;
location / {
proxy_pass http://127.0.0.1:8000/user-serv/;
}
}
server
{
listen 80;
server_name product.zmall.com;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 0;
chunked_transfer_encoding on;
location / {
proxy_pass http://127.0.0.1:8000/product-serv/;
}
}
server
{
listen 80;
server_name cart.zmall.com;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 0;
chunked_transfer_encoding on;
location / {
proxy_pass http://127.0.0.1:8000/cart-serv/;
}
}
server
{
listen 80;
server_name order.zmall.com;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 0;
chunked_transfer_encoding on;
location / {
proxy_pass http://127.0.0.1:8000/order-serv/;
}
}
server
{
listen 80;
server_name play.zmall.com;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 0;
chunked_transfer_encoding on;
location / {
proxy_pass http://127.0.0.1:8000/play-serv/;
}
}
server
{
listen 80;
server_name kill.zmall.com;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 0;
chunked_transfer_encoding on;
location / {
proxy_pass http://127.0.0.1:8000/kill-serv/;
}
}
- 启动服务测试
测试链接
http://product.zmall.com/index.html
http://product.zmall.com/product.html?pid=733
http://cart.zmall.com/cart.html
访问第二个发现是404
解决,修改页面残余的元素
三、spring session实战
3.1 什么是Spring Session
SpringBoot整合Spring-Session的自动配置可谓是开箱即用,极其简洁和方便。这篇文章即介绍SpringBoot整合Spring-Session,这里只介绍基于RedisSession的实战。
Spring Session 是Spring家族中的一个子项目,Spring Session提供了用于管理用户会话信息的API和实现。它把servlet容器实现的httpSession替换为spring-session,专注于解决 session管理问题,默认Session信息存储在Redis中,可简单快速且无缝的集成到我们的应用中
spring session官网地址:https://spring.io/projects/spring-session
Spring Session的特性:
- 提供用户session管理的API和实现
- 提供HttpSession,以中立的方式取代web容器的session,比如tomcat中的session
- 支持集群的session处理,不必绑定到具体的web容器去解决集群下的session共享问题
3.2 为什么要使用Spring Session
SpringCloud微服务将一个完整的单体应用拆解成了一个个独立的子服务,而每一个独立的微服务子模块都将部署到不同的服务器中,而服务与服务之间是独立隔离的,这个时候使用要实现服务与服务之间的session会话共享,则需要借助于spring-session框架来解决分布式session管理与共享问题。
3.3 错误案例展示
- 在用户服务zmall-user中编写登录控制器,登录时创建session,并将当前登录用户存储sesion中。(这里可以打断点,以确认真正的进入到该方法中)
@Controller
public class UserController {
@RequestMapping("/login.html")
public String toLogin(HttpSession session){
session.setAttribute("username","admin");
return "login";
}
}
- 在Gateway网关服务中添加用户服务的路由转发规则
spring:
application:
name: zmall-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
routes:
...
- id: user_route
uri: lb://zmall-user # lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
predicates:
- Path=/user-serv/**
filters:
- StripPrefix=1
- PreserveHostHeader
- 在商品服务zmall-product中编写查询控制器,在登录创建session后,使用将sessionId置于cookie中访问。如果没有session将返回错误。
@Controller
public class ProductController {
@RequestMapping("/index.html")
public String index(Model model, HttpSession session){
Object username = session.getAttribute("username");
System.out.println("**********"+username);
return "index";
}
}
- 测试链路
#1.session信息存储
http://localhost:8010/login.html
#2.session信息获取
http://product.zmall.com/index.html
3.4 配置spring-session
在公共模块zmall-common中引入spring-session的pom配置,由于spring-boot包含spring-session的starter模块,所以pom中依赖:
注意:公共模块作为所有微服务子模块的依赖支持,如果不在各服务模块中配置redis支持,会导致启动其他微服务时出现报错情况。
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--spring session-->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<!--commons-pool2-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
分别在商品服务zmall-product和用户服务zmall-user中配置application.yml
spring:
session:
redis:
flush-mode: on_save
namespace: session.zmall
cleanup-cron: 0 * * * * *
store-type: redis
timeout: 1800
redis:
host: localhost
port: 6379
password: 123456
jedis:
pool:
max-active: 100
max-wait: 10
max-idle: 10
min-idle: 10
database: 0
注意配置位置
重新启动zmall-user和zmall-product服务,先访问:http://zmall.com/user-serv/login.html,然后在访问:http://zmall.com/product-serv/index.html;回到zmall-product模块控制台查看session获取情况。
注意:借助网关微服务内部可以访问,不同二级域名之间不可以访问
测试一级域名
但是当我们二级域名访问又拿不到值
3.5 二级域名问题
请分别在用户服务和商品服务中该配置类,解决二级域名访问session无效问题。
@Configuration
public class SessionConfig {
@Bean
public CookieSerializer cookieSerializer(){
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
cookieSerializer.setDomainName("zmall.com");
cookieSerializer.setCookieName("ZMALLSESSION");
return cookieSerializer;
}
@Bean
public RedisSerializer<Object> springSessionDefaultRedisSerializer(){
return new GenericJackson2JsonRedisSerializer();
}
}
测试链路
#1.先访问
http://user.zmall.com/login.html
#2.后访问
http://product.zmall.com/index.html
四、用户登录
第1步:在zmall-common公共模块中创建全局异常处理、响应封装类
第2步:生成user相关表数据,然后将其生成的代码进行分离(跟前面生成代码一样操作)
zmall_user_address,zmall_user
第3步:创建UserVo类
@Data
public class UserVo {
private String loginName;
private String password;
}
第4步:在zmall-user模块中定义IUserService及UserServiceImpl
IUserService
public interface IUserService extends IService<User> {
JsonResponseBody<?> userLogin(UserVo user, HttpServletRequest req, HttpServletResponse resp);
}
UserServiceImpl
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Override
public JsonResponseBody<?> userLogin(UserVo user,
HttpServletRequest req,
HttpServletResponse resp) {
//1.判断用户账号和密码是否为空
if(StringUtils.isEmpty(user.getLoginName())||
StringUtils.isEmpty(user.getPassword()))
return new JsonResponseBody<>(JsonResponseStatus.USERNAME_OR_PWD_EMPTY);
//2.根据用户名查询数据对应的用户信息
User us = this.getOne(new QueryWrapper<User>()
.eq("loginName", user.getLoginName()));
//3.判断us用户对象是否为空
if(null==us)
return new JsonResponseBody<>(JsonResponseStatus.USERNAME_ERROR);
try {
//MD5加密转换处理
String pwd=MD5Utils.md5Hex(user.getPassword().getBytes());
//4.判断输入密码与数据库表存储密码是否一致
if(!us.getPassword().equals(pwd)){
return new JsonResponseBody<>(JsonResponseStatus.PASSWORD_ERROR);
}
} catch (Exception e) {
e.printStackTrace();
return new JsonResponseBody<>(JsonResponseStatus.ERROR);
}
//5.通过UUID生成token令牌并保存到cookie中
String token= UUID.randomUUID().toString().replace("-","");
//将随机生成的Token令牌保存到Cookie中,并设置1800秒超时时间
CookieUtils.setCookie(req,resp,"token",token,7200);
//6.将token令牌与spring session进行绑定并存入redis中
HttpSession session = req.getSession();
session.setAttribute(token,us);
return new JsonResponseBody<>(token);
}
}
第5步:在UserController中定义用户登录方法
package com.zking.zmall.controller;
import com.zking.zmall.model.Order;
import com.zking.zmall.service.IUserAddressService;
import com.zking.zmall.service.impl.UserServiceImpl;
import com.zking.zmall.util.JsonResponseBody;
import com.zking.zmall.vo.UserVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.List;
@Controller
public class UserController {
@Autowired
private IOrderFeignService orderFeignService;
private UserServiceImpl userService;
@RequestMapping("/login.html")
public String login(HttpSession session){
session.setAttribute("username","admin");
return "login";
}
@RequestMapping("/order.html")
@ResponseBody
public List<Order> orderUserList(){
return orderFeignService.orderUserList();
}
/**
* 用户登陆功能实现
* @return
*/
@RequestMapping("/userLogin")
@ResponseBody
public JsonResponseBody<?> userLogin(UserVo user,
HttpServletRequest req,
HttpServletResponse resp){
return userService.userLogin(user,req,resp);
}
}
第6步:在前端login.html页面中定义登录js方法
<script>
$(function(){
$('.log_btn').click(function(){
let loginName=$('.l_user').val();
let password=$('.l_pwd').val();
if(''===loginName){
alert('请输入用户名!');
return false;
}
if(''===password){
alert('请输入密码!');
return false;
}
console.log({
loginName:loginName,
password:password
});
$.post('http://zmall.com/user-serv/userLogin',{
loginName:loginName,
password:password
},function(rs){
console.log(rs);
if(rs.code===200){
location.href='http://zmall.com/product-serv/index.html';
}else{
alert(rs.msg);
}
},'json');
});
});
</script>
第七步:修改配置文件
第八步:修改启动类
加入@MapperScan({
"com.zking.zmall.mapper"})
第九步:测试
转载:https://blog.csdn.net/qq_63531917/article/details/128903080