1、Redis是简介

  • redis官方网
  • Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。从2010年3月15日起,Redis的开发工作由VMware主持。从2013年5月开始,Redis的开发由Pivotal赞助
  • 工程基于基础架构 Kotlin+SpringBoot+MyBatisPlus搭建最简洁的前后端分离框架美搭建最简洁最酷的前后端分离框架进行完善

2、Redis开发者

  • redis 的作者,叫Salvatore Sanfilippo,来自意大利的西西里岛,现在居住在卡塔尼亚。目前供职于Pivotal公司。他使用的网名是antirez。

3、Redis安装

  • Redis安装与其他知识点请参考几年前我编写文档 Redis Detailed operating instruction.pdf,这里不做太多的描述,主要讲解在kotlin+SpringBoot然后搭建Redis与遇到的问题

Redis详细使用说明书.pdf

https://github.com/jilongliang/kotlin/blob/dev-redis/src/main/resource/Redis%20Detailed%20operating%20instruction.pdf

4、Redis应该学习哪些?

  • 列举一些常见的内容



redis pipeline 中间会执行其他的命令吗 redis pipline 和 lua_Redis


5、Redis有哪些命令

Redis官方命令清单 https://redis.io/commands

  • Redis常用命令


redis pipeline 中间会执行其他的命令吗 redis pipline 和 lua_redis_02

Redis常用命令


6、 Redis常见应用场景


redis pipeline 中间会执行其他的命令吗 redis pipline 和 lua_限流_03

应用场景


7、 Redis常见的几种特征

  • Redis的哨兵机制
  • Redis的原子性
  • Redis持久化有RDB与AOF方式

8、工程结构


redis pipeline 中间会执行其他的命令吗 redis pipline 和 lua_lua get reused time_04

工程结构


9、Kotlin与Redis+Lua的代码实现

  • Redis 依赖的Jar配置
org.springframework.boot    spring-boot-starter-data-redisredis.clients    jedis
  • LuaConfiguration
  • 设置加载限流lua脚本
@Configurationclass LuaConfiguration {    @Bean    fun redisScript(): DefaultRedisScript {        val redisScript = DefaultRedisScript()        //设置限流lua脚本        redisScript.setScriptSource(ResourceScriptSource(ClassPathResource("limitrate.lua")))        //第1种写法反射转换Number类型        //redisScript.setResultType(Number::class.java)        //第2种写法反射转换Number类型        redisScript.resultType = Number::class.java        return redisScript    }}
  • Lua限流脚本
local key = "request:limit:rate:" .. KEYS[1]    --限流KEYlocal limitCount = tonumber(ARGV[1])            --限流大小local limitTime = tonumber(ARGV[2])             --限流时间local current = tonumber(redis.call('get', key) or "0")if current + 1 > limitCount then --如果超出限流大小    return 0else  --请求数+1,并设置1秒过期    redis.call("INCRBY", key,"1")    redis.call("expire", key,limitTime)    return current + 1end
  • RateLimiter自定义注解
  • 1、Java自定义注解Target使用@Target(ElementType.TYPE, ElementType.METHOD)
  • 2、Java自定义注解Retention使用@Retention(RetentionPolicy.RUNTIME)
  • 3、Kotlin自定义注解Target使用@Target(AnnotationTarget.TYPE, AnnotationTarget.FUNCTION)
  • 4、Kotlin自定义注解Target使用@Retention(AnnotationTarget.TYPE, AnnotationTarget.FUNCTION)
@Target(AnnotationTarget.TYPE, AnnotationTarget.FUNCTION)@Retention(AnnotationRetention.RUNTIME)annotation class RateLimiter(        /**         * 限流唯一标识         * @return         */        val key: String = "",        /**         * 限流时间         * @return         */        val time: Int,        /**         * 限流次数         * @return         */        val count: Int )

限流AOP的Aspect切面实现

import com.flong.kotlin.core.annotation.RateLimiterimport com.flong.kotlin.core.exception.BaseExceptionimport com.flong.kotlin.core.exception.CommMsgCodeimport com.flong.kotlin.core.vo.ErrorRespimport com.flong.kotlin.utils.WebUtilsimport org.aspectj.lang.ProceedingJoinPointimport org.aspectj.lang.annotation.Aroundimport org.aspectj.lang.annotation.Aspectimport org.aspectj.lang.reflect.MethodSignatureimport org.slf4j.Loggerimport org.slf4j.LoggerFactoryimport org.springframework.beans.factory.annotation.Autowiredimport org.springframework.context.annotation.Configurationimport org.springframework.data.redis.core.RedisTemplateimport org.springframework.data.redis.core.script.DefaultRedisScriptimport org.springframework.web.context.request.RequestContextHolderimport org.springframework.web.context.request.ServletRequestAttributesimport java.util.*@Suppress("SpringKotlinAutowiring")@Aspect@Configurationclass RateLimiterAspect {    @Autowired lateinit var redisTemplate: RedisTemplate    @Autowired var redisScript: DefaultRedisScript? = null    /**     * 半生对象     */    companion object {        private val log: Logger = LoggerFactory.getLogger(RateLimiterAspect::class.java)    }    @Around("execution(* com.flong.kotlin.modules.controller ..*(..) )")    @Throws(Throwable::class)    fun interceptor(joinPoint: ProceedingJoinPoint): Any {        val signature = joinPoint.signature as MethodSignature        val method = signature.method        val targetClass = method.declaringClass        val rateLimit = method.getAnnotation(RateLimiter::class.java)        if (rateLimit != null) {            val request = (RequestContextHolder.getRequestAttributes() as ServletRequestAttributes).request            val ipAddress = WebUtils.getIpAddr(request = request)            val stringBuffer = StringBuffer()            stringBuffer.append(ipAddress).append("-")                    .append(targetClass.name).append("- ")                    .append(method.name).append("-")                    .append(rateLimit!!.key)            val keys = Collections.singletonList(stringBuffer.toString())            print(keys + rateLimit!!.count + rateLimit!!.time)            val number = redisTemplate!!.execute(redisScript, keys, rateLimit!!.count, rateLimit!!.time)            if (number != null && number!!.toInt() != 0 && number!!.toInt() <= rateLimit!!.count) {                log.info("限流时间段内访问第:{} 次", number!!.toString())                return joinPoint.proceed()            }        } else {            var proceed: Any? = joinPoint.proceed() ?: return ErrorResp(CommMsgCode.SUCCESS.code!!, CommMsgCode.SUCCESS.message!!)            return joinPoint.proceed()        }        throw BaseException(CommMsgCode.RATE_LIMIT.code!!, CommMsgCode.RATE_LIMIT.message!!)    }}
WebUtils代码
import javax.servlet.http.HttpServletRequest/** * User: liangjl * Date: 2020/7/28 * Time: 10:01 */object WebUtils {    fun getIpAddr(request: HttpServletRequest): String? {        var ipAddress: String? = null        try {            ipAddress = request.getHeader("x-forwarded-for")            if (ipAddress == null || ipAddress!!.length == 0 || "unknown".equals(ipAddress!!, ignoreCase = true)) {                ipAddress = request.getHeader("Proxy-Client-IP")            }            if (ipAddress == null || ipAddress!!.length == 0 || "unknown".equals(ipAddress!!, ignoreCase = true)) {                ipAddress = request.getHeader("WL-Proxy-Client-IP")            }            if (ipAddress == null || ipAddress!!.length == 0 || "unknown".equals(ipAddress!!, ignoreCase = true)) {                ipAddress = request.getRemoteAddr()            }            // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割            if (ipAddress != null && ipAddress!!.length > 15) {                // "***.***.***.***".length()= 15                if (ipAddress!!.indexOf(",") > 0) {                    ipAddress = ipAddress!!.substring(0, ipAddress.indexOf(","))                }            }        } catch (e: Exception) {            ipAddress = ""        }        return ipAddress    }}
  • Controller代码
@RestController @RequestMapping("rest") class RateLimiterController {     companion object {         private val log: Logger = LoggerFactory.getLogger(RateLimiterController::class.java)     }      @Autowired     private val redisTemplate: RedisTemplate? = null      @GetMapping(value = "/limit")     @RateLimiter(key = "limit", time = 10, count = 1)     fun limit(): ResponseEntity {          val date = DateFormatUtils.format(Date(), "yyyy-MM-dd HH:mm:ss.SSS")         val limitCounter = RedisAtomicInteger("limit:rate:counter", redisTemplate!!.connectionFactory!!)         val str = date + " 累计访问次数:" + limitCounter.andIncrement         log.info(str)          return ResponseEntity.ok(str)     } }

注意:RedisTemplateK, V>这个类由于有K与V,下面的做法是必须要指定Key-Value 2 type arguments expected for class RedisTemplate

  • 运行结果
  • 访问地址:http://localhost:8080/rest/limit


redis pipeline 中间会执行其他的命令吗 redis pipline 和 lua_Redis_05

成功


redis pipeline 中间会执行其他的命令吗 redis pipline 和 lua_限流_06

失败


10、参考文章

参考分布式限流之Redis+Lua实现参考springboot + aop + Lua分布式限流的最佳实践

11、工程架构源代码

Kotlin+SpringBoot+Redis+Lua实现限流访问控制详解工程源代码

https://github.com/jilongliang/kotlin/tree/dev-lua

12 、总结与建议

  • 1 、以上问题根据搭建 kotlin与Redis实际情况进行总结整理,除了技术问题查很多网上资料,通过自身进行学习之后梳理与分享。
  • 2、 在学习过程中也遇到很多困难和疑点,如有问题或误点,望各位老司机多多指出或者提出建议。本人会采纳各种好建议和正确方式不断完善现况,人在成长过程中的需要优质的养料。
  • 3、 希望此文章能帮助各位老铁们更好去了解如何在 kotlin上搭建RabbitMQ,也希望您看了此文档或者通过找资料进行手动安装效果会更好。