Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
mica
mica
提交
cbaec03a
mica
项目概览
mica
/
mica
通知
10
Star
2
Fork
1
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
mica
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
cbaec03a
编写于
5月 03, 2020
作者:
如梦技术
🐛
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
✨
开源 mica 2.0 mica-redis 模块.
上级
a58fe532
变更
22
展开全部
隐藏空白更改
内联
并排
Showing
22 changed file
with
1984 addition
and
0 deletion
+1984
-0
mica-bom/build.gradle
mica-bom/build.gradle
+1
-0
mica-redis/README.md
mica-redis/README.md
+147
-0
mica-redis/build.gradle
mica-redis/build.gradle
+8
-0
mica-redis/src/main/java/net/dreamlu/mica/redis/cache/CacheKey.java
.../src/main/java/net/dreamlu/mica/redis/cache/CacheKey.java
+49
-0
mica-redis/src/main/java/net/dreamlu/mica/redis/cache/ICacheKey.java
...src/main/java/net/dreamlu/mica/redis/cache/ICacheKey.java
+69
-0
mica-redis/src/main/java/net/dreamlu/mica/redis/cache/MicaRedisCache.java
...ain/java/net/dreamlu/mica/redis/cache/MicaRedisCache.java
+821
-0
mica-redis/src/main/java/net/dreamlu/mica/redis/cache/package-info.java
.../main/java/net/dreamlu/mica/redis/cache/package-info.java
+22
-0
mica-redis/src/main/java/net/dreamlu/mica/redis/config/MicaRedisCacheAutoConfiguration.java
...lu/mica/redis/config/MicaRedisCacheAutoConfiguration.java
+117
-0
mica-redis/src/main/java/net/dreamlu/mica/redis/config/MicaRedisProperties.java
...va/net/dreamlu/mica/redis/config/MicaRedisProperties.java
+50
-0
mica-redis/src/main/java/net/dreamlu/mica/redis/config/RateLimiterAutoConfiguration.java
...eamlu/mica/redis/config/RateLimiterAutoConfiguration.java
+64
-0
mica-redis/src/main/java/net/dreamlu/mica/redis/config/RedisAutoCacheManager.java
.../net/dreamlu/mica/redis/config/RedisAutoCacheManager.java
+62
-0
mica-redis/src/main/java/net/dreamlu/mica/redis/config/RedisCacheManagerConfig.java
...et/dreamlu/mica/redis/config/RedisCacheManagerConfig.java
+42
-0
mica-redis/src/main/java/net/dreamlu/mica/redis/config/RedisTemplateConfiguration.java
...dreamlu/mica/redis/config/RedisTemplateConfiguration.java
+87
-0
mica-redis/src/main/java/net/dreamlu/mica/redis/ratelimiter/RateLimiter.java
.../java/net/dreamlu/mica/redis/ratelimiter/RateLimiter.java
+66
-0
mica-redis/src/main/java/net/dreamlu/mica/redis/ratelimiter/RateLimiterClient.java
...net/dreamlu/mica/redis/ratelimiter/RateLimiterClient.java
+87
-0
mica-redis/src/main/java/net/dreamlu/mica/redis/ratelimiter/RateLimiterException.java
.../dreamlu/mica/redis/ratelimiter/RateLimiterException.java
+42
-0
mica-redis/src/main/java/net/dreamlu/mica/redis/ratelimiter/RedisRateLimiterAspect.java
...reamlu/mica/redis/ratelimiter/RedisRateLimiterAspect.java
+101
-0
mica-redis/src/main/java/net/dreamlu/mica/redis/ratelimiter/RedisRateLimiterClient.java
...reamlu/mica/redis/ratelimiter/RedisRateLimiterClient.java
+82
-0
mica-redis/src/main/resources/META-INF/additional-spring-configuration-metadata.json
...es/META-INF/additional-spring-configuration-metadata.json
+11
-0
mica-redis/src/main/resources/META-INF/scripts/mica_rate_limiter.lua
...src/main/resources/META-INF/scripts/mica_rate_limiter.lua
+27
-0
mica-redis/src/test/java/net/dreamlu/mica/RedisApplicationTests.java
...src/test/java/net/dreamlu/mica/RedisApplicationTests.java
+28
-0
settings.gradle
settings.gradle
+1
-0
未找到文件。
mica-bom/build.gradle
浏览文件 @
cbaec03a
...
...
@@ -23,6 +23,7 @@ dependencyManagement {
dependency
"net.dreamlu:mica-captcha:${VERSION}"
dependency
"net.dreamlu:mica-jobs:${VERSION}"
dependency
"net.dreamlu:mica-mongo:${VERSION}"
dependency
"net.dreamlu:mica-redis:${VERSION}"
// commons
dependency
"com.google.code.findbugs:jsr305:${findbugsVersion}"
dependency
"io.swagger:swagger-annotations:${swaggerAnnotationsVersion}"
...
...
mica-redis/README.md
0 → 100644
浏览文件 @
cbaec03a
# redis-plus-redis
-
redis cache 增强
-
分布式限流组件
## 依赖引用
### maven
```
xml
<dependency>
<groupId>
net.dreamlu
</groupId>
<artifactId>
mica-redis
</artifactId>
<version>
${version}
</version>
</dependency>
```
### gradle
```
groovy
compile
(
"net.dreamlu:mica-redis:${version}"
)
```
## 1. redis cache 增强
1.
支持 # 号分隔 cachename 和 超时,支持 ms(毫秒),s(秒默认),m(分),h(小时),d(天)等单位。
示例:
```
java
@Cacheable
(
value
=
"user#5m"
,
key
=
"#id"
)
public
String
selectById
(
Serializable
id
)
{
log
.
info
(
"selectById"
);
return
"selectById:"
+
id
;
}
```
### MicaRedisCache
MicaRedisCache 为简化 redis 使用的 bean。
```
java
@Autowired
private
MicaRedisCache
redisCache
;
@Override
public
String
findById
(
Serializable
id
)
{
return
redisCache
.
get
(
"user:"
+
id
,
()
->
userMapper
.
selectById
(
id
));
}
```
## 2. 分布式限流
### 2.1 开启限流组件
```
yaml
mica
:
redis
:
rate-limiter
:
enable
:
true
```
### 2.2 使用注解
```
java
@RateLimiter
```
注解变量:
```
java
/**
* 限流的 key 支持,必须:请保持唯一性
*
* @return key
*/
String
value
();
/**
* 限流的参数,可选,支持 spring el # 读取方法参数和 @ 读取 spring bean
*
* @return param
*/
String
param
()
default
""
;
/**
* 支持的最大请求,默认: 2500
*
* @return 请求数
*/
long
max
()
default
2500L
;
/**
* 持续时间,默认: 3600
*
* @return 持续时间
*/
long
ttl
()
default
3600L
;
/**
* 时间单位,默认为秒
*
* @return TimeUnit
*/
TimeUnit
timeUnit
()
default
TimeUnit
.
SECONDS
;
```
### 2.3 使用 Client
```
java
@Autowired
private
RateLimiterClient
rateLimiterClient
;
```
方法:
```
java
/**
* 服务是否被限流
*
* @param key 自定义的key,请保证唯一
* @param max 支持的最大请求
* @param ttl 时间,单位默认为秒(seconds)
* @return 是否允许
*/
boolean
isAllowed
(
String
key
,
long
max
,
long
ttl
);
/**
* 服务是否被限流
*
* @param key 自定义的key,请保证唯一
* @param max 支持的最大请求
* @param ttl 时间
* @param timeUnit 时间单位
* @return 是否允许
*/
boolean
isAllowed
(
String
key
,
long
max
,
long
ttl
,
TimeUnit
timeUnit
);
/**
* 服务限流,被限制时抛出 RateLimiterException 异常,需要自行处理异常
*
* @param key 自定义的key,请保证唯一
* @param max 支持的最大请求
* @param ttl 时间
* @param supplier Supplier 函数式
* @return 函数执行结果
*/
<
T
>
T
allow
(
String
key
,
long
max
,
long
ttl
,
CheckedSupplier
<
T
>
supplier
);
/**
* 服务限流,被限制时抛出 RateLimiterException 异常,需要自行处理异常
*
* @param key 自定义的key,请保证唯一
* @param max 支持的最大请求
* @param ttl 时间
* @param supplier Supplier 函数式
* @return 函数执行结果
*/
<
T
>
T
allow
(
String
key
,
long
max
,
long
ttl
,
TimeUnit
timeUnit
,
CheckedSupplier
<
T
>
supplier
);
```
\ No newline at end of file
mica-redis/build.gradle
0 → 100644
浏览文件 @
cbaec03a
dependencies
{
api
project
(
":mica-core"
)
api
"org.springframework.boot:spring-boot-starter-aop"
api
"org.springframework.boot:spring-boot-starter-data-redis"
compileOnly
"org.springframework.cloud:spring-cloud-context"
annotationProcessor
"net.dreamlu:mica-auto:${micaAutoVersion}"
testImplementation
"org.springframework.boot:spring-boot-starter-test"
}
mica-redis/src/main/java/net/dreamlu/mica/redis/cache/CacheKey.java
0 → 100644
浏览文件 @
cbaec03a
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
net.dreamlu.mica.redis.cache
;
import
lombok.AllArgsConstructor
;
import
lombok.Getter
;
import
lombok.ToString
;
import
org.springframework.lang.Nullable
;
import
java.time.Duration
;
/**
* cache key 封装
*
* @author L.cm
*/
@Getter
@ToString
@AllArgsConstructor
public
class
CacheKey
{
/**
* redis key
*/
private
String
key
;
/**
* 超时时间 秒
*/
@Nullable
private
Duration
expire
;
public
CacheKey
(
String
key
)
{
this
.
key
=
key
;
}
}
mica-redis/src/main/java/net/dreamlu/mica/redis/cache/ICacheKey.java
0 → 100644
浏览文件 @
cbaec03a
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
net.dreamlu.mica.redis.cache
;
import
net.dreamlu.mica.core.utils.ObjectUtil
;
import
net.dreamlu.mica.core.utils.StringPool
;
import
net.dreamlu.mica.core.utils.StringUtil
;
import
javax.annotation.Nullable
;
import
java.time.Duration
;
/**
* cache key
*
* @author L.cm
*/
public
interface
ICacheKey
{
/**
* 获取前缀
*
* @return key 前缀
*/
String
getPrefix
();
/**
* 超时时间
*
* @return 超时时间
*/
@Nullable
default
Duration
getExpire
()
{
return
null
;
}
/**
* 组装 cache key
*
* @param suffix 参数
* @return cache key
*/
default
CacheKey
getKey
(
Object
...
suffix
)
{
String
prefix
=
this
.
getPrefix
();
// 拼接参数
String
key
;
if
(
ObjectUtil
.
isEmpty
(
suffix
))
{
key
=
prefix
;
}
else
{
key
=
prefix
.
concat
(
StringUtil
.
join
(
suffix
,
StringPool
.
COLON
));
}
Duration
expire
=
this
.
getExpire
();
return
expire
==
null
?
new
CacheKey
(
key
)
:
new
CacheKey
(
key
,
expire
);
}
}
mica-redis/src/main/java/net/dreamlu/mica/redis/cache/MicaRedisCache.java
0 → 100644
浏览文件 @
cbaec03a
此差异已折叠。
点击以展开。
mica-redis/src/main/java/net/dreamlu/mica/redis/cache/package-info.java
0 → 100644
浏览文件 @
cbaec03a
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@NonNullApi
@NonNullFields
package
net.dreamlu.mica.redis.cache
;
import
org.springframework.lang.NonNullApi
;
import
org.springframework.lang.NonNullFields
;
mica-redis/src/main/java/net/dreamlu/mica/redis/config/MicaRedisCacheAutoConfiguration.java
0 → 100644
浏览文件 @
cbaec03a
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
net.dreamlu.mica.redis.config
;
import
org.springframework.beans.factory.ObjectProvider
;
import
org.springframework.boot.autoconfigure.AutoConfigureAfter
;
import
org.springframework.boot.autoconfigure.cache.CacheManagerCustomizers
;
import
org.springframework.boot.autoconfigure.cache.CacheProperties
;
import
org.springframework.boot.autoconfigure.condition.ConditionalOnBean
;
import
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
;
import
org.springframework.boot.context.properties.EnableConfigurationProperties
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.context.annotation.Primary
;
import
org.springframework.data.redis.cache.RedisCacheConfiguration
;
import
org.springframework.data.redis.cache.RedisCacheManager
;
import
org.springframework.data.redis.cache.RedisCacheWriter
;
import
org.springframework.data.redis.connection.RedisConnectionFactory
;
import
org.springframework.data.redis.serializer.RedisSerializationContext
;
import
org.springframework.data.redis.serializer.RedisSerializer
;
import
org.springframework.lang.Nullable
;
import
java.util.LinkedHashMap
;
import
java.util.List
;
import
java.util.Map
;
/**
* 扩展redis-cache支持注解cacheName添加超时时间
* <p>
*
* @author L.cm
*/
@Configuration
@AutoConfigureAfter
(
RedisAutoConfiguration
.
class
)
@ConditionalOnBean
(
RedisConnectionFactory
.
class
)
@EnableConfigurationProperties
(
CacheProperties
.
class
)
public
class
MicaRedisCacheAutoConfiguration
{
/**
* 序列化方式
*/
private
final
RedisSerializer
<
Object
>
redisSerializer
;
private
final
CacheProperties
cacheProperties
;
private
final
CacheManagerCustomizers
customizerInvoker
;
@Nullable
private
final
RedisCacheConfiguration
redisCacheConfiguration
;
MicaRedisCacheAutoConfiguration
(
RedisSerializer
<
Object
>
redisSerializer
,
CacheProperties
cacheProperties
,
CacheManagerCustomizers
customizerInvoker
,
ObjectProvider
<
RedisCacheConfiguration
>
redisCacheConfiguration
)
{
this
.
redisSerializer
=
redisSerializer
;
this
.
cacheProperties
=
cacheProperties
;
this
.
customizerInvoker
=
customizerInvoker
;
this
.
redisCacheConfiguration
=
redisCacheConfiguration
.
getIfAvailable
();
}
@Primary
@Bean
(
"redisCacheManager"
)
public
RedisCacheManager
redisCacheManager
(
RedisConnectionFactory
connectionFactory
)
{
RedisCacheWriter
redisCacheWriter
=
RedisCacheWriter
.
nonLockingRedisCacheWriter
(
connectionFactory
);
RedisCacheConfiguration
cacheConfiguration
=
this
.
determineConfiguration
();
List
<
String
>
cacheNames
=
this
.
cacheProperties
.
getCacheNames
();
Map
<
String
,
RedisCacheConfiguration
>
initialCaches
=
new
LinkedHashMap
<>();
if
(!
cacheNames
.
isEmpty
())
{
Map
<
String
,
RedisCacheConfiguration
>
cacheConfigMap
=
new
LinkedHashMap
<>(
cacheNames
.
size
());
cacheNames
.
forEach
(
it
->
cacheConfigMap
.
put
(
it
,
cacheConfiguration
));
initialCaches
.
putAll
(
cacheConfigMap
);
}
boolean
allowInFlightCacheCreation
=
true
;
boolean
enableTransactions
=
false
;
RedisAutoCacheManager
cacheManager
=
new
RedisAutoCacheManager
(
redisCacheWriter
,
cacheConfiguration
,
initialCaches
,
allowInFlightCacheCreation
);
cacheManager
.
setTransactionAware
(
enableTransactions
);
return
this
.
customizerInvoker
.
customize
(
cacheManager
);
}
private
RedisCacheConfiguration
determineConfiguration
()
{
if
(
this
.
redisCacheConfiguration
!=
null
)
{
return
this
.
redisCacheConfiguration
;
}
else
{
CacheProperties
.
Redis
redisProperties
=
this
.
cacheProperties
.
getRedis
();
RedisCacheConfiguration
config
=
RedisCacheConfiguration
.
defaultCacheConfig
();
config
=
config
.
serializeValuesWith
(
RedisSerializationContext
.
SerializationPair
.
fromSerializer
(
redisSerializer
));
if
(
redisProperties
.
getTimeToLive
()
!=
null
)
{
config
=
config
.
entryTtl
(
redisProperties
.
getTimeToLive
());
}
if
(
redisProperties
.
getKeyPrefix
()
!=
null
)
{
config
=
config
.
prefixKeysWith
(
redisProperties
.
getKeyPrefix
());
}
if
(!
redisProperties
.
isCacheNullValues
())
{
config
=
config
.
disableCachingNullValues
();
}
if
(!
redisProperties
.
isUseKeyPrefix
())
{
config
=
config
.
disableKeyPrefix
();
}
return
config
;
}
}
}
mica-redis/src/main/java/net/dreamlu/mica/redis/config/MicaRedisProperties.java
0 → 100644
浏览文件 @
cbaec03a
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
net.dreamlu.mica.redis.config
;
import
lombok.Getter
;
import
lombok.Setter
;
import
org.springframework.boot.context.properties.ConfigurationProperties
;
import
org.springframework.cloud.context.config.annotation.RefreshScope
;
/**
* redis 配置
*
* @author L.cm
*/
@Getter
@Setter
@RefreshScope
@ConfigurationProperties
(
"mica.redis"
)
public
class
MicaRedisProperties
{
/**
* 序列化方式
*/
private
SerializerType
serializerType
=
SerializerType
.
JSON
;
public
enum
SerializerType
{
/**
* json 序列化
*/
JSON
,
/**
* jdk 序列化
*/
JDK
}
}
mica-redis/src/main/java/net/dreamlu/mica/redis/config/RateLimiterAutoConfiguration.java
0 → 100644
浏览文件 @
cbaec03a
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
net.dreamlu.mica.redis.config
;
import
net.dreamlu.mica.redis.ratelimiter.RedisRateLimiterAspect
;
import
net.dreamlu.mica.redis.ratelimiter.RedisRateLimiterClient
;
import
org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
;
import
org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.core.env.Environment
;
import
org.springframework.core.io.ClassPathResource
;
import
org.springframework.data.redis.core.StringRedisTemplate
;
import
org.springframework.data.redis.core.script.DefaultRedisScript
;
import
org.springframework.data.redis.core.script.RedisScript
;
import
org.springframework.scripting.support.ResourceScriptSource
;
import
java.util.List
;
/**
* 基于 redis 的分布式限流自动配置
*
* @author L.cm
*/
@Configuration
@ConditionalOnProperty
(
value
=
"mica.redis.rate-limiter.enable"
)
public
class
RateLimiterAutoConfiguration
{
@SuppressWarnings
(
"unchecked"
)
private
RedisScript
<
List
<
Long
>>
redisRateLimiterScript
()
{
DefaultRedisScript
redisScript
=
new
DefaultRedisScript
<>();
redisScript
.
setScriptSource
(
new
ResourceScriptSource
(
new
ClassPathResource
(
"META-INF/scripts/mica_rate_limiter.lua"
)));
redisScript
.
setResultType
(
List
.
class
);
return
redisScript
;
}
@Bean
@ConditionalOnMissingBean
public
RedisRateLimiterClient
redisRateLimiter
(
StringRedisTemplate
redisTemplate
,
Environment
environment
)
{
RedisScript
<
List
<
Long
>>
redisRateLimiterScript
=
redisRateLimiterScript
();
return
new
RedisRateLimiterClient
(
redisTemplate
,
redisRateLimiterScript
,
environment
);
}
@Bean
@ConditionalOnMissingBean
public
RedisRateLimiterAspect
redisRateLimiterAspect
(
RedisRateLimiterClient
rateLimiterClient
)
{
return
new
RedisRateLimiterAspect
(
rateLimiterClient
);
}
}
mica-redis/src/main/java/net/dreamlu/mica/redis/config/RedisAutoCacheManager.java
0 → 100644
浏览文件 @
cbaec03a
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
net.dreamlu.mica.redis.config
;
import
net.dreamlu.mica.core.utils.StringPool
;
import
net.dreamlu.mica.core.utils.StringUtil
;
import
org.springframework.boot.convert.DurationStyle
;
import
org.springframework.data.redis.cache.RedisCache
;
import
org.springframework.data.redis.cache.RedisCacheConfiguration
;
import
org.springframework.data.redis.cache.RedisCacheManager
;
import
org.springframework.data.redis.cache.RedisCacheWriter
;
import
org.springframework.lang.Nullable
;
import
java.time.Duration
;
import
java.time.temporal.ChronoUnit
;
import
java.util.Map
;
/**
* redis cache 扩展cache name自动化配置
*
* @author L.cm
*/
public
class
RedisAutoCacheManager
extends
RedisCacheManager
{
public
RedisAutoCacheManager
(
RedisCacheWriter
cacheWriter
,
RedisCacheConfiguration
defaultCacheConfiguration
,
Map
<
String
,
RedisCacheConfiguration
>
initialCacheConfigurations
,
boolean
allowInFlightCacheCreation
)
{
super
(
cacheWriter
,
defaultCacheConfiguration
,
initialCacheConfigurations
,
allowInFlightCacheCreation
);
}
@Override
protected
RedisCache
createRedisCache
(
String
name
,
@Nullable
RedisCacheConfiguration
cacheConfig
)
{
if
(
StringUtil
.
isBlank
(
name
)
||
!
name
.
contains
(
StringPool
.
HASH
))
{
return
super
.
createRedisCache
(
name
,
cacheConfig
);
}
String
[]
cacheArray
=
name
.
split
(
StringPool
.
HASH
);
if
(
cacheArray
.
length
<
2
)
{
return
super
.
createRedisCache
(
name
,
cacheConfig
);
}
String
cacheName
=
cacheArray
[
0
];
if
(
cacheConfig
!=
null
)
{
// 转换时间,支持时间单位例如:300ms,第二个参数是默认单位
Duration
duration
=
DurationStyle
.
detectAndParse
(
cacheArray
[
1
],
ChronoUnit
.
SECONDS
);
cacheConfig
=
cacheConfig
.
entryTtl
(
duration
);
}
return
super
.
createRedisCache
(
cacheName
,
cacheConfig
);
}
}
mica-redis/src/main/java/net/dreamlu/mica/redis/config/RedisCacheManagerConfig.java
0 → 100644
浏览文件 @
cbaec03a
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
net.dreamlu.mica.redis.config
;
import
org.springframework.beans.factory.ObjectProvider
;
import
org.springframework.boot.autoconfigure.cache.CacheManagerCustomizer
;
import
org.springframework.boot.autoconfigure.cache.CacheManagerCustomizers
;
import
org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
java.util.List
;
/**
* CacheManagerCustomizers配置
*
* @author L.cm
*/
@Configuration
@ConditionalOnMissingBean
(
CacheManagerCustomizers
.
class
)
public
class
RedisCacheManagerConfig
{
@Bean
public
CacheManagerCustomizers
cacheManagerCustomizers
(
ObjectProvider
<
List
<
CacheManagerCustomizer
<?>>>
customizers
)
{
return
new
CacheManagerCustomizers
(
customizers
.
getIfAvailable
());
}
}
mica-redis/src/main/java/net/dreamlu/mica/redis/config/RedisTemplateConfiguration.java
0 → 100644
浏览文件 @
cbaec03a
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
net.dreamlu.mica.redis.config
;
import
net.dreamlu.mica.redis.cache.MicaRedisCache
;
import
org.springframework.boot.autoconfigure.AutoConfigureBefore
;
import
org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
;
import
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
;
import
org.springframework.boot.context.properties.EnableConfigurationProperties
;
import
org.springframework.cache.annotation.EnableCaching
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.data.redis.connection.RedisConnectionFactory
;
import
org.springframework.data.redis.core.RedisTemplate
;
import
org.springframework.data.redis.core.ValueOperations
;
import
org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer
;
import
org.springframework.data.redis.serializer.JdkSerializationRedisSerializer
;
import
org.springframework.data.redis.serializer.RedisSerializer
;
import
org.springframework.data.redis.serializer.StringRedisSerializer
;
/**
* RedisTemplate 配置
*
* @author L.cm
*/
@EnableCaching
@Configuration
@AutoConfigureBefore
(
RedisAutoConfiguration
.
class
)
@EnableConfigurationProperties
(
MicaRedisProperties
.
class
)
public
class
RedisTemplateConfiguration
{
/**
* value 值 序列化
*
* @return RedisSerializer
*/
@Bean
@ConditionalOnMissingBean
(
RedisSerializer
.
class
)
public
RedisSerializer
<
Object
>
redisSerializer
(
MicaRedisProperties
properties
)
{
MicaRedisProperties
.
SerializerType
serializerType
=
properties
.
getSerializerType
();
if
(
MicaRedisProperties
.
SerializerType
.
JDK
==
serializerType
)
{
return
new
JdkSerializationRedisSerializer
();
}
return
new
GenericJackson2JsonRedisSerializer
();
}
@Bean
(
name
=
"redisTemplate"
)
@ConditionalOnMissingBean
(
RedisTemplate
.
class
)
public
RedisTemplate
<
String
,
Object
>
redisTemplate
(
RedisConnectionFactory
redisConnectionFactory
,
RedisSerializer
<
Object
>
redisSerializer
)
{
RedisTemplate
<
String
,
Object
>
redisTemplate
=
new
RedisTemplate
<>();
// key 序列化
StringRedisSerializer
keySerializer
=
new
StringRedisSerializer
();
redisTemplate
.
setKeySerializer
(
keySerializer
);
redisTemplate
.
setHashKeySerializer
(
keySerializer
);
// value 序列化
redisTemplate
.
setValueSerializer
(
redisSerializer
);
redisTemplate
.
setHashValueSerializer
(
redisSerializer
);
redisTemplate
.
setConnectionFactory
(
redisConnectionFactory
);
return
redisTemplate
;
}
@Bean
@ConditionalOnMissingBean
(
ValueOperations
.
class
)
public
ValueOperations
valueOperations
(
RedisTemplate
redisTemplate
)
{
return
redisTemplate
.
opsForValue
();
}
@Bean
public
MicaRedisCache
redisClient
(
RedisTemplate
<
String
,
Object
>
redisTemplate
)
{
return
new
MicaRedisCache
(
redisTemplate
);
}
}
mica-redis/src/main/java/net/dreamlu/mica/redis/ratelimiter/RateLimiter.java
0 → 100644
浏览文件 @
cbaec03a
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
net.dreamlu.mica.redis.ratelimiter
;
import
java.lang.annotation.*
;
import
java.util.concurrent.TimeUnit
;
/**
* 分布式 限流注解,默认速率为 600/ms
*
* @author L.cm
*/
@Retention
(
RetentionPolicy
.
RUNTIME
)
@Target
(
ElementType
.
METHOD
)
@Documented
public
@interface
RateLimiter
{
/**
* 限流的 key 支持,必须:请保持唯一性
*
* @return key
*/
String
value
();
/**
* 限流的参数,可选,支持 spring el # 读取方法参数和 @ 读取 spring bean
*
* @return param
*/
String
param
()
default
""
;
/**
* 支持的最大请求,默认: 100
*
* @return 请求数
*/
long
max
()
default
100L
;
/**
* 持续时间,默认: 3600
*
* @return 持续时间
*/
long
ttl
()
default
1L
;
/**
* 时间单位,默认为分
*
* @return TimeUnit
*/
TimeUnit
timeUnit
()
default
TimeUnit
.
MINUTES
;
}
mica-redis/src/main/java/net/dreamlu/mica/redis/ratelimiter/RateLimiterClient.java
0 → 100644
浏览文件 @
cbaec03a
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
net.dreamlu.mica.redis.ratelimiter
;
import
net.dreamlu.mica.core.function.CheckedSupplier
;
import
net.dreamlu.mica.core.utils.Exceptions
;
import
java.util.concurrent.TimeUnit
;
/**
* RateLimiter 限流 Client
*
* @author L.cm
*/
public
interface
RateLimiterClient
{
/**
* 服务是否被限流
*
* @param key 自定义的key,请保证唯一
* @param max 支持的最大请求
* @param ttl 时间,单位默认为秒(seconds)
* @return 是否允许
*/
default
boolean
isAllowed
(
String
key
,
long
max
,
long
ttl
)
{
return
this
.
isAllowed
(
key
,
max
,
ttl
,
TimeUnit
.
SECONDS
);
}
/**
* 服务是否被限流
*
* @param key 自定义的key,请保证唯一
* @param max 支持的最大请求
* @param ttl 时间
* @param timeUnit 时间单位
* @return 是否允许
*/
boolean
isAllowed
(
String
key
,
long
max
,
long
ttl
,
TimeUnit
timeUnit
);
/**
* 服务限流,被限制时抛出 RateLimiterException 异常,需要自行处理异常
*
* @param key 自定义的key,请保证唯一
* @param max 支持的最大请求
* @param ttl 时间
* @param supplier Supplier 函数式
* @return 函数执行结果
*/
default
<
T
>
T
allow
(
String
key
,
long
max
,
long
ttl
,
CheckedSupplier
<
T
>
supplier
)
{
return
allow
(
key
,
max
,
ttl
,
TimeUnit
.
SECONDS
,
supplier
);
}
/**
* 服务限流,被限制时抛出 RateLimiterException 异常,需要自行处理异常
*
* @param key 自定义的key,请保证唯一
* @param max 支持的最大请求
* @param ttl 时间
* @param supplier Supplier 函数式
* @return 函数执行结果
*/
default
<
T
>
T
allow
(
String
key
,
long
max
,
long
ttl
,
TimeUnit
timeUnit
,
CheckedSupplier
<
T
>
supplier
)
{
boolean
isAllowed
=
this
.
isAllowed
(
key
,
max
,
ttl
,
timeUnit
);
if
(
isAllowed
)
{
try
{
return
supplier
.
get
();
}
catch
(
Throwable
e
)
{
throw
Exceptions
.
unchecked
(
e
);
}
}
throw
new
RateLimiterException
(
key
,
max
,
ttl
,
timeUnit
);
}
}
mica-redis/src/main/java/net/dreamlu/mica/redis/ratelimiter/RateLimiterException.java
0 → 100644
浏览文件 @
cbaec03a
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
net.dreamlu.mica.redis.ratelimiter
;
import
lombok.Getter
;
import
java.util.concurrent.TimeUnit
;
/**
* 限流异常
*
* @author L.cm
*/
@Getter
public
class
RateLimiterException
extends
RuntimeException
{
private
final
String
key
;
private
final
long
max
;
private
final
long
ttl
;
private
final
TimeUnit
timeUnit
;
public
RateLimiterException
(
String
key
,
long
max
,
long
ttl
,
TimeUnit
timeUnit
)
{
super
(
String
.
format
(
"您的访问次数已超限:%s,速率:%d/%ds"
,
key
,
max
,
timeUnit
.
toSeconds
(
ttl
)));
this
.
key
=
key
;
this
.
max
=
max
;
this
.
ttl
=
ttl
;
this
.
timeUnit
=
timeUnit
;
}
}
mica-redis/src/main/java/net/dreamlu/mica/redis/ratelimiter/RedisRateLimiterAspect.java
0 → 100644
浏览文件 @
cbaec03a
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
net.dreamlu.mica.redis.ratelimiter
;
import
lombok.RequiredArgsConstructor
;
import
net.dreamlu.mica.core.spel.MicaExpressionEvaluator
;
import
net.dreamlu.mica.core.utils.CharPool
;
import
net.dreamlu.mica.core.utils.StringUtil
;
import
org.aspectj.lang.ProceedingJoinPoint
;
import
org.aspectj.lang.annotation.Around
;
import
org.aspectj.lang.annotation.Aspect
;
import
org.aspectj.lang.reflect.MethodSignature
;
import
org.springframework.beans.BeansException
;
import
org.springframework.context.ApplicationContext
;
import
org.springframework.context.ApplicationContextAware
;
import
org.springframework.context.expression.AnnotatedElementKey
;
import
org.springframework.expression.EvaluationContext
;
import
org.springframework.lang.NonNull
;
import
org.springframework.util.Assert
;
import
java.lang.reflect.Method
;
import
java.util.concurrent.TimeUnit
;
/**
* redis 限流
*
* @author L.cm
*/
@Aspect
@RequiredArgsConstructor
public
class
RedisRateLimiterAspect
implements
ApplicationContextAware
{
/**
* 表达式处理
*/
private
final
MicaExpressionEvaluator
evaluator
=
new
MicaExpressionEvaluator
();
/**
* redis 限流服务
*/
private
final
RedisRateLimiterClient
rateLimiterClient
;
private
ApplicationContext
applicationContext
;
/**
* AOP 环切 注解 @RateLimiter
*/
@Around
(
"@annotation(limiter)"
)
public
Object
aroundRateLimiter
(
ProceedingJoinPoint
point
,
RateLimiter
limiter
)
throws
Throwable
{
String
limitKey
=
limiter
.
value
();
Assert
.
hasText
(
limitKey
,
"@RateLimiter value must have length; it must not be null or empty"
);
// el 表达式
String
limitParam
=
limiter
.
param
();
// 表达式不为空
String
rateKey
;
if
(
StringUtil
.
isNotBlank
(
limitParam
))
{
String
evalAsText
=
evalLimitParam
(
point
,
limitParam
);
rateKey
=
limitKey
+
CharPool
.
COLON
+
evalAsText
;
}
else
{
rateKey
=
limitKey
;
}
long
max
=
limiter
.
max
();
long
ttl
=
limiter
.
ttl
();
TimeUnit
timeUnit
=
limiter
.
timeUnit
();
return
rateLimiterClient
.
allow
(
rateKey
,
max
,
ttl
,
timeUnit
,
point:
:
proceed
);
}
/**
* 计算参数表达式
*
* @param point ProceedingJoinPoint
* @param limitParam limitParam
* @return 结果
*/
private
String
evalLimitParam
(
ProceedingJoinPoint
point
,
String
limitParam
)
{
MethodSignature
ms
=
(
MethodSignature
)
point
.
getSignature
();
Method
method
=
ms
.
getMethod
();
Object
[]
args
=
point
.
getArgs
();
Object
target
=
point
.
getTarget
();
Class
<?>
targetClass
=
target
.
getClass
();
EvaluationContext
context
=
evaluator
.
createContext
(
method
,
args
,
target
,
targetClass
,
applicationContext
);
AnnotatedElementKey
elementKey
=
new
AnnotatedElementKey
(
method
,
targetClass
);
return
evaluator
.
evalAsText
(
limitParam
,
elementKey
,
context
);
}
@Override
public
void
setApplicationContext
(
@NonNull
ApplicationContext
applicationContext
)
throws
BeansException
{
this
.
applicationContext
=
applicationContext
;
}
}
mica-redis/src/main/java/net/dreamlu/mica/redis/ratelimiter/RedisRateLimiterClient.java
0 → 100644
浏览文件 @
cbaec03a
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
net.dreamlu.mica.redis.ratelimiter
;
import
lombok.RequiredArgsConstructor
;
import
net.dreamlu.mica.core.utils.CharPool
;
import
org.springframework.core.env.Environment
;
import
org.springframework.data.redis.core.StringRedisTemplate
;
import
org.springframework.data.redis.core.script.RedisScript
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.concurrent.TimeUnit
;
/**
* redis 限流服务
*
* @author dream.lu
*/
@RequiredArgsConstructor
public
class
RedisRateLimiterClient
implements
RateLimiterClient
{
/**
* redis 限流 key 前缀
*/
private
static
final
String
REDIS_KEY_PREFIX
=
"limiter:"
;
/**
* 失败的默认返回值
*/
private
static
final
long
FAIL_CODE
=
0
;
/**
* redisTemplate
*/
private
final
StringRedisTemplate
redisTemplate
;
/**
* redisScript
*/
private
final
RedisScript
<
List
<
Long
>>
script
;
/**
* env
*/
private
final
Environment
environment
;
@Override
public
boolean
isAllowed
(
String
key
,
long
max
,
long
ttl
,
TimeUnit
timeUnit
)
{
// redis key
String
redisKeyBuilder
=
REDIS_KEY_PREFIX
+
getApplicationName
(
environment
)
+
CharPool
.
COLON
+
key
;
List
<
String
>
keys
=
Collections
.
singletonList
(
redisKeyBuilder
);
// 毫秒,考虑主从策略和脚本回放机制,这个time由客户端获取传入
long
now
=
System
.
currentTimeMillis
();
// 转为毫秒,pexpire
long
ttlMillis
=
timeUnit
.
toMillis
(
ttl
);
// 执行命令
List
<
Long
>
results
=
this
.
redisTemplate
.
execute
(
this
.
script
,
keys
,
max
+
""
,
ttlMillis
+
""
,
now
+
""
);
// 结果为空返回失败
if
(
results
==
null
||
results
.
isEmpty
())
{
return
false
;
}
// 判断返回成功
Long
result
=
results
.
get
(
0
);
return
result
!=
FAIL_CODE
;
}
private
static
String
getApplicationName
(
Environment
environment
)
{
return
environment
.
getProperty
(
"spring.application.name"
,
""
);
}
}
mica-redis/src/main/resources/META-INF/additional-spring-configuration-metadata.json
0 → 100644
浏览文件 @
cbaec03a
{
"properties"
:
[
{
"name"
:
"mica.redis.rate-limiter.enable"
,
"type"
:
"java.lang.Boolean"
,
"description"
:
"是否开启 redis 分布式限流."
,
"defaultValue"
:
"false"
}
]
}
mica-redis/src/main/resources/META-INF/scripts/mica_rate_limiter.lua
0 → 100644
浏览文件 @
cbaec03a
-- lua 下标从 1 开始
-- 限流 key
local
key
=
KEYS
[
1
]
-- 限流大小
local
max
=
tonumber
(
ARGV
[
1
])
-- 超时时间
local
ttl
=
tonumber
(
ARGV
[
2
])
-- 考虑主从策略和脚本回放机制,这个time由客户端获取传入
local
now
=
tonumber
(
ARGV
[
3
])
-- 已经过期的时间点
local
expired
=
now
-
ttl
-- 清除过期的数据,移除指定分数(score)区间内的所有成员
redis
.
call
(
'zremrangebyscore'
,
key
,
0
,
expired
)
-- 获取当前流量大小
local
currentLimit
=
tonumber
(
redis
.
call
(
'zcard'
,
key
))
local
nextLimit
=
currentLimit
+
1
if
nextLimit
>
max
then
-- 达到限流大小 返回 0
return
0
;
else
-- 没有达到阈值 value + 1
redis
.
call
(
"zadd"
,
key
,
now
,
now
)
-- 秒为单位设置 key 的生存时间
redis
.
call
(
"pexpire"
,
key
,
ttl
)
return
nextLimit
end
mica-redis/src/test/java/net/dreamlu/mica/RedisApplicationTests.java
0 → 100644
浏览文件 @
cbaec03a
package
net.dreamlu.mica
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.springframework.boot.autoconfigure.SpringBootApplication
;
import
org.springframework.boot.test.context.SpringBootTest
;
import
org.springframework.test.context.junit4.SpringRunner
;
/**
* redis 测试
*
* @author L.cm
*/
@RunWith
(
SpringRunner
.
class
)
@SpringBootTest
public
class
RedisApplicationTests
{
@SpringBootApplication
public
static
class
App
{
}
@Test
public
void
contextLoads
()
{
}
}
settings.gradle
浏览文件 @
cbaec03a
...
...
@@ -8,3 +8,4 @@ include "mica-swagger"
include
"mica-captcha"
include
"mica-jobs"
include
"mica-mongo"
include
"mica-redis"
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录