nginx的负载均衡后session共享(ip_hashredis集群解决)将maven项目向springboot项目改造

Posted 小吕不秃顶也能变强

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了nginx的负载均衡后session共享(ip_hashredis集群解决)将maven项目向springboot项目改造相关的知识,希望对你有一定的参考价值。

nginx的session共享、springboot项目、redis集群

一、准备工作


实现zookeeper集群、dubbo集群、实现dubbo负载均衡!!!
没有搭建好的可以看上篇文章->文章地址

主要ip和端口声明:

  • zookeeper集群配置
    zookeeper配置在linux虚拟机上(因为不想开太多个linux虚拟机,所以配置在 一台linux虚拟机上了,差别只在于修改ip地址)
包名称ip地址端口号
zookeeper-2181172.16.248.2012181
zookeeper-2182172.16.248.2012182
zookeeper-2183172.16.248.2012183

  • dubbo集群配置
    • 消费者集群
      配置在我的windows系统上了
应用名ip地址端口号
dubbodemo-consumer_1localhost2011
dubbodemo-consumer_1localhost2012
dubbodemo-consumer_1localhost2013
dubbodemo-consumer_1localhost2014

  • 服务者集群
    配置在我的windows系统上了
应用名ip地址端口号
dubbodemo_provider_1localhost8081
dubbodemo_provider_1localhost8082

二、引出session共享问题

上篇文章已经实现了nginx实现对消费者端口的分配,以及dubbo对提供者的负载均衡
可以知道当我们访问www.ebuy.com时,nginx服务器会自动分配请求的服务器,再以下四台服务器中轮询选择!!!


  • 那么我们创建一个存储session的业务,来看看会出现什么问题!!!

改造消费者的项目

修改pom.xml文件

  • 首先因为要用到HttpServletRequest的类,所以先引入jar包

  • 然后引入我们的javax.servlet-api
<!--为了session-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>

  • 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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>cn</groupId>
  <artifactId>dubbodemo_customer</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>dubbodemo_customer Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>14</maven.compiler.source>
    <maven.compiler.target>14</maven.compiler.target>
    <spring.version>5.0.5.RELEASE</spring.version>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jms</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <!-- dubbo相关 -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>dubbo</artifactId>
      <version>2.6.0</version>

      <exclusions>
        <exclusion>
          <groupId>javax.servlet</groupId>
          <artifactId>javax.servlet-api</artifactId>
        </exclusion>
      </exclusions>

    </dependency>
    <dependency>
      <groupId>org.apache.zookeeper</groupId>
      <artifactId>zookeeper</artifactId>
      <version>3.4.7</version>
    </dependency>
    <dependency>
      <groupId>com.github.sgroschupf</groupId>
      <artifactId>zkclient</artifactId>
      <version>0.1</version>
    </dependency>
    <dependency>
      <groupId>javassist</groupId>
      <artifactId>javassist</artifactId>
      <version>3.12.1.GA</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.47</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

    <!--为了session-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>

  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <configuration>
          <!-- 指定端口 -->
          <port>2014</port>
          <!-- 请求路径 -->
          <path>/</path>
        </configuration>
      </plugin>
    </plugins>

    <finalName>dubbodemo_provider</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>


修改controller


  • helloController源码:
package cn.ebuy.controller;


import cn.ebuy.service.HelloService;
import com.alibaba.dubbo.config.annotation.Reference;
import org.springframework.http.HttpRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;

@Controller
@RequestMapping("/demo")
public class HelloController {
    @Reference
    private HelloService helloService;
    int port=2014;

    @RequestMapping("/hello")
    @ResponseBody
    public String getName(String name){
        //远程调用
        String result = helloService.sayHello(name);
        System.out.println(port+result);
        return port+result;
    }
    @RequestMapping("/toSession")
    @ResponseBody
    public String toSession(HttpServletRequest request){
        request.getSession().setAttribute("USERS","xiaoming");
        System.out.println("存放USERS进"+port+"端口的session中");
        return "port:"+port+"->session"+":Success";
    }
    @RequestMapping("/doSession")
    @ResponseBody
    public String doSession(HttpServletRequest request){
        String USERS= (String) request.getSession().getAttribute("USERS");
        System.out.println("从端口"+port+"的session中取得USERS"+USERS);
        return "port:"+port+"----USERS:"+USERS;
    }
}

启动服务

首先其中我们的虚拟机的zookeeper集群,然后启动提供者集群,之后启动我们的消费者单个项目(注意是单个,仅2011这一个项目),然后开启我们的nginx服务器,最后进入dubbo-admin查看注册情况!!!

  • 测试一下

  • 那么此时我们开始启动消费者集群2012->2014
    开始测试


那真是奇怪了,为什么就连相同服务器的session都拿不到了!!!
(因为nginx会认为你产生的新的会话—这一点可以具体百度)


session不共享引发的问题

  • 那么session不共享,会引发什么问题呢?

因为nginx内部的负载均衡策略,我们每次请求的服务器可能不同,导致session不共享,那么举个例子,你登录系统后,一刷新再次让你登录!
可以说是web应用的灾难性的问题!!!


三、解决nginx的session共享问题

1、ip_hash方式

在nginx内部,便存在解决session共享的方法,那就是ip_hash算法。
ip_hash算法,简单来说就是你(220.181.38.251)去请求服务时,ip_hash算法会将你的ip取值为hash值,然后给你分配服务器A,当你再次请求服务时,分配给你的还是服务器A,甚至只要是(220.181.38.xxx)的都是服务器A!

测试

那么如何使用这个ip_hash算法呢?
很简单!!!
只需要在


  • 然后重启nginx服务
    再次请求


  • 可以说ip_hash是真的简单粗暴,相当于对于请求一次后的你来说,不再“负载均衡”!!!

2、通过redis集群实现session共享

温馨提示(记得把ip_hash的配置注释掉,然后重启服务)

  • 工作原理

redis集群实现
session共享的原理就是将你的session数据存放进redis集群中,再次请求时,从redis中取得数据,从而实现session共享。

因为涉及到redis集群,为了更快的搭建项目,首先进行改造提供者和消费者的项目为(springboot项目)!!!


改造提供者项目

注意一定要选择jdk1.8,zookeeper对jdk的兼容性极差,高版本都是问题(一小时的教训!!!)

创建项目

注意主启动类不在cn.ebuy目录下,注意扫描包
(@SpringBootApplication(scanBasePackages = ))


注意接下来不要选任何jar包,因为后面我们会一起导入!!!


导入jar包

注意java版本必须是1.8
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>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.ebuy</groupId>
    <artifactId>springboot_dubbo_provider</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot_dubbo_provider</name>
    <description>springboot_dubbo_provider</description>
    <properties>
        <java.version>11</java.version>

    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!--web需要的jar包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--dubbo需要的jar-->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.7.6</version>
        </dependency>
        <!--zookeeper需要的jar-->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-registry-zookeeper</artifactId>
            <version>2.7.6</version>
            <exclusions>
                <!--排除掉zookeeper内置的这个jar-->
                <exclusion>
                    <groupId>org.apache.curator</groupId>
                    <artifactId>curator-recipes</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>4.2.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>


创建service

  • 这个注解原本是alibaba包下的,换为apache包下的(过时问题)

配置主配置文件

application.yaml(原本是application.properties,修改为yaml文件)

注意超时时间必须配置(有可能会因为超时时间连不上,报错)


拿来之前的普通的spring项目对比下


启动项目


改造消费者项目

创建项目

  • 同上面的提供者项目一样,不要选择任何jar

导入jar包

注意这个jar不同于上面的提供者!!!
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>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.ebuy</groupId>
    <artifactId>springboot_dubbo_consumer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot_dubbo_consumer</name>
    <description>springboot_dubbo_consumer</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>


        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.7.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>


        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-registry-zookeeper</artifactId>
            <version>2.7.6</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.curator</groupId>
                    <artifactId>curator-recipes</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>4.2.0</version>
        </dependency>



        <!-- 实现对 Spring Session 使用 Redis 作为数据源的自动化配置 -->
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>

        <!-- 实现对 Spring Data Redis 的自动化配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <exclusions>
                <!-- 去掉对 Lettuce 的依赖,因为 Spring Boot 优先使用 Lettuce 作为 Redis 客户端 -->
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- 引入 Jedis 的依赖,这样 Spring Boot 实现对 Jedis 的自动化配置 -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>






        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>


创建service


创建controller


源码:

package cn.ebuy.controller;


import cn.ebuy.service.HelloService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;

@Controller
@RequestMapping("/demo")
public class HelloController {
    @Reference
    private HelloService helloService;
    int port=2014;

    @RequestMapping("/hello")
    @ResponseBody
    public String getName(String name){
        //远程调用
        String result = helloService.sayHello(name);
        System.out.println(port+result);
        return port+result;
    }
    @RequestMapping("/toSession")
    @ResponseBody
    public String toSession(HttpServletRequest request){
        request.getSession().setAttribute("USERS","xiaoming");
        System.out.println("存放USERS进"+port+"端口的session中");
        return "port:"+port+"->session"+":Success";
    }
    @RequestMapping("/doSession")
    @ResponseBody
    public String doSession(HttpServletRequest request){
        String USERS= (String) request.getSession().getAttribute("USERS");
        System.out.println("从端口"+port+"的session中取得USERS"+USERS);
        return "port:"+port+"----USERS:"+USERS;
    }
}


创建util包(关键)


搭建redis集群

这里不再详细讲解,可以看这个文章,或者百度均可!!!

我的redis集群的密码都是123456,然后端口号是6379、6380、6381、6382、6383和6384。

  • 搭建完成之后,进行启动!!!

配置主配置文件


测试启动

因为要测试多个消费者,所以要一个项目跑多个端口服务,那么如何做到呢?(小技巧)


然后分别运行一次修改一次application.yaml中的server:port(2011->2014
对应你的nginx中配置的内容!!!)和controller中的port变量(这个只是为了便于区分,看出效果!!),

  • 分别启动后看到

  • 最后测试我们的session共享吗

  • 然后我们打开redis可视化工具看下内容

这样就算是完成了!!!


感谢观看!!!

以上是关于nginx的负载均衡后session共享(ip_hashredis集群解决)将maven项目向springboot项目改造的主要内容,如果未能解决你的问题,请参考以下文章

Nginx+Tomca+Redis实现负载均衡资源分离session共享

nginx的负载均衡后session共享(ip_hashredis集群解决)将maven项目向springboot项目改造

nginx负载均衡session共享

nginx负载均衡集群中的session共享说明

解决nginx负载均衡的session共享问题

解决nginx负载均衡的session共享问题