Object's Blog

2月踩坑记录(2020)

字数统计: 1.8k阅读时长: 7 min
2020/02/29 分享

Tomcat8.5+Redis实现Session共享

  • 问题描述

    公司项目需要用到Tomcat集群之间共享Session。

  • 解决方案

    1. 将redis-data-cache.properties放到tomcat的conf目录下,并按需修改配置。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      #-- Redis data-cache configuration

      #- redis hosts ex: 127.0.0.1:6379, 127.0.0.2:6379, 127.0.0.2:6380, ....
      redis.hosts=127.0.0.1:6379

      #- redis password (for stand-alone mode)
      #redis.password=

      #- set true to enable redis cluster mode
      redis.cluster.enabled=false

      #- redis database (default 0)
      #redis.database=0

      #- redis connection timeout (default 2000)
      #redis.timeout=2000
    2. 将jar包分别放入每台tomcat的lib目录下

      需要四个jar:

      commons-pool2-2.2.jar

      jedis-2.5.2.jar

      tomcat-cluster-redis-session-manager-2.0.4.jar

      commons-logging-1.2.jar

    3. 修改每台tomcat的context.xml和web.xml

      1
      2
      3
      4
      5
      6
      7
      <!-- context.xml添加以下内容 -->
      <Valve className="tomcat.request.session.redis.SessionHandlerValve" />
      <Manager className="tomcat.request.session.redis.SessionManager" />

      <!-- Web.xml修改 -->
      <!-- 修改session-timeout节点 (也可以不改)-->
      <session-timeout>60</session-timeout>
    4. 重启Tomcat即可。

Zuul过滤器放行时加参数

  • 问题描述

    在做项目中间遇到的问题,自己实现了一个认证中心,是在认证成功后把token写入cookie中,让浏览器继续进行访问,但是在实际应用中发现,这么做行不通,因为我在访问网关的时候他会与认证中心进行交互获取token,但是获取token写入cookie之前,这个请求就已经发出了,所以在之后的认证过程中是没法获取到这个存有token的参数的。

  • 解决方案

    我的解决方案就是不再使用cookie来存储token,而是直接加载参数中传递,那么Zuul如何在放行之前在请求中加查询参数呢?

    1
    2
    3
    4
    5
    6
    7
    8
    Map<String,List<String>> requestQueryParams = requestContext.getRequestQueryParams();
    //如果参数列表为null,那么就new一个,用于存放token
    if (requestQueryParams==null) requestQueryParams=new HashMap<>();
    ArrayList<String> paramsList = new ArrayList<>();
    paramsList.add(token);
    requestQueryParams.put(ConstantPool.AC_TOKEN, paramsList);
    requestContext.setRequestQueryParams(requestQueryParams);
    requestContext.setSendZuulResponse(true);

SpringCloud多个服务之间SESSION名冲突

  • 问题描述

    在项目中遇到的问题,当用户登录之后访问一个微服务时没问题,但是一旦访问到另一个微服务,那么之前那个微服务的SESSION就被挤掉了,经过排查之后发现是Session的Cookie名字产生了冲突,默认都是JSESSIONID,所以只要修改一下Session的Cookie名字就可以搞定的

  • 解决方案

    1
    2
    3
    4
    5
    server:
    servlet:
    session:
    cookie:
    name: 修改的名

Vert.x发送HTTP请求(JSON)

  • 问题描述

    不需要使用Gson转换为JSON串,直接传送对象,否则jackson无法解析。

  • 解决方案

    1
    2
    3
    4
    5
    6
    //直接传送要序列化为JSON的对象,而不需要在传送之前对对象做toJSON的处理
    webClient.postAbs(sendHeartBeatUrl).sendJson(ServerUtil.serverInfoDtoBuilder(),
    handle->{
    System.out.println("心跳信号已传送");
    System.out.println("返回信息:"+handle.result().bodyAsString());
    });

使用Redis作为MyBatis二级缓存缓存造成缓存中所有数据丢失(已找到解决方案)

  • 问题描述

    使用Redis作为MyBatis的二级缓存,本意是要提高效率,但是却出现了一个大坑。
    就是MyBatis做写操作的时候,会调用clear方法,而如果这个方法里我们写的是jedis.flushAll();那么Redis中的所有数据都会消失。
    要知道我们的Redis可不光是拿来做二级缓存的,有时候更是要存一些身份认证信息。那么就会造成每次写操作时,身份信息都会丢失。举个例子,你这边登录的好好的,别人那边一注册,你就掉线了,这谁能受的了。

  • 解决思路

    因为每个namespace下的cache的id是不同的,每当调用putObject写入缓存时,可以使用一个list,将其key都存储在当前id的list下,当执行clear时,清除这个list下的缓存即可。这样就可以达成我想要的结果,当进行写操作时,只删除当前mapper下的缓存,这也符合二级缓存的粒度是mapper级别这项特征。

    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
    @Override
    public void putObject(Object key, Object value) {
    RedisUtil redisUtil = getRedisUtil();
    logger.info("将数据回写Redis缓存。");
    //记录当前id下所存的key
    redisUtil.lpush(SerializeUtil.serialize("MyBatisCache:"+id),SerializeUtil.serialize(key));
    //写入缓存
    redisUtil.set(SerializeUtil.serialize(key), SerializeUtil.serialize(value));

    }

    @Override
    public void clear() {
    //导致写入数据库时 所有的数据被清空
    /*RedisUtil redisUtil = getRedisUtil();
    redisUtil.flushAll();*/
    //采用以下方案即可。
    RedisUtil redisUtil = getRedisUtil();
    //如果不存在这个list,那么也不用再删除了,直接返回。
    if(redisUtil.lLen(SerializeUtil.serialize("MyBatisCache:"+id))==null) return;
    List<byte[]> keys = redisUtil.lrange(SerializeUtil.serialize("MyBatisCache:"+id),0,redisUtil.lLen(SerializeUtil.serialize("MyBatisCache:"+id)));
    //如果list中key为null
    if(keys==null) return;
    for(byte[] key:keys) {
    redisUtil.del(key);
    //删除redis中该元素
    redisUtil.lpop(SerializeUtil.serialize("MyBatisCache:"+id));
    }
    }

Jackson解析布尔值

  • 问题描述

    项目中如果使用jackson,如果变量名是isXXXX那么,经过解析后的JSON会将is去掉,前台就有可能获取不到值。

  • 解决方案

    假设属性名叫isXXX 那么解析之后会将is去掉。要根据阿里的规范,尽量不要使用isXX命名,如果一定要用,记得将get和set方法改为正确的,因为生成get和set默认是getXXX而不是getIsXXX,或者前台获取数据时将is去掉。

多表连接的SQL优化

  • 问题描述

    这是在项目中遇到的问题,使用的数据库是MySQL,在一次查询中需要用到多表连接查询,第一个表大概400条数据,第二个表1600条数据,第三个表2000条数据,第四个表1200条数据,但是就是这么小的数据量,也用了非常长的时间才查询出来。
    原SQL:

    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
    SELECT 
    s.computer_name,
    s.ip,
    s.os_name,
    s.os_version,
    s.os_arch,
    s.mem_total,
    s.swap_total,
    c.cpu_id,
    c.frequency,
    c.vendor,
    c.model,
    d.disk_name,
    d.disk_type,
    d.total,
    net.net_name,
    net_ip,
    net.net_mask
    FROM
    cloud_server_info_now s LEFT JOIN cloud_cpu_info_now c
    ON s.ip = c.cpu_server_ip
    LEFT JOIN cloud_disk_info_now d
    ON c.cpu_server_ip = d.disk_server_ip
    LEFT JOIN cloud_net_info_now net
    ON d.disk_server_ip = net.net_server_ip
    (select (max(record_time)) as max_record_time from cloud_server_info_now) mt
    WHERE s.ip = '169.254.13.82' and s.record_time = mt.max_record_time and s.server_user = 3;
  • 解决方案

    1. 使用explain工具查看SQL状态,发现所有语句都走全表扫描。
      第一次explain
    2. 既然是多条件查询,那么索性上联合索引。
    1
    2
    3
    4
    ALTER TABLE `cloud_server_info_now` ADD INDEX server_info_now_index ( `ip`, `server_user`, `record_time` );
    ALTER TABLE `cloud_cpu_info_now` ADD INDEX cpu_info_now_index ( `cpu_server_ip`, `cpu_server_user`, `record_time` );
    ALTER TABLE `cloud_disk_info_now` ADD INDEX disk_info_now_index ( `disk_server_ip`, `disk_server_user`, `record_time` );
    ALTER TABLE `cloud_net_info_now` ADD INDEX net_info_now_index ( `net_server_ip`, `net_server_user`, `record_time` );
    1. 修改sql
    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
    SELECT 
    s.computer_name,
    s.ip,
    s.os_name,
    s.os_version,
    s.os_arch,
    s.mem_total,
    s.swap_total,
    c.cpu_id,
    c.frequency,
    c.vendor,
    c.model,
    d.disk_name,
    d.disk_type,
    d.total,
    net.net_name,
    net_ip,
    net.net_mask
    FROM
    cloud_server_info_now s LEFT JOIN cloud_cpu_info_now c
    ON s.ip = c.cpu_server_ip and s.server_user = c.cpu_server_user and s.record_time = c.record_time
    LEFT JOIN cloud_disk_info_now d
    ON c.cpu_server_ip = d.disk_server_ip and c.cpu_server_user = d.disk_server_user and c.record_time = d.record_time
    LEFT JOIN cloud_net_info_now net
    ON d.disk_server_ip = net.net_server_ip and d.disk_server_user = net.net_server_user and d.record_time = net.record_time,
    (select (max(record_time)) as max_record_time from cloud_server_info_now) mt
    WHERE s.ip = '169.254.13.82' and s.record_time = mt.max_record_time and s.server_user = 3;
    1. explain再分析
      发现已经全索引。
      第二次Explain
    2. 运行
      优化成功!优化成功图

Zuul+WebSocket

  • 问题描述

    在项目中使用了Zuul网关,但是需要使用WebSocket,发现Zuul不能很好地支持WebSocket,出现握手失败。

  • 解决方案

    在Zuul方面暂时无法解决,我是将网关换成了SpringCloud的Gateway网关,发现已经可以支持websocket了,暂时没有出现什么问题,gateway更多的功能正在学习中。

原文作者:Object

原文链接:http://blog.objectspace.cn/2020/02/29/2月踩坑记录(2020)/

发表日期:2020 February 29th, 4:33:44 pm

更新日期:2020 March 3rd, 9:28:09 pm

版权声明:未经作者授权请勿转载

目录
  1. 1. Tomcat8.5+Redis实现Session共享
    1. 1.1. 问题描述
    2. 1.2. 解决方案
  2. 2. Zuul过滤器放行时加参数
    1. 2.1. 问题描述
    2. 2.2. 解决方案
  3. 3. SpringCloud多个服务之间SESSION名冲突
    1. 3.1. 问题描述
    2. 3.2. 解决方案
  4. 4. Vert.x发送HTTP请求(JSON)
    1. 4.1. 问题描述
    2. 4.2. 解决方案
  5. 5. 使用Redis作为MyBatis二级缓存缓存造成缓存中所有数据丢失(已找到解决方案)
    1. 5.1. 问题描述
    2. 5.2. 解决思路
  6. 6. Jackson解析布尔值
    1. 6.1. 问题描述
    2. 6.2. 解决方案
  7. 7. 多表连接的SQL优化
    1. 7.1. 问题描述
    2. 7.2. 解决方案
  8. 8. Zuul+WebSocket
    1. 8.1. 问题描述
    2. 8.2. 解决方案