秒杀项目之服务调用&分布式session

前言

基于上一篇博客静态资源的重复利用问题,这篇博客就利用动静分离把静态资源分开。本篇博客用到的所有资源博主都会分享到博主主页资源里面

一、Nginx实现动静分离

实现思路:首先配置二级域名来模仿基本业务的跳转,然后把项目中所有的静态资源放到Nginx目录下的html文件夹中进行统一管理(需要把项目中的静态资源删除),在就是在Nginx中的conf文件夹中的nginx.conf加入映射(前提是通过域名管理工具进行域名的映射(SwitchHosts)),然后启动Nginx测试静态资源是否访问成功。接着就是修改项目中全部引入的头部文件,要把文件映射成二级域名以至于访问,访问的基本原理就是Nginx启动访问项目,项目通过网关配置的断言去找到网关中对应的微服务,然后找到微服务中的头部文件,头部文件里面放的是Nginx的二级域名模式,这样就直接通过二级域名去访问Nginx中配置的html文件夹,而html文件夹中放入的是项目中所有的静态资源,这样就实现的动静分离!!!

  1. 通过SwitchHosts新增二级域名:images.zmall.com
    在这里插入图片描述
  2. 将本次项目的易买网所有静态资源js/css/images复制到nginx中的html目录下
    在这里插入图片描述
  3. 在nginx的核心配置文件nginx.conf中新增二级域名images.zmall.com访问映射,用于实现nginx动静分离

注意:修改成功之后,重启nginx服务使其配置生效!!

server{
    listen 80;
    server_name images.zmall.com;
    location / {
        root html;
        index index.html;
    }
}

在这里插入图片描述

  1. 检测静态资源服务器配置成功
http://images.zmall.com/css/style.css

在这里插入图片描述

  1. 删除zmall-product商品服务和zmall-gateway网关服务以及zmall-user下的static静态资源,改用nginx中配置的静态资源

  2. 修改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/"/>
  1. 分别重启zmall-product、zmall-gateway以及nginx后输入请求地址:zmall.com/product-serv/index.html访问商品服务首页,如下所示:
    在这里插入图片描述
    如果出现IIS7,那么cmd窗口执行下列指令
net stop w3svc

二、服务调用

2.1 创建配置zmall-cart购物车模块

  1. 基于Spring initializr创建zmall-cart购物车模块

在这里插入图片描述

  1. 修改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>

在这里插入图片描述

  1. 配置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
  1. 在启动类上加入@EnableDiscoveryClient

  2. 分别将购物车页面和common/head.html导入到templates目录,并修改head.html中的ctx局部变量

<#assign ctx>
    <#--一级域名,动态请求时需加入该前缀-->
    http://cart.zmall.com
</#assign>
<#--采用H5方式的base标签,在整个页面的url地址前加入,用于访问nginx中的静态资源-->
<base href="http://images.zmall.com/"/>

在这里插入图片描述

  1. 在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,用于处理重定向时依然已原始主机头发送请求。直白点就是处理二级域名的请求

  1. 创建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网关路由配置。

  1. 在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购物车模块

  1. 基于Spring initializr创建zmall-order订单模块

在这里插入图片描述

  1. 将zmall-order订单模块配置到主模块中
    在这里插入图片描述

  2. 修改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>
  1. 配置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
  1. 在启动类上加入@EnableDiscoveryClient@MapperScan({"com.zking.zmall.mapper"})
  2. 代码生成器生成代码

生成表zmall_orderzmall_order_detail

在这里插入图片描述

  1. 把生成出的mapper文件与model放入到公共模块中,把service文件放入到zmall-order模块中

  2. 创建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));
    }
}
  1. 在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服务接口

  1. 定义openfeign接口
@FeignClient("zmall-order")
public interface IOrderFeignService {

    @RequestMapping("/orderUserList")
    List<Order> orderUserList();
}
  1. 在zmall-user启动类上设置@EnableDiscoveryClient@EnableFeignClients

  2. 调用接口并测试接口

@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();
    }
}
  1. 配置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/;
		}
	}
  1. 启动服务测试

测试链接
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 错误案例展示

  1. 在用户服务zmall-user中编写登录控制器,登录时创建session,并将当前登录用户存储sesion中。(这里可以打断点,以确认真正的进入到该方法中)
@Controller
public class UserController {
	
    @RequestMapping("/login.html")
    public String toLogin(HttpSession session){
        session.setAttribute("username","admin");
        return "login";
    }
}
  1. 在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

在这里插入图片描述

  1. 在商品服务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. 测试链路

#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"})

第九步:测试

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值