内网单点登录系统慢接口(/user/varify)优化总结

背景

最近接到平台研发同事反馈说inpass的/user/varify接口慢,经过沟通后开始排查了,对方给了cat监控的历史,每天大概有个100~200个样子,说是验证超过1s,我这边监控平台除了这个接口其他接口都监控到了,并无异常,因为没有接调用链,架构部也无法给出准确答复,由于历史原因接入调用链的时机并不成熟所以也不好说什么,自己通过access日志排查,发现也没有多慢,因为这个接口是高频接口,本能上告诉对方不会有多慢,但是对方说人家俩接口也是高频接口,,所以我这边也只能说继续跟进了。

说明

1.inpass:内部passport的简单实现,登录一个系统后,登录其他系统不需要再次登录;
2./user/varify:该接口的作用是验证cookie和ticket的有效性,也就是说相当于一个拦截器,在业务系统的每个http请求之后,后端的拦截器会调用这个接口进行登录状态的验证。所以调用量会非常多。

正好,最近有个inpass单设备登录的需求,因此进行了排查
1.首先在接口开始的地方获取当前时间戳,然后在返回的地方获取当前时间戳,然后取时间差,就是毫秒数了。有4个地方进行了返回,因此在4个地方都打印了执行的时间差。
2.经过测试环境的日志打印发现,确实有些有问题,时不时的有100 ~ 600ms之间的调用量,但是90%的情况下是10 ~ 30ms之内返回验证成功。

查代码

验证成功返回后获取用户信息的逻辑_20190802173819.jpg

上图代码中的逻辑是登录成功之后,调用服务填充信息,并返回,然后打印执行时间。
重点就在画红框的地方。
下面跟踪分析一下代码逻辑


开始调用远程接口获取用户信息_20190802174025.jpg

再进上图中的RPC方法里(在另外一个工程里),由于逻辑有点多,不贴代码了,下面使用伪代码说明一下调用情况

User user = userDB.getByid(id);

if(user != null){
    userB = user.copy();//属性复制
    staffRedis =  staffservice.getredis();
    if(staffRedis == null){
         Staff staff = staffDB.getById(id)
         if(staff != null){
            staffB = staff.copy();
            if(staffB.deptid !=null){
                Depart depart = departservice.getredis(staffB.deptid);
                if(depart == null){
                    depart = departDB.getById(id)
                    while(depart.superid != 0){
                        depart = departDB.getById(depart.superid)
                    }
                    departB = depart.copy();
                    departB.putredis();
                }
            }
            
            User userC = userDB.getByStaff(id);
            staffB.setuser(userC.copy());
            staffB.putredis();
         }
    }else{
        Depart depart = departservice.getredis(staffB.deptid);
        if(depart == null){
            depart = departDB.getById(id)
            while(depart.superid != 0){
                depart = departDB.getById(depart.superid)
            }
            departB = depart.copy();
            departB.putredis();
        }
    }
}

当然,通过上面代码我们可以知道,为啥很少的情况下会出现100~600之间的调用返回了,同时,由于有多次属性copy,多次redis调用,多次db调用从而导致接口抖动,后面再看,登录成功或者验证登录状态成功之后我们需要返回哪些信息
userId,userName,staffId,nickName,handphone,jobNumber,email,departmentId,departmentName

那么分析到这里该怎么优化呢

  1. 重写后端getById逻辑
  2. 提供新接口获取以上字段数据
  3. 提供新接口获取以上字段数据并实时缓存,保证缓存永久有效

三种思路

  1. 接口可能被别的地方调用,重写影响稳定性
  2. 按字段逻辑需要取三张表的数据(user,staff,depart),由于是高频访问接口(DB+Redis)的普通访问逻辑同样存在多次调用的情况,连表查+Redis的方案也不行,高频访问的话,总会有redis key失效的时候,由于底层DB被多个上层应用共享,导致Redis key失效的情况很多,不可能让其他服务专门清这些redis,所以也不完美
  3. 提供新接口的时候做如下操作
  • 调用RCP服务直接根据userID取Redis
  • 使用定时任务线程从User表中获取所有在职的员工的账号,然后取所有关联表的staff,department表中的相关字段的数据
  • 然后根据userId遍历Redis中的相关key,进行更新
  1. 最后可以设置userID_redis的失效时间是2小时,定时任务的执行频率是30分钟一次,即可满足Redis数据永不失效,同时提供近实时的数据

看数据量:user:33370,staff:26437,department:2456,虽然不是很多,由于是高频访问(qps200左右),且其他有很多应用系统也是直连这个库,所以有必要采用第三种方案优化

总结

由于很长时间没有反馈说这个系统有问题了,毕竟这个系统是个只负责登录的系统,同时肩负着全公司近200多个业务系统的使用和验证,因此非常有必要进行排查以及代码优化。幸好有同事反馈,不然还不知道背后的调用过程如此繁琐,影响接入方。

你可能感兴趣的:(内网单点登录系统慢接口(/user/varify)优化总结)