添加redis配置
This commit is contained in:
parent
b0616a1098
commit
c01841e742
@ -1,10 +1,13 @@
|
|||||||
package dev.surl.surl
|
package dev.surl.surl
|
||||||
|
|
||||||
|
import dev.surl.surl.cfg.BaseConfig
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||||
import org.springframework.boot.runApplication
|
import org.springframework.boot.runApplication
|
||||||
import org.springframework.context.ConfigurableApplicationContext
|
import org.springframework.context.ConfigurableApplicationContext
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
|
@EnableConfigurationProperties(BaseConfig::class)
|
||||||
open class SurlApplication {
|
open class SurlApplication {
|
||||||
companion object {
|
companion object {
|
||||||
lateinit var context: ConfigurableApplicationContext
|
lateinit var context: ConfigurableApplicationContext
|
||||||
|
@ -3,19 +3,13 @@ package dev.surl.surl.cfg
|
|||||||
import dev.surl.surl.util.numberToKey
|
import dev.surl.surl.util.numberToKey
|
||||||
import io.jsonwebtoken.security.Keys
|
import io.jsonwebtoken.security.Keys
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties
|
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||||
import org.springframework.stereotype.Component
|
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import javax.crypto.SecretKey
|
import javax.crypto.SecretKey
|
||||||
|
|
||||||
@Component
|
|
||||||
@ConfigurationProperties(prefix = "base.configs")
|
@ConfigurationProperties(prefix = "base.configs")
|
||||||
class BaseConfig(
|
class BaseConfig(
|
||||||
/**
|
val site: String = "https://surl.org",
|
||||||
* 站点域名
|
val expire: Long = 3600000, // token expire time
|
||||||
*/
|
private val secret: String = numberToKey(Date().time).repeat(5),
|
||||||
var site: String = "https://surl.org",
|
val secretKey: SecretKey = Keys.hmacShaKeyFor(secret.toByteArray())
|
||||||
var expire: Long = 24 * 60 * 60 * 1000, // token expire time
|
|
||||||
private var secret: String = numberToKey(Date().time),
|
|
||||||
var secretKey: SecretKey = Keys.hmacShaKeyFor(secret.toByteArray()),
|
|
||||||
var tokenHead: String = "token"
|
|
||||||
)
|
)
|
@ -8,4 +8,10 @@ import kotlin.reflect.KClass
|
|||||||
@Component
|
@Component
|
||||||
open class Logging {
|
open class Logging {
|
||||||
fun <T: Any> logger(clazz: KClass<T>): Logger = LoggerFactory.getLogger(clazz::class.java)
|
fun <T: Any> logger(clazz: KClass<T>): Logger = LoggerFactory.getLogger(clazz::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("UNUSED")
|
||||||
|
fun <T: Any> T.logger(): Logger = LoggerFactory.getLogger(this::class.java)
|
||||||
|
|
||||||
|
@Suppress("UNUSED")
|
||||||
|
fun logger(name: String): Logger = LoggerFactory.getLogger(name)
|
@ -3,6 +3,7 @@ package dev.surl.surl.cfg
|
|||||||
import org.springframework.context.annotation.Configuration
|
import org.springframework.context.annotation.Configuration
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
|
@Suppress("UNUSED")
|
||||||
open class PatternConfig {
|
open class PatternConfig {
|
||||||
val usernamePattern = Regex("""\w{6,20}""")
|
val usernamePattern = Regex("""\w{6,20}""")
|
||||||
val passwordPattern = Regex("""^((?=\S*?[A-Z])(?=\S*?[a-z])(?=\S*?[0-9])(?=\S*?)).{10,}\S$""")
|
val passwordPattern = Regex("""^((?=\S*?[A-Z])(?=\S*?[a-z])(?=\S*?[0-9])(?=\S*?)).{10,}\S$""")
|
||||||
|
15
src/main/java/dev/surl/surl/cfg/RedisConfig.kt
Normal file
15
src/main/java/dev/surl/surl/cfg/RedisConfig.kt
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package dev.surl.surl.cfg
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.data.redis.connection.RedisConnectionFactory
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class RedisConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun baseRedis(factory: RedisConnectionFactory): StringRedisTemplate {
|
||||||
|
return StringRedisTemplate(factory)
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +0,0 @@
|
|||||||
package dev.surl.surl.controller
|
|
||||||
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping
|
|
||||||
import org.springframework.web.bind.annotation.RestController
|
|
||||||
|
|
||||||
@RestController(value = "/")
|
|
||||||
class DefaultFcontroller {
|
|
||||||
@RequestMapping
|
|
||||||
fun defaultMessage() = 123
|
|
||||||
}
|
|
@ -1,10 +1,11 @@
|
|||||||
package dev.surl.surl.controller
|
package dev.surl.surl.controller
|
||||||
|
|
||||||
import dev.surl.surl.cfg.BaseConfig
|
import dev.surl.surl.common.Msg
|
||||||
import dev.surl.surl.service.SurlService
|
import dev.surl.surl.service.SurlService
|
||||||
import jakarta.validation.Valid
|
import jakarta.validation.Valid
|
||||||
|
import jakarta.validation.constraints.Pattern
|
||||||
import org.hibernate.validator.constraints.Length
|
import org.hibernate.validator.constraints.Length
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
import org.springframework.http.HttpStatus
|
||||||
import org.springframework.http.ResponseEntity
|
import org.springframework.http.ResponseEntity
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
import org.springframework.web.bind.annotation.PathVariable
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
@ -12,16 +13,18 @@ import org.springframework.web.bind.annotation.RestController
|
|||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
class RedirectController {
|
class RedirectController(private val service: SurlService) {
|
||||||
@GetMapping("/{key}")
|
@GetMapping("/{key}")
|
||||||
fun redirect(
|
fun redirect(
|
||||||
@PathVariable @Valid @Length(min = 1, max = 11, message = "Key length is not valid") key: String,
|
@PathVariable
|
||||||
@Autowired service: SurlService,
|
@Valid
|
||||||
@Autowired cfg: BaseConfig
|
@Length(min = 1, max = 11, message = "Key length is not valid")
|
||||||
|
@Pattern(regexp = "[\\w!*().\\-_~]+", message = "Key format is not valid")
|
||||||
|
key: String
|
||||||
): ResponseEntity<Any> {
|
): ResponseEntity<Any> {
|
||||||
val redirectUrl = service.getUrlByKey(key)
|
val redirectUrl = service.getUrlByKey(key)
|
||||||
return if(redirectUrl.isBlank()) {
|
return if(redirectUrl.isBlank()) {
|
||||||
ResponseEntity.status(302).location(URI.create(cfg.site)).build()
|
ResponseEntity(Msg<String>(code = -1, msg = "key `$key` not found"), HttpStatus.NOT_FOUND)
|
||||||
} else {
|
} else {
|
||||||
ResponseEntity.status(302).location(URI.create(redirectUrl)).build()
|
ResponseEntity.status(302).location(URI.create(redirectUrl)).build()
|
||||||
}
|
}
|
||||||
|
@ -10,14 +10,14 @@ import org.springframework.http.HttpStatus
|
|||||||
import org.springframework.http.ResponseEntity
|
import org.springframework.http.ResponseEntity
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping
|
||||||
import org.springframework.web.bind.annotation.RequestBody
|
import org.springframework.web.bind.annotation.RequestBody
|
||||||
import org.springframework.web.bind.annotation.RequestMapping
|
|
||||||
import org.springframework.web.bind.annotation.RestController
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
class SurlAddController {
|
class SurlAddController {
|
||||||
@RequestMapping("/surl/add")
|
@PostMapping("/surl/add")
|
||||||
fun addSurl(
|
fun addSurl(
|
||||||
@Valid @RequestBody body: SurlDto, @Autowired service: SurlService, @Autowired cfg: BaseConfig
|
@Valid @RequestBody body: SurlDto, @Autowired service: SurlService, @Autowired cfg: BaseConfig
|
||||||
): Any {
|
): Any {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package dev.surl.surl.controller
|
package dev.surl.surl.controller
|
||||||
|
|
||||||
import dev.surl.surl.common.Msg
|
|
||||||
import dev.surl.surl.dto.UserDto
|
import dev.surl.surl.dto.UserDto
|
||||||
import dev.surl.surl.service.UserService
|
import dev.surl.surl.service.UserService
|
||||||
import jakarta.validation.Valid
|
import jakarta.validation.Valid
|
||||||
|
@ -5,6 +5,7 @@ import org.jetbrains.exposed.dao.Entity
|
|||||||
import org.jetbrains.exposed.dao.EntityClass
|
import org.jetbrains.exposed.dao.EntityClass
|
||||||
import org.jetbrains.exposed.dao.id.EntityID
|
import org.jetbrains.exposed.dao.id.EntityID
|
||||||
|
|
||||||
|
@Suppress("UNUSED")
|
||||||
class Surl(id: EntityID<Long>): Entity<Long>(id) {
|
class Surl(id: EntityID<Long>): Entity<Long>(id) {
|
||||||
companion object: EntityClass<Long, Surl>(Surls)
|
companion object: EntityClass<Long, Surl>(Surls)
|
||||||
var url by Surls.url
|
var url by Surls.url
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package dev.surl.surl.dao
|
package dev.surl.surl.dao
|
||||||
|
|
||||||
import dev.surl.surl.common.Access
|
import dev.surl.surl.common.Access
|
||||||
import dev.surl.surl.dao.Surl.Companion.referrersOn
|
|
||||||
import dev.surl.surl.dsl.UserAccesses
|
import dev.surl.surl.dsl.UserAccesses
|
||||||
import org.jetbrains.exposed.dao.LongEntity
|
import org.jetbrains.exposed.dao.LongEntity
|
||||||
import org.jetbrains.exposed.dao.LongEntityClass
|
import org.jetbrains.exposed.dao.LongEntityClass
|
||||||
|
@ -12,6 +12,7 @@ import org.springframework.web.bind.MethodArgumentNotValidException
|
|||||||
import org.springframework.web.bind.annotation.ControllerAdvice
|
import org.springframework.web.bind.annotation.ControllerAdvice
|
||||||
import org.springframework.web.bind.annotation.ExceptionHandler
|
import org.springframework.web.bind.annotation.ExceptionHandler
|
||||||
import org.springframework.web.context.request.WebRequest
|
import org.springframework.web.context.request.WebRequest
|
||||||
|
import org.springframework.web.method.annotation.HandlerMethodValidationException
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler
|
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler
|
||||||
|
|
||||||
@ControllerAdvice
|
@ControllerAdvice
|
||||||
@ -22,6 +23,20 @@ class DefaultExceptionHandler : ResponseEntityExceptionHandler() {
|
|||||||
return ResponseEntity(Msg<String>(code = -1, msg = ex.allValidationResults.joinToString(";")), status)
|
return ResponseEntity(Msg<String>(code = -1, msg = ex.allValidationResults.joinToString(";")), status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun handleHandlerMethodValidationException(
|
||||||
|
ex: HandlerMethodValidationException,
|
||||||
|
headers: HttpHeaders,
|
||||||
|
status: HttpStatusCode,
|
||||||
|
request: WebRequest
|
||||||
|
): ResponseEntity<Any> {
|
||||||
|
return ResponseEntity(Msg<String>(code = -1, msg = ex.allValidationResults.joinToString {
|
||||||
|
it.resolvableErrors.joinToString(";") {
|
||||||
|
err ->
|
||||||
|
err.defaultMessage ?: "unknown validation error"
|
||||||
|
}
|
||||||
|
}), status)
|
||||||
|
}
|
||||||
|
|
||||||
override fun handleMethodArgumentNotValid(
|
override fun handleMethodArgumentNotValid(
|
||||||
ex: MethodArgumentNotValidException, headers: HttpHeaders, status: HttpStatusCode, request: WebRequest
|
ex: MethodArgumentNotValidException, headers: HttpHeaders, status: HttpStatusCode, request: WebRequest
|
||||||
): ResponseEntity<Any> {
|
): ResponseEntity<Any> {
|
||||||
|
@ -4,6 +4,7 @@ import dev.surl.surl.dao.Surl
|
|||||||
import dev.surl.surl.dsl.Surls
|
import dev.surl.surl.dsl.Surls
|
||||||
import dev.surl.surl.util.*
|
import dev.surl.surl.util.*
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.jetbrains.exposed.sql.batchInsert
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
@ -19,6 +20,13 @@ class SurlService {
|
|||||||
numberToKey(id)
|
numberToKey(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun batchAddSurl(baseurls: List<String>) = transaction {
|
||||||
|
Surls.batchInsert(baseurls, shouldReturnGeneratedValues = false) {
|
||||||
|
this[Surls.url] = it
|
||||||
|
this[Surls.id] = runBlocking { genSnowflakeUID() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun getUrlByKey(key: String): String {
|
fun getUrlByKey(key: String): String {
|
||||||
return transaction {
|
return transaction {
|
||||||
Surls.select(Surls.url).where {
|
Surls.select(Surls.url).where {
|
||||||
|
@ -1,16 +1,28 @@
|
|||||||
package dev.surl.surl.util.redis
|
package dev.surl.surl.util.redis
|
||||||
|
|
||||||
|
import dev.surl.surl.cfg.BaseConfig
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate
|
import org.springframework.data.redis.core.StringRedisTemplate
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
@Suppress("UNUSED")
|
||||||
class RedisUtil {
|
@Component
|
||||||
private val template = StringRedisTemplate()
|
class RedisUtil(private val template: StringRedisTemplate, private val cfg: BaseConfig) {
|
||||||
fun get(key: String): String? {
|
private val ops = template.opsForValue()
|
||||||
return template.opsForValue().get(key)
|
fun getString(key: String): String? {
|
||||||
|
return ops.get(key)
|
||||||
|
}
|
||||||
|
fun setString(key: String, value: String, expire: Long = cfg.expire, unit: TimeUnit = TimeUnit.MILLISECONDS) {
|
||||||
|
ops.set(key, value, expire, unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun batchSet(map: Map<String, String>) {
|
fun delKey(key: String) {
|
||||||
template.executePipelined {
|
ops.operations.delete(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun flushdb() {
|
||||||
|
template.execute {
|
||||||
|
it.serverCommands().flushDb()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,5 @@
|
|||||||
server:
|
server:
|
||||||
port: 18888
|
port: 18888
|
||||||
servlet:
|
|
||||||
context-path: /api/v1
|
|
||||||
spring:
|
spring:
|
||||||
profiles:
|
profiles:
|
||||||
active: default
|
active: default
|
||||||
@ -26,13 +24,25 @@ spring:
|
|||||||
time-zone: Asia/Shanghai
|
time-zone: Asia/Shanghai
|
||||||
serialization:
|
serialization:
|
||||||
indent-output: true
|
indent-output: true
|
||||||
|
data:
|
||||||
|
redis:
|
||||||
|
host: localhost
|
||||||
|
database: 0
|
||||||
|
jedis:
|
||||||
|
pool:
|
||||||
|
enabled: true
|
||||||
|
max-active: 8
|
||||||
|
max-idle: 8
|
||||||
|
min-idle: 1
|
||||||
|
max-wait: 100ms
|
||||||
exposed:
|
exposed:
|
||||||
show-sql: false
|
show-sql: false
|
||||||
generate-ddl: true
|
generate-ddl: true
|
||||||
logging:
|
logging:
|
||||||
level:
|
level:
|
||||||
root: info
|
root: info
|
||||||
Exposed: debug
|
Exposed: info
|
||||||
base:
|
base:
|
||||||
configs:
|
configs:
|
||||||
expire: 1000
|
site: http://127.0.0.1:18888
|
||||||
|
expire: 3600000
|
@ -23,6 +23,19 @@ class Benchmark {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val timeEllapsed = System.currentTimeMillis() - now
|
val timeEllapsed = System.currentTimeMillis() - now
|
||||||
println("TPS: ${128 * 100.0 * 1000 / timeEllapsed}")
|
println("one by one TPS: ${128 * 100.0 * 1000 / timeEllapsed}")
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
fun `test batch insert`(@Autowired service: SurlService) {
|
||||||
|
runBlocking {
|
||||||
|
val urls = mutableListOf<String>()
|
||||||
|
for(i in 1..10000) {
|
||||||
|
urls.add("https://surl.org/${genSnowflakeUID()}")
|
||||||
|
}
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
service.batchAddSurl(urls)
|
||||||
|
val timeEllapsed = System.currentTimeMillis() - now
|
||||||
|
println("batch TPS: ${10000 * 1000 / timeEllapsed}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
22
src/test/java/dev/surl/surl/RedisTest.kt
Normal file
22
src/test/java/dev/surl/surl/RedisTest.kt
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package dev.surl.surl
|
||||||
|
|
||||||
|
import dev.surl.surl.util.numberToKey
|
||||||
|
import dev.surl.surl.util.redis.RedisUtil
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfiguration
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
@AutoConfiguration
|
||||||
|
open class RedisTest(@Autowired private val redisUtil: RedisUtil) {
|
||||||
|
@Test
|
||||||
|
fun `test set value`() {
|
||||||
|
redisUtil.setString("test", numberToKey(1145141919810L))
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
fun `test get value`() {
|
||||||
|
val value = redisUtil.getString("test")
|
||||||
|
println(value)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user