具有请求队列的Kotlin服务
我想使用以下API设计一个服务:具有请求队列的Kotlin服务,kotlin,architecture,future,deferred,kotlin-coroutines,Kotlin,Architecture,Future,Deferred,Kotlin Coroutines,我想使用以下API设计一个服务: suspend fun getUsers(请求:请求):列表 在引擎盖下,我会向服务器发送请求(不管如何,但可以说这是一个反应式WebClient),但这里有一个技巧:我只能每隔500毫秒发送一次请求,否则我会出错 有人能推荐我如何实现它吗?当我从一个挂起的协程调用getUsers时,工作单元被添加到具有此方法的服务的某个队列中,然后在某个时间点实现并返回结果 我假设我可以使用一些ReceiveChannel作为一个队列,为它的元素设置一个for循环,里面有一个
suspend fun getUsers(请求:请求):列表
在引擎盖下,我会向服务器发送请求(不管如何,但可以说这是一个反应式WebClient
),但这里有一个技巧:我只能每隔500毫秒发送一次请求,否则我会出错
有人能推荐我如何实现它吗?当我从一个挂起的协程调用getUsers
时,工作单元被添加到具有此方法的服务的某个队列中,然后在某个时间点实现并返回结果
我假设我可以使用一些ReceiveChannel
作为一个队列,为它的元素设置一个for
循环,里面有一个delay
,但是我有点不知道该把这个逻辑放在哪里。这是否应该像后台方法一样永远运行并被getUsers
调用?可能永远不会调用close
方法,因此此方法也可以挂起,但是如何将值从这个无限运行的方法传递回需要结果的getUsers
编辑
目前,我正在考虑这样的解决方案:
private const val REQUEST_INTERVAL = 500
@Service
class DelayedRequestSenderImpl<T> : DelayedRequestSender<T> {
private var lastRequestTime: LocalDateTime = LocalDateTime.now()
private val requestChannel: Channel<Deferred<T>> = Channel()
override suspend fun requestAsync(block: () -> T): Deferred<T> {
val deferred = GlobalScope.async(start = CoroutineStart.LAZY) { block() }
requestChannel.send(deferred)
return deferred
}
@PostConstruct
private fun startRequestProcessing() = GlobalScope.launch {
for (request in requestChannel) {
val now = LocalDateTime.now()
val diff = ChronoUnit.MILLIS.between(lastRequestTime, now)
if (diff < REQUEST_INTERVAL) {
delay(REQUEST_INTERVAL - diff)
lastRequestTime = now
}
request.start()
}
}
}
我建议:
- 将泛型向下推到函数级别
- 使用参与者而不是协同程序实现(但您可能更喜欢这样)李>
private const val REQUEST_INTERVAL=500
接口DelayedRequestHandler{
暂停具有延迟的有趣手柄(块:()->T):T
}
类DelayedRequestHandlerImpl(requestInterval:Int=REQUEST\u INTERVAL):DelayedRequestHandler,CoroutineScope{
private val job=job()
override val coroutineContext=调度程序。未定义+作业
private val delayedHandlerator=delayedRequestHandlerator(请求间隔)
用延迟覆盖暂停操作手柄(块:()->T):T{
val result=CompletableDeferred()
delayedHandlerator.send(DelayedHandlerMsg(结果,块))
返回结果。等待()
}
}
私有数据类DelayedHandlerMsg(val结果:CompletableDeferred,val块:()->result)
private fun CoroutineScope.delayedRequestHandlerator(requestInterval:Int)=actor(){
var lastRequestTime:LocalDateTime=LocalDateTime.now()
用于(通道中的消息){
试一试{
println(“获得消息处理”)
val now=LocalDateTime.now()
val diff=时间间隔(lastRequestTime,现在)
if(差值<请求间隔){
延迟(请求间隔-差异)
}
lastRequestTime=LocalDateTime.now()
@抑制(“未选中的_CAST”)
val msgCast=作为DelayedHandlerMsg的消息
val结果=msgCast.block()
println(结果)
msgCast.result.complete(结果)
}捕获(e:例外){
message.result.complete异常(e)
}
}
}
fun main()=运行阻塞{
val mydelayHandler=DelayedRequestHandlerImpl(2000)
val作业=列表(10){
发射{
mydelayHandler.handleWithDelay{
“结果$it”
}
}
}
jobs.forEach{it.join()}
}
所以这是我提出的最后一个实现。请注意SupevisorJob
,因为我们不希望在其中一个请求失败时停止处理,这是完全可能的,也很好(至少在我的情况下)
另外,@Laurence建议的选项可能更好,但我决定暂时不使用actors,因为API被标记为过时
@Service
class DelayingRequestSenderImpl(@Value("\${vk.request.interval}") private val interval: Int) : DelayingRequestSender {
private var lastRequestTime: LocalDateTime = LocalDateTime.now()
private val requestChannel: Channel<Deferred<*>> = Channel()
//SupervisorJob is used because we want to have continuous processing of requestChannel
//even if one of the requests fails
private val coroutineScope = CoroutineScope(SupervisorJob())
override suspend fun <T> request(block: () -> T): T {
val deferred = coroutineScope.async(start = CoroutineStart.LAZY) { block() }
requestChannel.send(deferred)
return deferred.await()
}
@PostConstruct
fun startProcessing() = coroutineScope.launch {
for (request in requestChannel) {
val now = LocalDateTime.now()
val diff = ChronoUnit.MILLIS.between(lastRequestTime, now)
if (diff < interval) {
delay(interval - diff)
}
lastRequestTime = LocalDateTime.now()
request.start()
}
}
}
@服务
类DelayingRequestSenderImpl(@Value(\${vk.request.interval})私有val interval:Int):DelayingRequestSender{
私有变量lastRequestTime:LocalDateTime=LocalDateTime.now()
私有val请求通道:通道=通道()
//之所以使用SupervisorJob,是因为我们希望对requestChannel进行连续处理
//即使其中一个请求失败
private val coroutineScope=coroutineScope(SupervisorJob())
覆盖挂起乐趣请求(块:()->T):T{
val deferred=coroutineScope.async(start=CoroutineStart.LAZY){block()}
requestChannel.send(延迟)
返回延迟。等待()
}
@施工后
fun startProcessing()=coroutineScope.launch{
for(请求通道中的请求){
val now=LocalDateTime.now()
val diff=时间间隔(lastRequestTime,现在)
if(差值<间隔){
延迟(间隔-差异)
}
lastRequestTime=LocalDateTime.now()
request.start()
}
}
}
无法帮助您完成合作计划,但我想到了一些想法。是否允许每隔500毫秒从客户端调用服务器?或者从您的请求发件人的每个实例?让一个不使用泛型而使用原始字符串的单例请求发送者(如果第一种情况是这样的话)怎么样。。而不是使用另一种更高级别的服务,将数据解析为对象,这些对象可能是多个实例……感谢您的建议!但是,actor
标记为@ObsoleteCoroutinesApi
,因此我认为最好尽量避免使用it@LeonidBor是的,但是这里解释了它的语义,现在没有其他选择,但是它们将在将来出现,并且可以进行迁移。它们仍然是通过通道通信封装线程安全状态的正确方法。你能帮我吗
private const val REQUEST_INTERVAL = 500
interface DelayedRequestHandler {
suspend fun <T> handleWithDelay(block: () -> T): T
}
class DelayedRequestHandlerImpl(requestInterval: Int = REQUEST_INTERVAL) : DelayedRequestHandler, CoroutineScope {
private val job = Job()
override val coroutineContext = Dispatchers.Unconfined + job
private val delayedHandlerActor = delayedRequestHandlerActor(requestInterval)
override suspend fun <T> handleWithDelay(block: () -> T): T {
val result = CompletableDeferred<T>()
delayedHandlerActor.send(DelayedHandlerMsg(result, block))
return result.await()
}
}
private data class DelayedHandlerMsg<RESULT>(val result: CompletableDeferred<RESULT>, val block: () -> RESULT)
private fun CoroutineScope.delayedRequestHandlerActor(requestInterval: Int) = actor<DelayedHandlerMsg<*>>() {
var lastRequestTime: LocalDateTime = LocalDateTime.now()
for (message in channel) {
try {
println("got a message processing")
val now = LocalDateTime.now()
val diff = ChronoUnit.MILLIS.between(lastRequestTime, now)
if (diff < requestInterval) {
delay(requestInterval - diff)
}
lastRequestTime = LocalDateTime.now()
@Suppress("UNCHECKED_CAST")
val msgCast = message as DelayedHandlerMsg<Any?>
val result = msgCast.block()
println(result)
msgCast.result.complete(result)
} catch (e: Exception) {
message.result.completeExceptionally(e)
}
}
}
fun main() = runBlocking {
val mydelayHandler = DelayedRequestHandlerImpl(2000)
val jobs = List(10) {
launch {
mydelayHandler.handleWithDelay {
"Result $it"
}
}
}
jobs.forEach { it.join() }
}
@Service
class DelayingRequestSenderImpl(@Value("\${vk.request.interval}") private val interval: Int) : DelayingRequestSender {
private var lastRequestTime: LocalDateTime = LocalDateTime.now()
private val requestChannel: Channel<Deferred<*>> = Channel()
//SupervisorJob is used because we want to have continuous processing of requestChannel
//even if one of the requests fails
private val coroutineScope = CoroutineScope(SupervisorJob())
override suspend fun <T> request(block: () -> T): T {
val deferred = coroutineScope.async(start = CoroutineStart.LAZY) { block() }
requestChannel.send(deferred)
return deferred.await()
}
@PostConstruct
fun startProcessing() = coroutineScope.launch {
for (request in requestChannel) {
val now = LocalDateTime.now()
val diff = ChronoUnit.MILLIS.between(lastRequestTime, now)
if (diff < interval) {
delay(interval - diff)
}
lastRequestTime = LocalDateTime.now()
request.start()
}
}
}