电商秒杀之分布式扩展

本文介绍了电商秒杀系统如何通过Nginx实现分布式扩展,包括前端部署、Nginx OpenResty的安装、前端资源路由配置、Nginx反向代理设置,以及分布式会话管理的两种实现方式:基于cookie的sessionid传输和基于token的sessionid传输。通过这些步骤,系统实现了负载均衡和性能提升。
摘要由CSDN通过智能技术生成

同样操作 云端部署的方式: https://www.cnblogs.com/yugehard/p/14171007.html

现在我们的部署方案将我们tomcat一个秒杀的java web的服务器及对应的数据库部署在了同一台机器上,单机有对应的容量问题,需要系统有一个水平扩展的能力,这个能力依托于nginx的反向代理做一个收口的操作。

如何对于同一个域名可以代理到多台不同的application server的应用服务器上——nginx的反向代理 它可以代理我们后端的一个tomcat的服务器集群,以一个同一域名的方式暴露出去供用户调用

 

一.修改前端用于部署nginx

 因为之前在JMter测压之前进行了云端部署,但是我这里没有做云端部署,但是还是记录一下操作的过程

建立gethost.js

var g_host = "localhost:8090";

在前端文件中引入js

<script src="./gethost.js" type="text/javascript"></script>

在每个url处进行使用

url:"http://"+g_host+"/

二.部署Nginx OpenResty

只需要将一个对应的OpenResty打包下载下来,进行编译之后就可以使用nginx的互联网相关的一些基本内容

OpenResty是基于nginx上面另外开发包装对应的一个可以支持lua的一个应用

视频中是在linux上进行操作

这里百度了一个Openresty在windows安装和使用  不知道影不影响后续使用 现在先这样处理

Openresty在windows安装和使用_openresty windows_蓝色雨88的博客-CSDN博客

下载完成后 点击nginx.exe运行

将秒杀系统中html文件复制进入D:\openresty-1.21.4.1-win64\openresty-1.21.4.1-win64\html下

 进入网站

 成功

然后全部运行一遍 运行成功

三.前端资源路由

第一步:修改C:\Windows\System32\drivers\etc 中hosts文件

  

出现了问题 修改后不能使用miaoshaserver直接访问

解决①(1条消息) windows修改hosts文件不生效(非DNS缓存问题)_TianXinCoord的博客-CSDN博客

(1条消息) 修改hosts 不生效? 三种方法解决-CSDN博客

最后是修改后重新启动电脑成功解决

  第二步:修改修改nginx.conf

输入之后 在进行nginx重新配置时出错

在任务管理器把进程结束了 想着重新启动

结果启动搞不起来 错误如下

 解决(1条消息) nginx.conf配置文件修改的注意事项/规则_nginx conf文件被修改_哆啦Ci梦的博客-CSDN博客

 末尾少了一个 /

----------------------------------

此处我感觉用windows做这一步意义不大

就是在html文件下创建了一个 resources文件 把html中的文件全部放入resources中

四.配置nginx反向代理

第一步:进入nginx.conf进行修改

    #gzip  on;
    #
    #后端服务器集群
    upstream backend_server{
     server 本地网Ip  weight=1;

     }

这里的网址 我添加的是电脑本地的网址 不知道后续有没有影响 先这样放在这

   location /resources/ {
        alias   /usr/local/openresty/nginx/html/resources/;
        index  index.html index.htm;
        }
        location / {
                 proxy_pass http://backend_server;
                 proxy_set_header Host  $http_host:$proxy_port;
                 proxy_set_header X-Real-IP  $remote_addr;
                 proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
         }

①proxy_pass http://backend_server; 当路径访问除开resouces节点之外,就反向代理到http://backend_server    backend_server    对应的ip地址是后端服务器集群中的地址

②proxy_set_header Host  $http_host:$proxy_port; 后面$后是端口号

③proxy_set_header X-Real-IP  $remote_addr;真正的客户地址其实是一个远端的客户地址

④ proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;Nginx作为一个代理服务器转发了这个请求

⚠经试验后,未成功

参考这个博主duiwindows下配置nginx访问本地静态资源,完整配置,使用root和alias的区别,小心掉坑里_windows nginx alias_一点光辉的博客-CSDN博客

nginx配置访问本地静态资源 - long77 - 博客园 (cnblogs.com)

 对nginx.conf重新进行修改

完整代码如下:


worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;


    upstream backend_server{
     server localhost:8090;
    }

    server {
        listen       80;
        server_name  localhost;

        location /resources/ {
        alias   D:/openresty-1.21.4.1-win64/openresty-1.21.4.1-win64/html/;
        index  index.html index.htm;
        }

        location / {
               proxy_pass http://backend_server;
                proxy_set_header Host  $http_host:$proxy_port;
               proxy_set_header X-Real-IP  $remote_addr;
                proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
        }


        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

运行成功!!

 第二步:开启tomcat access log验证

这里又涉及到是在windows上而不是在linux上

首先 设置application.properties

server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.directory=D:/miaosha/tomcat
server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D

解释:

%h - 远程主机名称(如果resolveHosts为false则展示IP)
%l - 远程用户名,始终为'-'(Remote logical username from identd)
%r - 请求的第一行(包括请求方法和请求的URI)
%s - response的HTTP状态码(200,404等)
%t - 日期和时间,Common Log Format格式
%u - 被认证的远程用户, 不存在则展示'-'
%D - 处理请求的时间,单位为毫秒
%T - 处理请求的时间,单位为秒
%I - 当前请求的线程名(can compare later with stacktraces)


server.tomcat.accesslog.directory 表示日志存放的位置

然后对nginx.conf进行修改 设置日志输入位置 这里出了很多错误 其中还把logs中的nginx.pid复制进了miaosha\tomcat中

error_log   D:/miaosha/tomcat;
error_log   D:/miaosha/tomcat notice;
error_log   D:/miaosha/tomcat info;
pid         D:/miaosha/tomcat/nginx.pid;

access_log  D:/miaosha/tomcat main;

运行

 运行后查看日志输出

 返回码 200,发送了331个字节,处理时间791ms

gethost.js进行修改

var g_host = "miaoshaserver";

五.分布式扩展后的性能压测

还在是localhost上测压 

换为miaoshaserver

 换为127.0.0.1

 修改Nginx.conf

    upstream backend_server{
     server localhost:8090;
     keepalive  30;
    }
        location / {
               proxy_pass http://backend_server;
                proxy_set_header Host  $http_host:$proxy_port;
               proxy_set_header X-Real-IP  $remote_addr;
                proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
              proxy_http_version  1.1;
             proxy_set_header Connection  "";
        }

运行 127.0.0.1

 不知道为什么 每次重新点运行 数据就会一直发生变化

六.Nginx高性能原因

参考第三章 分布式扩展(二)_猿小羽的博客-CSDN博客

七.分布式会话管理

1.基于cookie传输sessionid:java tomcat容器session实现

目前我们项目使用的是 SpringBoot内嵌tomcat容器里的 HTTPsession 机制,是基于cookie传输sessionid的一个机制,用来标识一个用户会话项目中用到的地方是登陆/注册功能

第一步:在pom.xml添加依赖

  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.session</groupId>
      <artifactId>spring-session-data-redis</artifactId>
      <version>2.0.5.RELEASE</version>
    </dependency>

第二步:创建RedisConfig


@Component
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 3600)
public class RedisConfig {

}

第三步:在windows上安装redis

Window下Redis的安装和部署详细图文教程(Redis的安装和可视化工具的使用)_windows 安装redis_明金同学的博客-CSDN博客

测试

 第四步:将redis操作配置上去

修改application.properties


#配置springboot对redis的依赖
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.database=10
#spring.redis.password=

#设置jedis连接池
spring.redis.jedis.pool.max-active=50
spring.redis.jedis.pool.min-idle=20

启动app

Caused by: org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [com.miaoshaproject.service.model.UserModel]

报错没有UserModel的一个序列化接口 要存入redis的数据,都得实现 Serializable 序列化

修改UserModel.java 

public class UserModel implements Serializable {
}

重新启动后 查看keys *

 第五步:验证这套方案是否适应于分布式

在redis.conf中 如果默认不加任何ip地址的话 默认绑在0.0.0.0 也就是说所有的只要能够连接到这台服务器的 不管是内网ip还是外网ip都可以 现在修改成对应的内网ip

 不知道这样可行不可行 先放在这

然后重新运行程序 进行操作 具体操作就是登录之后在下单页面点击几次下单

 2.基于token传输sessionid:java代码session的实现

token类似于服务端下发sessionid植入到cookie,下发令牌,交给前端,等登陆请求时,把token加入到http header或url中。将java代码将token传到redis中

第一步:修改UserController.java

 @Autowired
    private RedisTemplate redisTemplate;
    //用户登录接口
    @RequestMapping(value = "/login", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})
    @ResponseBody
    public  CommonReturnType login(@RequestParam(name="telphone")String telphone,
                                   @RequestParam(name="password")String password) throws BusinessException, UnsupportedEncodingException, NoSuchAlgorithmException {
        //入参校验
        if(org.apache.commons.lang3.StringUtils.isEmpty(telphone)||
                StringUtils.isEmpty(password)){
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);
        }
        //用户登录服务,用来校验用户登录是否合法
        //用户加密后的密码

        UserModel userModel = userService.validateLogin(telphone, this.EncodeByMd5(password));
        
        //将登陆凭证加入到用户登陆成功的session内

        //修改成若用户登录验证成功后将对应的登录信息和登录凭证一起存入redis中

        //生成登录凭证token,UUID
        String uuidToken = UUID.randomUUID().toString();
        // 例如:5dcac225-b98b-4618-b09d-533440b936e7 将token的"-"给去掉
        uuidToken = uuidToken.replace("-","");
        //建议token和用户登陆态之间的联系
        redisTemplate.opsForValue().set(uuidToken,userModel);
        // 设置uuidToken过期时间为1小时
        redisTemplate.expire(uuidToken,1, TimeUnit.HOURS);

        /*//将登陆凭证加入到用户登录成功的session内
        this.httpServletRequest.getSession().setAttribute("IS_LOGIN", true);
        this.httpServletRequest.getSession().setAttribute("LOGIN_USER", userModel);*/

        //下发了token
        return CommonReturnType.create(uuidToken);

    }

第二步:修改login.html

  $.ajax({
                type:"POST",
                contentType:"application/x-www-form-urlencoded",
                url:"http://"+g_host+"/user/login",
                data:{
                    "telphone":telphone,
                    "password":password
                },
                //允许跨域请求
                xhrFields:{withCredentials:true},
                success:function (data) {
                    if (data.status=="success") {
                        alert("登录成功");
                        //将token返回给前端 并储存起来
                        var token=data.data;
                        window.localStorage["token"] = token;
                        window.location.href = "listitem.html";
                    }else {
                        alert("登录失败,原因为" + data.data.errMsg);
                    }
                },
                error:function (data) {
                    alert("登录失败,原因为"+data.responseText);
                }
            });
            return false;
        });
    });

第三步:修改getitem.html

$("#createorder").on("click",function () {
            //将储存在客户端的token取出
            var token = window.localStorage["token"];
            if(token == null){
                alert("没有登录,不能下单");
                window.location.href="login.html";
                return false;
            }
            $.ajax({
                type:"POST",
                contentType:"application/x-www-form-urlencoded",
                //如果获取到token就将token返回到后端
                url:"http://"+g_host+"/order/createorder?token="+token,

第四步:修改OrderController.java

@Autowired
    private RedisTemplate redisTemplate;
    //封装下单请求
    @RequestMapping(value = "/createorder", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})
    @ResponseBody
    public CommonReturnType createOrder(@RequestParam(name = "itemId") Integer itemId,
                                        @RequestParam(name = "amount") Integer amount,
                                        @RequestParam(name="promoId",required=false)Integer promoId) throws BusinessException {

        //获取用户登录信息
       // Boolean isLogin = (Boolean) httpServletRequest.getSession().getAttribute("IS_LOGIN");
       /* if (isLogin == null || !isLogin.booleanValue()) {
            throw new BusinessException(EmBusinessError.USER_NOT_LOGIN, "用户还未登录,不能下单");
        }
         UserModel userModel = (UserModel) httpServletRequest.getSession().getAttribute("LOGIN_USER");*/
       //获取token请求
        String token = httpServletRequest.getParameterMap().get("token")[0];
        if (StringUtils.isEmpty(token)) {
            throw new BusinessException(EmBusinessError.USER_NOT_LOGIN, "用户还未登录,不能下单");
        }
        // 获取用户的登录信息
        UserModel userModel = (UserModel) redisTemplate.opsForValue().get(token);

        if (userModel == null) {
            throw new BusinessException(EmBusinessError.USER_NOT_LOGIN, "用户还未登录,不能下单");
        }

        OrderModel orderModel = orderService.createOrder(userModel.getId(), promoId,itemId, amount);

        return CommonReturnType.create(null);
    }

}

第六步:运行进入 下单

查看控制台

 这里不知道为什么用火狐就不行 用了微软

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值