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-2181 | 172.16.248.201 | 2181 |
zookeeper-2182 | 172.16.248.201 | 2182 |
zookeeper-2183 | 172.16.248.201 | 2183 |
- dubbo集群配置
- 消费者集群
配置在我的windows系统上了
- 消费者集群
应用名 | ip地址 | 端口号 |
---|---|---|
dubbodemo-consumer_1 | localhost | 2011 |
dubbodemo-consumer_1 | localhost | 2012 |
dubbodemo-consumer_1 | localhost | 2013 |
dubbodemo-consumer_1 | localhost | 2014 |
- 服务者集群
配置在我的windows系统上了
应用名 | ip地址 | 端口号 |
---|---|---|
dubbodemo_provider_1 | localhost | 8081 |
dubbodemo_provider_1 | localhost | 8082 |
二、引出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项目改造