Skip to content

Commit

Permalink
feat: 支持websocket #2494
Browse files Browse the repository at this point in the history
  • Loading branch information
yaoxuwan committed Aug 27, 2024
1 parent 57d9224 commit 46401d5
Show file tree
Hide file tree
Showing 31 changed files with 903 additions and 4 deletions.
1 change: 1 addition & 0 deletions src/backend/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ allprojects {
exclude(group = "log4j", module = "log4j")
exclude(group = "org.slf4j", module = "slf4j-log4j12")
exclude(group = "commons-logging", module = "commons-logging")
exclude(group = "org.springframework.boot", module = "spring-boot-starter-tomcat")
}

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,10 @@ class CustomMetricsPushAutoConfiguration {
fun customMetricsExporter(
drive: PrometheusDrive,
prometheusProperties: PrometheusProperties,
scheduler: ThreadPoolTaskScheduler,
taskScheduler: ThreadPoolTaskScheduler,
customPushConfig: CustomPushConfig,
): CustomMetricsExporter {
return CustomMetricsExporter(customPushConfig, CollectorRegistry(), drive, prometheusProperties, scheduler)
return CustomMetricsExporter(customPushConfig, CollectorRegistry(), drive, prometheusProperties, taskScheduler)
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ class RedisOperation(private val redisTemplate: RedisTemplate<String, String>) {
}
}

fun delete(key: String) {
redisTemplate.delete(key)
fun delete(key: String): Boolean {
return redisTemplate.delete(key)
}

fun delete(keys: Collection<String>) {
Expand Down
1 change: 1 addition & 0 deletions src/backend/settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,4 @@ includeAll(":s3")
includeAll(":router-controller")
includeAll(":media")
includeAll(":common:common-metadata")
includeAll(":websocket")
40 changes: 40 additions & 0 deletions src/backend/websocket/biz-websocket/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available.
*
* Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved.
*
* BK-CI 蓝鲸持续集成平台 is licensed under the MIT license.
*
* A copy of the MIT License is included in this file.
*
*
* Terms of the MIT License:
* ---------------------------------------------------
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED " AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

dependencies {
api(project(":common:common-stream"))
api(project(":common:common-service"))
api(project(":common:common-artifact:artifact-service"))
implementation("org.springframework.boot:spring-boot-starter-websocket")
implementation("javax.websocket:javax.websocket-api")
implementation("io.undertow:undertow-servlet")
implementation("io.undertow:undertow-websockets-jsr")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.tencent.bkrepo.websocket.config

import com.tencent.bkrepo.common.artifact.config.ArtifactConfigurerSupport
import com.tencent.bkrepo.common.artifact.pojo.RepositoryType
import com.tencent.bkrepo.common.artifact.repository.local.LocalRepository
import com.tencent.bkrepo.common.artifact.repository.remote.RemoteRepository
import com.tencent.bkrepo.common.artifact.repository.virtual.VirtualRepository
import com.tencent.bkrepo.common.security.http.core.HttpAuthSecurityCustomizer
import org.springframework.context.annotation.Configuration

@Configuration
class WebSocketConfigurer : ArtifactConfigurerSupport() {

override fun getRepositoryType() = RepositoryType.NONE
override fun getLocalRepository(): LocalRepository = object : LocalRepository() {}
override fun getRemoteRepository(): RemoteRepository = object : RemoteRepository() {}
override fun getVirtualRepository(): VirtualRepository = object : VirtualRepository() {}

override fun getAuthSecurityCustomizer() =
HttpAuthSecurityCustomizer { httpAuthSecurity -> httpAuthSecurity.withPrefix("/websocket") }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.tencent.bkrepo.websocket.config

import org.springframework.boot.context.properties.ConfigurationProperties

@ConfigurationProperties("websocket")
data class WebSocketProperties(
var cacheLimit: Int = 3600,
var minThread: Int = 8,
var transfer: Boolean = false
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.tencent.bkrepo.websocket.config

import com.tencent.bkrepo.common.redis.RedisOperation
import com.tencent.bkrepo.common.security.http.jwt.JwtAuthProperties
import com.tencent.bkrepo.common.security.manager.AuthenticationManager
import com.tencent.bkrepo.websocket.constant.APP_ENDPOINT
import com.tencent.bkrepo.websocket.constant.USER_ENDPOINT
import com.tencent.bkrepo.websocket.dispatch.push.TransferPush
import com.tencent.bkrepo.websocket.handler.SessionWebSocketHandlerDecoratorFactory
import com.tencent.bkrepo.websocket.listener.TransferPushListener
import com.tencent.bkrepo.websocket.service.WebsocketService
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.messaging.Message
import org.springframework.messaging.simp.config.ChannelRegistration
import org.springframework.messaging.simp.config.MessageBrokerRegistry
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker
import org.springframework.web.socket.config.annotation.StompEndpointRegistry
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer
import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration
import java.util.function.Consumer

@Configuration
@EnableWebSocketMessageBroker
@EnableConfigurationProperties(WebSocketProperties::class)
class WebsocketConfiguration(
private val webSocketProperties: WebSocketProperties,
private val websocketService: WebsocketService,
private val redisOperation: RedisOperation,
private val jwtAuthProperties: JwtAuthProperties,
private val authenticationManager: AuthenticationManager,
) : WebSocketMessageBrokerConfigurer {

override fun configureMessageBroker(config: MessageBrokerRegistry) {
config.setCacheLimit(webSocketProperties.cacheLimit)
config.enableSimpleBroker("/topic")
config.setApplicationDestinationPrefixes("/app")
}

override fun registerStompEndpoints(registry: StompEndpointRegistry) {
registry.addEndpoint(USER_ENDPOINT, APP_ENDPOINT)
.setAllowedOriginPatterns("*")
registry.addEndpoint(USER_ENDPOINT, APP_ENDPOINT)
.setAllowedOriginPatterns("*")
.withSockJS()
}

@Override
override fun configureClientInboundChannel(registration: ChannelRegistration) {
var defaultCorePoolSize = webSocketProperties.minThread
if (defaultCorePoolSize < Runtime.getRuntime().availableProcessors() * 2) {
defaultCorePoolSize = Runtime.getRuntime().availableProcessors() * 2
}
registration.taskExecutor().corePoolSize(defaultCorePoolSize)
.maxPoolSize(defaultCorePoolSize * 2)
.keepAliveSeconds(60)
}

@Override
override fun configureClientOutboundChannel(registration: ChannelRegistration) {
var defaultCorePoolSize = webSocketProperties.minThread
if (defaultCorePoolSize < Runtime.getRuntime().availableProcessors() * 2) {
defaultCorePoolSize = Runtime.getRuntime().availableProcessors() * 2
}
registration.taskExecutor().corePoolSize(defaultCorePoolSize).maxPoolSize(defaultCorePoolSize * 2)
}

override fun configureWebSocketTransport(registration: WebSocketTransportRegistration) {
registration.addDecoratorFactory(wsHandlerDecoratorFactory())
super.configureWebSocketTransport(registration)
}

@Bean
fun wsHandlerDecoratorFactory(): SessionWebSocketHandlerDecoratorFactory {
return SessionWebSocketHandlerDecoratorFactory(
websocketService = websocketService,
redisOperation = redisOperation,
authenticationManager = authenticationManager,
jwtAuthProperties = jwtAuthProperties
)
}

@Bean
fun websocketTransferConsumer(transferPushListener: TransferPushListener): Consumer<Message<TransferPush>> {
return Consumer { transferPushListener.accept(it) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.tencent.bkrepo.websocket.config

import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration
import org.springframework.boot.task.TaskExecutorBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Lazy
import org.springframework.context.annotation.Primary
import org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor

/**
* Websocket会注册自定义的ThreadPoolTaskExecutor
*
* [org.springframework.messaging.simp.config.AbstractMessageBrokerConfiguration.clientInboundChannelExecutor]
*
* [org.springframework.messaging.simp.config.AbstractMessageBrokerConfiguration.clientOutboundChannelExecutor]
*
* [org.springframework.messaging.simp.config.AbstractMessageBrokerConfiguration.brokerChannelExecutor]
*
* 导致默认的ThreadPoolTaskExecutor不会实例化
*
* [org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration.applicationTaskExecutor]
*
*/
@Configuration
class WsThreadPoolTaskExeccutorConfiguration {

@Lazy
@Bean(name = [
TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME,
AsyncAnnotationBeanPostProcessor.DEFAULT_TASK_EXECUTOR_BEAN_NAME
])
@Primary
fun applicationTaskExecutor(builder: TaskExecutorBuilder): ThreadPoolTaskExecutor {
return builder.build()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* BK-CI 蓝鲸持续集成平台 is licensed under the MIT license.
*
* A copy of the MIT License is included in this file.
*
*
* Terms of the MIT License:
* ---------------------------------------------------
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
* the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

package com.tencent.bkrepo.websocket.constant

const val USER_ENDPOINT = "/ws/user"
const val APP_ENDPOINT = "/ws/app"

const val SESSION_ID = "sessionId"
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.tencent.bkrepo.websocket.controller

import com.tencent.bkrepo.websocket.pojo.fs.CopyPDU
import com.tencent.bkrepo.websocket.pojo.fs.PastePDU
import com.tencent.bkrepo.websocket.service.ClipboardService
import org.springframework.messaging.handler.annotation.MessageMapping
import org.springframework.stereotype.Controller

@Controller
@MessageMapping("/clipboard")
class ClipboardController(
private val clipboardService: ClipboardService
) {

@MessageMapping("/copy")
fun copy(copyPDU: CopyPDU) {
clipboardService.copy(copyPDU)
}

@MessageMapping("/paste")
fun paste(pastePDU: PastePDU) {
clipboardService.paste(pastePDU)
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* BK-CI 蓝鲸持续集成平台 is licensed under the MIT license.
*
* A copy of the MIT License is included in this file.
*
*
* Terms of the MIT License:
* ---------------------------------------------------
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
* the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

package com.tencent.bkrepo.websocket.dispatch

/**
* 下发接口
*/
interface Dispatcher<T> {

fun dispatch(data: T)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.tencent.bkrepo.websocket.dispatch

import com.tencent.bkrepo.common.api.util.toJsonString
import com.tencent.bkrepo.common.stream.event.supplier.MessageSupplier
import com.tencent.bkrepo.websocket.config.WebSocketProperties
import com.tencent.bkrepo.websocket.dispatch.push.TransferPush
import org.springframework.messaging.simp.SimpMessagingTemplate
import org.springframework.stereotype.Component

@Component
class TransferDispatch(
private val messageSupplier: MessageSupplier,
private val simpMessagingTemplate: SimpMessagingTemplate,
private val webSocketProperties: WebSocketProperties
) : Dispatcher<TransferPush> {
override fun dispatch(data: TransferPush) {
if (webSocketProperties.transfer) {
messageSupplier.delegateToSupplier(data, topic = TOPIC)
} else {
simpMessagingTemplate.convertAndSend(data.topic, data.data.toJsonString())
}
}

companion object {
private const val TOPIC = "websocket-transfer-out-0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.tencent.bkrepo.websocket.dispatch.push

import com.tencent.bkrepo.websocket.pojo.fs.CopyPDU

class CopyPDUTransferPush(
copyPDU: CopyPDU,
) : TransferPush(
topic = "/topic/clipboard/copy/${copyPDU.workspaceName}",
data = copyPDU
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.tencent.bkrepo.websocket.dispatch.push

import com.tencent.bkrepo.websocket.pojo.fs.PastePDU

class PastePDUTransferPush(
pastePDU: PastePDU,
) : TransferPush(
topic = "/topic/clipboard/paste/${pastePDU.workspaceName}",
data = pastePDU
)
Loading

0 comments on commit 46401d5

Please sign in to comment.