Fork me on GitHub
每天进步一小点

一切就是这么简单


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

Spring IOC级联容器原理

发表于 2019-05-12 | 分类于 spring

spring mvc框架中的两级容器

  • spring mvc在开发中经常使用,一般我们在web.xml配置时候,都需要配置ContextLoaderListener和一个DispatcherServlet,那么这2个都起到什么所用呢?
  • 其实就是配置了2个spring ioc容器,并且DispatcherServlet创建IOC容器的父容器就是ContextLoaderListener创建的IOC容器
  • 一般配置如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    配置 ContextLoaderListener 

    <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>WEB-INF/applicationContext.xml</param-value>
    </context-param>

    配置 DispatcherServlet

    <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
    </servlet>
  • ContextLoaderListener 会创建一个由应用程序上下文 XMLWebApplicationContext 来管理的 IOC 容器,IOC 里面的 bean 是通过 配置的 contextConfigLocation 参数对应的 WEB-INF/applicationContext.xml 来注入的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
    <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:springmvc-servlet.xml</param-value>
    </init-param>
    </servlet>
  • 以上配置完成后就会形成两级级联IOC容器了

    • spring sub context ———> spring root context

总结

  • 1 子容器无法访问子容器的bean,因为相互隔离
  • 2 子容器可以访问父容器的bean,因为子容器也是通过父容器创建的
  • 3 父容器无法访问子容器中的bean,不支持自顶向下访问

Mac版本破解starUML

发表于 2019-05-03 | 更新于 2019-05-16 | 分类于 developTools

下载并安装

  • 1 从官网http://staruml.io/下载 StarUML,因为是dmg,可以直接安装
  • 2 安装完成后,需要先安装asar
    1
    npm install asar -g

破解

  • 1 安装成功默认装在/Applications/StarUML.app/Contents/Resources,进入该目录
  • 2 解压文件 asar extract app.asar app

    1
    asar extract app.asar app
  • 3 修改js文件app/src/engine/license-manager.js

    1
    vim app/src/engine/license-manager.js ,可通过find命令查找,输入/checkLicenseValidity
  • 4 找到checkLicenseValidity ()方法,修改成如下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    checkLicenseValidity () {
    this.validate().then(() => {
    setStatus(this, true)
    }, () => {
    setStatus(this, true)//false 改为true
    // UnregisteredDialog.showDialog()
    })
    }
  • 5 重新打包app文件夹asar pack app app.asar

    1
    asar pack app app.asar
  • 破解成功,重启StarUML即可开启画图的乐趣

starUML入门使用

发表于 2019-05-03 | 更新于 2019-05-17 | 分类于 developTools

使用

Leader选举原理

发表于 2019-04-03 | 更新于 2019-05-11 | 分类于 zookeeper

前言

  • Leader崩溃或者Leader失去大多数的Follower,这时候zk进入恢复模式,恢复模式需要重新选举出一个新的Leader,让所有的Server都恢复到一个正确的状态。
    Zookeeper中Leader的选举采用了三种算法:
    • LeaderElection
    • FastLeaderElection
    • AuthFastLeaderElection
  • 并且在配置文件中是可配置的,对应的配置项为electionAlg

  • 成为Leader的必要条件: Leader要具有最高的zxid;当集群的规模是n时,集群中大多数的机器(至少n/2+1)得到响应并follow选出的Leader

  • 心跳机制:Leader与Follower利用PING来感知对方的是否存活,当Leader无法相应PING时,将重新发起Leader选举

  • zxid:zookeeper transaction id, 每个改变Zookeeper状态的操作都会形成一个对应的zxid,并记录到transaction log中。 这个值越大,表示更新越新

  • electionEpoch/logicalclock:逻辑时钟,用来判断是否为同一次选举。每调用一次选举函数,logicalclock自增1,并且在选举过程中如果遇到election比当前logicalclock大的值,就更新本地logicalclock的值

  • peerEpoch: 表示节点的Epoch

    zookeeper server状态说明

  • LOOKING:寻找Leader
  • LEADING:Leader状态,对应的节点为Leader。
  • FOLLOWING:Follower状态,对应的节点为Follower。
  • OBSERVING:Observer状态,对应节点为Observer,该节点不参与Leader选举

LeaderElection选举算法

  • LeaderElection是Fast Paxos最简单的一种实现,每个Server启动以后都询问其它的Server它要投票给谁,收到所有Server回复以后,就计算出zxid最大的哪个Server,并将这个Server相关信息设置成下一次要投票的Server。
    该算法于Zookeeper 3.4以后的版本废弃

  • 选举流程:

    • 1 选举线程首先向所有的server发起一次询问(包括自己)
    • 2 选举线程收到回复,验证是否是自己发起询问(验证zxid是否一致),然后获取对方的server Id(MyId),并存储到当前询问对象列表中,最后获取对方提议的leader相关信息(id,zxid),并将这些信息存储到当次选举的投票记录表中
    • 3 收到所有的server回复后,就计算出zxid最大的那个server,并将这个server的相关信息设置成下一次要投票的server
    • 4 选举线程将当前zxid最大的server设置为当前server要推举的leader,如果此时(zxid最大的server)获取多数server投票,就设置当前推荐的leader为获胜的server
      将根据获胜的server相关信息设置自己的状态,否则继续这个过程,直到leader被选举出来
  • 流程图展示

    LeaderElectoin

  • 通过流程分析我们可以得出:要使Leader获得多数Server的支持,则Server总数必须是奇数2n+1,且存活的Server的数目不得少于n+1

  • Leader选举异常情况

    • 1 选举过程中,Server的退出,只要保证n/2+1个Server存活就没有任何问题,如果少于n/2+1个Server 存活就没办法选出Leader

面试问题总结

发表于 2019-04-03 | 更新于 2019-05-10 | 分类于 interview

前言

  • 相信大家在面试过程中,都会经历面试官一些问题答复,或多或少会有一些答不出来或者答的不好。其实面试过程就好比一次技术的交流,或者说是一次自我的技术检查,其实面试管对面试者的技术面试是看我们在工作中解决问题的思路与能,所以面试者需要认真对待每一次面试,我们都需要在面试中成长。所以我们需要做到如下几点:
    • 面试过程中遇到不会的问题,需要铭刻记住,以便回来之后对技术的补充和复习
    • 尽量对不会的问题做到表里如一,由浅到深
    • 多动手实战,学会画流程图或者思维导图来加深自己对技能的掌握
    • 勤做技术笔记,形成一种良好的习惯,阅读官方文档或者源码分析

面试问题

  • 以下是笔者在面试中遇到的一些问题,下面将以分类形式具体说明

    redis

  • 我有个业务场景,需要往redis list 数据结构插入上百万数据量或者更多,如何提高插入速度和减轻网络磁盘IO消耗?

    • 笔者当时并未使用过 redis list,所以当时只想到了,可使用缓存批量插入
    • 经过查看redis提供以下两种方式:
      • 利用redis pipline 管道技术
      • 把需要插入数据分块批量插入
  • 实战

    • 举个例子需要添加1000万数据用于图标展示分析,现在需要把1000万的数字插入redis数据库

      • 普通的插入redis set集合方法

        1
        2
        3
        4
        5
        r = redis.Redis(host="127.0.0.1", port=6379)
        for i in range(1, 3 * 10 ** 8):
        r.sadd('xxxxx', i)

        数据量少还行,一次插入这么多数据就会出现问题
      • 利用redis pipline 管道技术

        1
        2
        3
        4
        5
        6
        7
        r = redis.Redis(host="127.0.0.1", port=6379)
        pipeline = r.pipeline()
        for i in range(1, 3 * 10 ** 8):
        pipeline.sadd('xxxxx', i)
        pipeline.execute()

        这种操作相当于写了一堆的命令一次性执行完,一旦某个命令出问题那么这次插入数据就会失败。这种方式的好处。节省了本机与redis服务器链接的 IO 延时,一般来说节省了很多时间
      • 把需要插入的数据分块批量插入

        1
        2
        3
        4
        5
        for i in range(30):
        ls = list(range(i*1000000,(i+1)*1000000))
        r.sadd('xxxxx', *ls)

        这样的方式是一次插入多个数据,不会出现使用pipline如果某个命令出问题就全部插入失败的现象,速度非常快,比使用pipline 的方式快了几倍

MQ消息队列

  • mq消息队列如何实现幂等性的?

    • 在消费者业务处理或者业务设计中,如果消息已经被消费,可更改实体状态(根据msgId区分),如果更新过,则不进行重复处理业务
  • mq如何保证有序性?

    • 场景分析:比如异步执行业务,发送2条消息,且处理业务是,先增加,修改,删除。但在并发情况下(消费者不止一个)那么如何保证业务的执行顺序呢?
    • 解决方案:
      • 如果是同步肯定不存在,异步且高并发,可针对业务执行顺序,分别使用不同的消息队列。在业务逻辑处理时,可对顺序进行控制
      • 可在消息体中加入版本号 & 状态机 & msgid & parent_msgid,通过 parent_msgid 判断消息的顺序(需要全局存储,记录消息的执行状态)
    • 缺点:因为要求业务顺序,我们在业务处理中,需要额外的io或者内存资源来对顺序业务的处理,导致一部分性能降低,这也是MQ不提高顺序性的理由之一
    • 总结:有没有一种可能,我们在设计业务的时候是否可以避免有序执行业务,从而提高程序执行性能

zookeeper

  • zookeeper Leader选举原理?

spring mvc

  • 过滤器与拦截器的区别

    • 过滤器:

    • 过滤器是javaEE标准,采用函数回调的方式进行。请求进入容器之后,还未进入servlet之前进行预处理,并且在请求结束返回给前端这之间进行处理

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      //实现过滤功能
      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
      System.out.println("before...");
      chain.doFilter(request, response);
      System.out.println("after...");
      }

      //用于完成Filter的初始化
      void init(FilterConfig config):

      //用于Filter销毁前,完成某些资源的回收
      void destory():
    • 工作原理,在web.xml配置拦截客户请求,此时你可以对请求或者响应做统一设置编码,一些逻辑判断,如用户是否已登录,是否有权限访问页面等,Filter是随web容器启动的,只会初始化一次。

    • 拦截器:

    • spring mvc 拦截器是基于java反射机制实现,会动态拦截指定的service方法,可对请求的共性抽取出来独立,可以共享一个行为,一旦行为发生改变,不必修改很多类,只需要更改这个行为就可以
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      public interface HandlerInterceptor {
      //该方案将在请求处理前进行调用,spring mvc的interceptor是链式调用的,可以同时存在多个Interceptor,每个Interceptor调用会依据它的声明顺序依次执行
      boolean preHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;

      //当preHandle返回true此方案才会被调用,该方法就是在当前请求进行处理之后,也就是controller方法调用之后执行,会在视图返回渲染之前调用,也就是说这个方法对controller处理之后的ModelAndView对象进行操作
      void postHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3, ModelAndView var4) throws Exception;

      //该方法也是需要当前对应的preHandle方法返回true才执行,此方法将在整个请求结束之后,也就是在DispatcherServlet渲染对应的视图之后执行。该方法主要作用是用于进行资源清理工作
      void afterCompletion(HttpServletRequest var1, HttpServletResponse var2, Object var3, Exception var4) throws Exception;
      }
  • 总结
    • Filter只在Servlet前后起作用且无法使用spring容器资源,而拦截器可以深入到方法前后,异常抛出前后等
    • 拦截器是一个Spring的组件,归Spring管理,配置在Spring文件中,因此能使用Spring里的任何资源、对象,例如 Service对象、数据源、事务管理等,通过IoC注入到拦截器即可

mysql

  • 索引的使用

english入门基础

发表于 2019-04-03 | 更新于 2019-05-15 | 分类于 english

前言

  • Leader崩溃或者Leader失去大多数的Follower,这时候zk进入恢复模式,恢复模式需要重新选举出一个新的Leader,让所有的Server都恢复到一个正确的状态。
    Zookeeper中Leader的选举采用了三种算法:
    • LeaderElection
    • FastLeaderElection
    • AuthFastLeaderElection
  • 并且在配置文件中是可配置的,对应的配置项为electionAlg

  • 成为Leader的必要条件: Leader要具有最高的zxid;当集群的规模是n时,集群中大多数的机器(至少n/2+1)得到响应并follow选出的Leader

  • 心跳机制:Leader与Follower利用PING来感知对方的是否存活,当Leader无法相应PING时,将重新发起Leader选举

  • zxid:zookeeper transaction id, 每个改变Zookeeper状态的操作都会形成一个对应的zxid,并记录到transaction log中。 这个值越大,表示更新越新

  • electionEpoch/logicalclock:逻辑时钟,用来判断是否为同一次选举。每调用一次选举函数,logicalclock自增1,并且在选举过程中如果遇到election比当前logicalclock大的值,就更新本地logicalclock的值

  • peerEpoch: 表示节点的Epoch

    zookeeper server状态说明

  • LOOKING:寻找Leader
  • LEADING:Leader状态,对应的节点为Leader。
  • FOLLOWING:Follower状态,对应的节点为Follower。
  • OBSERVING:Observer状态,对应节点为Observer,该节点不参与Leader选举

LeaderElection选举算法

  • LeaderElection是Fast Paxos最简单的一种实现,每个Server启动以后都询问其它的Server它要投票给谁,收到所有Server回复以后,就计算出zxid最大的哪个Server,并将这个Server相关信息设置成下一次要投票的Server。
    该算法于Zookeeper 3.4以后的版本废弃

  • 选举流程:

    • 1 选举线程首先向所有的server发起一次询问(包括自己)
    • 2 选举线程收到回复,验证是否是自己发起询问(验证zxid是否一致),然后获取对方的server Id(MyId),并存储到当前询问对象列表中,最后获取对方提议的leader相关信息(id,zxid),并将这些信息存储到当次选举的投票记录表中
    • 3 收到所有的server回复后,就计算出zxid最大的那个server,并将这个server的相关信息设置成下一次要投票的server
    • 4 选举线程将当前zxid最大的server设置为当前server要推举的leader,如果此时(zxid最大的server)获取多数server投票,就设置当前推荐的leader为获胜的server
      将根据获胜的server相关信息设置自己的状态,否则继续这个过程,直到leader被选举出来
  • 流程图展示

    LeaderElectoin

  • 通过流程分析我们可以得出:要使Leader获得多数Server的支持,则Server总数必须是奇数2n+1,且存活的Server的数目不得少于n+1

  • Leader选举异常情况

    • 1 选举过程中,Server的退出,只要保证n/2+1个Server存活就没有任何问题,如果少于n/2+1个Server 存活就没办法选出Leader

ThreadLocal原理分析

发表于 2019-02-02 | 更新于 2019-05-11 | 分类于 java

什么是ThreadLocal

  • ThreadLocal提供了线程的局部变量,每个线程都可以通过set,get来对这个局部变量进行操作且不会影响其他线程的局部变量。防止冲突,实现线程的数据隔离,以到达线程的安全

ThreadLocal的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class ThreadLocalTest {

//(1)打印函数
static void print(String str){
//1.1 打印当前线程本地内存中localVariable变量的值
System.out.println(str + ":" +localVariable.get());
//1.2 清除当前线程本地内存中localVariable变量
//localVariable.remove();
}
//(2) 创建ThreadLocal变量
static ThreadLocal<String> localVariable = new ThreadLocal<>();
public static void main(String[] args) {

//(3) 创建线程one
Thread threadOne = new Thread(new Runnable() {
public void run() {
//3.1 设置线程one中本地变量localVariable的值
localVariable.set("threadOne local variable");
//3.2 调用打印函数
print("threadOne");
//3.3打印本地变量值
System.out.println("threadOne remove after" + ":" +localVariable.get());

}
});
//(4) 创建线程two
Thread threadTwo = new Thread(new Runnable() {
public void run() {
//4.1 设置线程one中本地变量localVariable的值
localVariable.set("threadTwo local variable");
//4.2 调用打印函数
print("threadTwo");
//4.3打印本地变量值
System.out.println("threadTwo remove after" + ":" +localVariable.get());

}
});
//(5)启动线程
threadOne.start();
threadTwo.start();
}
  • 运行结果:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    threadOne:threadOne local variable
    threadTwo:threadTwo local variable
    threadOne remove after:threadOne local variable
    threadTwo remove after:threadTwo local variable

    解开代码 1.2 的注释后,再次运行,运行结果为:

    threadOne:threadOne local variable
    threadOne remove after:null
    threadTwo:threadTwo local variable
    threadTwo remove after:null

ThreadLocal实现原理

ThreadLocal

  • 如上类图可知 Thread 类中有一个 threadLocals 和 inheritableThreadLocals 都是 ThreadLocalMap 类型的变量,而 ThreadLocalMap 是一个定制化的 Hashmap,默认每个线程中这个两个变量都为 null,只有当前线程第一次调用了 ThreadLocal 的 set 或者 get 方法时候才会进行创建
  • 其实每个线程的本地变量不是存放到 ThreadLocal 实例里面的,而是存放到调用线程的 threadLocals 变量里面。也就是说 ThreadLocal 类型的本地变量是存放到具体的线程内存空间的
  • ThreadLocal 就是一个工具壳,它通过 set 方法把 value 值放入调用线程的 threadLocals 里面存放起来,当调用线程调用它的 get 方法时候再从当前线程的 threadLocals变 量里面拿出来使用
  • 如果调用线程一直不终止,那么这个本地变量会一直存放到调用线程的 threadLocals 变量里面,所以当不需要使用本地变量时候可以通过调用 ThreadLocal 变量的 remove 方法,从当前线程的 threadLocals 里面删除该本地变量。另外 Thread 里面的 threadLocals 为何设计为 map 结构呢?很明显是因为每个线程里面可以关联多个 ThreadLocal 变量
  • 下面简单分析下 ThreadLocal 的 set,get,remove 方法的实现逻辑:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    * void set(T value)
    public void set(T value) {
    //(1)获取当前线程
    Thread t = Thread.currentThread();
    //(2)当前线程作为key,去查找对应的线程变量,找到则设置
    ThreadLocalMap map = getMap(t);
    if (map != null)
    map.set(this, value);
    else
    //(3)第一次调用则创建当前线程对应的HashMap
    createMap(t, value);
    }
  • 如上代码(1)首先获取调用线程,然后使用当前线程作为参数调用了 getMap(t) 方法,getMap(Thread t) 代码如下:

    1
    2
    3
    ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
    }
  • 可知 getMap(t) 所做的就是获取线程自己的变量 threadLocals,threadlocal 变量是绑定到了线程的成员变量里面

  • 如果 getMap(t) 返回不为空,则把 value 值设置进入到 threadLocals,也就是把当前变量值放入了当前线程的内存变量 threadLocals,threadLocals 是个 HashMap 结构,其中 key 就是当前 ThreadLocal 的实例对象引用,value 是通过 set 方法传递的值
  • 如果 getMap(t) 返回空那说明是第一次调用 set 方法,则创建当前线程的 threadLocals 变量

    1
    2
    3
    void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
  • T get()方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public T get() {
    //(4) 获取当前线程
    Thread t = Thread.currentThread();
    //(5)获取当前线程的threadLocals变量
    ThreadLocalMap map = getMap(t);
    //(6)如果threadLocals不为null,则返回对应本地变量值
    if (map != null) {
    ThreadLocalMap.Entry e = map.getEntry(this);
    if (e != null) {
    @SuppressWarnings("unchecked")
    T result = (T)e.value;
    return result;
    }
    }
    //(7)threadLocals为空则初始化当前线程的threadLocals成员变量
    return setInitialValue();
    }
  • 首先获取当前线程实例,如果当前线程的 threadLocals 变量不为 null 则直接返回当前线程绑定的本地变量。否者执行代码(7)进行初始化,setInitialValue() 的代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    private T setInitialValue() {
    //(8)初始化为null
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    //(9)如果当前线程的threadLocals变量不为空
    if (map != null)
    map.set(this, value);
    else
    //(10)如果当前线程的threadLocals变量为空
    createMap(t, value);
    return value;
    }
    protected T initialValue() {
    return null;
    }
  • 如上代码如果当前线程的 threadLocals 变量不为空,则设置当前线程的本地变量值为 null,否者调用 createMap 创建当前线程的 createMap 变量

  • T remove()方法

    1
    2
    3
    4
    5
    public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
    m.remove(this);
    }
  • 如果当前线程的 threadLocals 变量不为空,则删除当前线程中指定 ThreadLocal 实例的本地变量

  • 注意

    • 每个线程内部都有一个名字为 threadLocals 的成员变量,该变量类型为 HashMap,其中 key 为我们定义的 ThreadLocal 变量的 this 引用,value 则为我们 set 时候的值
    • 每个线程的本地变量是存到线程自己的内存变量 threadLocals 里面的,如果当前线程一直不消失那么这些本地变量会一直存到,所以可能会造成内存泄露
    • 所以使用完毕后要记得调用 ThreadLocal 的 remove 方法删除对应线程的 threadLocals 中的本地变量

高并发详解

发表于 2018-11-14 | 更新于 2019-05-04 | 分类于 distributed

##

理解OSI七层模型

发表于 2018-11-14 | 更新于 2019-05-05 | 分类于 network

概念

  • OSI模型,即开放式通信系统互联参考模型(Open System Interconnection,OSI/RM,Open Systems InterconnectionReference Model),是国际标准化组织(ISO)提出的一个试图使各种计算机在世界范围内互连为网络的标准框架,简称OSI

七层模型

  • 0SI/RM协议是由IS0(国际标准化组织)制定的,它有三个基本的功能:提供给开发者一个必须的、通用的概念以便开发完善、可以用来解释连接不同系统的框架
    OSI Mode
  • 应用层(Application Layer)

    • 提供网络与用户应用软件之间的接口服务
  • 表示层 (Presentation Layer)

    • 提供格式化的表示和转换数据服务,如加密和压缩
  • 会话层 (Session Layer)

    • 提供包括访问验证和会话管理在内的建立和维护应用之间通信的机制
  • 传输层(Transimission Layer)

    • 提供建立、维护和取消传输连接功能,负责可靠地传输数据(PC)
  • 网络层(NetWork Layer)

    • 处理网络间路由,确保数据及时传送(路由器)
  • 数据链路层(Data Link Layer)

    • 负责无错传输数据,确认帧、发错重传等(交换机)
  • 物理层(Physics Layer)

    • 提供机械、电气、功能和过程特性(网卡、网线、双绞线、同轴电缆、中继器)
  • 七层中应用层、表示层和会话层由软件控制,传输层、网络层和数据链路层由操作系统控制,物理层有物理设备控制

分布式Session一致性解决方案

发表于 2018-11-10 | 更新于 2019-05-04 | 分类于 distributed

Session复制

  • 在支持Session复制的Web服务器上,通过修改Web服务器的配置,可以实现将Session同步到其它Web服务器上,达到每个Web服务器上都保存一致的Session
    • 优点

      • 代码上不需要做支持和修改
    • 缺点

      • 需要依赖支持的Web服务器,一旦更换成不支持的Web服务器就不能使用了,在数据量很大的情况下不仅占用网络资源,而且会导致延迟
    • 使用场景

      • 只适用于Web服务器比较少且Session数据量少的情况
    • 可用方案

      • 开源方案tomcat-redis-session-manager,暂不支持Tomcat8

Session粘滞

  • 将用户的每次请求都通过某种方法(比如:nginx ip_hash等)强制分发到某一个Web服务器上,只要这个Web服务器上存储了对应Session数据,就可以实现会话跟踪
    • 优点

      • 使用简单,没有额外开销
    • 缺点

      • 一旦某个Web服务器重启或宕机,相对应的Session数据将会丢失,而且需要依赖负载均衡机制
    • 使用场景

      • 对稳定性要求不是很高的业务情景

Session集中管理

  • 在单独的服务器或服务器集群上使用缓存技术,如Redis存储Session数据,集中管理所有的Session,所有的Web服务器都从这个存储介质中存取对应的Session,实现Session共享
    • 优点

      • 可靠性高,减少Web服务器的资源开销
    • 缺点

      • 实现上有些复杂,配置较多
    • 使用场景

      • Web服务器较多、要求高可用性的情况
    • 可用方案

      • 开源方案Spring Session,也可以自己实现,主要是重写HttpServletRequestWrapper中的getSession方法

基于Cookie管理

  • 这种方式每次发起请求的时候都需要将Session数据放到Cookie中传递给服务端
    • 优点

      • 不需要依赖额外外部存储,不需要额外配置
    • 缺点

      • 不安全,易被盗取或篡改;Cookie数量和长度有限制,需要消耗更多网络带宽
    • 使用场景

      • 数据不重要、不敏感且数据量小的情况

总结

  • 经过分析对比以上4种方案,相对来说,Sessioin集中管理更加可靠,使用也是最多
12…4
Rick Liu

Rick Liu

33 日志
17 分类
17 标签
GitHub E-Mail
推荐阅读
  • 百度前端技术学院
近期文章
  • Spring IOC级联容器原理
  • Mac版本破解starUML
  • starUML入门使用
  • Leader选举原理
  • 面试问题总结
© 2019 Rick Liu
由 Hexo 强力驱动 v3.8.0
|
主题 – NexT.Mist v7.1.0