Skip to content

Commit

Permalink
support sofa thread pool actuator (#1301)
Browse files Browse the repository at this point in the history
1. update sofa-common-tools 2.1.1
2. support sofa thread pool actuator

---------

Co-authored-by: 致节 <hzj266771@antgroup.com>
  • Loading branch information
HzjNeverStop and 致节 authored Mar 22, 2024
1 parent baa0f04 commit 0fbe6e7
Show file tree
Hide file tree
Showing 8 changed files with 334 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 com.alipay.sofa.boot.actuator.autoconfigure.threadpool;

import com.alipay.sofa.boot.actuator.threadpool.ThreadPoolEndpoint;
import com.alipay.sofa.common.thread.ThreadPoolGovernor;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;

/**
* {@link EnableAutoConfiguration Auto-configuration} for {@link ThreadPoolEndpoint}.
*
* @author huzijie
* @version ThreadPoolEndpointAutoConfiguration.java, v 0.1 2024年03月22日 11:58 huzijie Exp $
*/
@AutoConfiguration
@ConditionalOnAvailableEndpoint(endpoint = ThreadPoolEndpoint.class)
public class ThreadPoolEndpointAutoConfiguration {

@Bean
@ConditionalOnMissingBean
public ThreadPoolEndpoint threadPoolEndpoint() {
return new ThreadPoolEndpoint(ThreadPoolGovernor.getInstance());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ com.alipay.sofa.boot.actuator.autoconfigure.health.ReadinessRuntimeAutoConfigura
com.alipay.sofa.boot.actuator.autoconfigure.health.ManualReadinessCallbackEndpointAutoConfiguration
com.alipay.sofa.boot.actuator.autoconfigure.startup.StartupEndPointAutoConfiguration
com.alipay.sofa.boot.actuator.autoconfigure.rpc.RpcActuatorAutoConfiguration
com.alipay.sofa.boot.actuator.autoconfigure.isle.IsleEndpointAutoConfiguration
com.alipay.sofa.boot.actuator.autoconfigure.isle.IsleEndpointAutoConfiguration
com.alipay.sofa.boot.actuator.autoconfigure.threadpool.ThreadPoolEndpointAutoConfiguration
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 com.alipay.sofa.boot.actuator.autoconfigure.threadpool;

import com.alipay.sofa.boot.actuator.threadpool.ThreadPoolEndpoint;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;

import static org.assertj.core.api.Assertions.assertThat;

/**
* Tests for {@link ThreadPoolEndpointAutoConfiguration}.
*
* @author huzijie
* @version ThreadPoolEndpointAutoConfigurationTests.java, v 0.1 2024年03月22日 11:59 huzijie Exp $
*/
public class ThreadPoolEndpointAutoConfigurationTests {

private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations
.of(ThreadPoolEndpointAutoConfiguration.class));

@Test
void runShouldHaveEndpointBean() {
this.contextRunner.withPropertyValues("management.endpoints.web.exposure.include=threadpool")
.run((context) -> assertThat(context).hasSingleBean(ThreadPoolEndpoint.class));
}

@Test
void runWhenNotExposedShouldNotHaveEndpointBean() {
this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(ThreadPoolEndpoint.class));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 com.alipay.sofa.boot.actuator.threadpool;

import com.alipay.sofa.common.thread.ThreadPoolConfig;
import com.alipay.sofa.common.thread.ThreadPoolGovernor;
import com.alipay.sofa.common.thread.ThreadPoolMonitorWrapper;
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;

import java.util.Collection;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;

/**
* {@link Endpoint @Endpoint} to expose details of sofa thread pools registered in {@link ThreadPoolGovernor}.
*
* @author huzijie
* @version ThreadPoolEndpoint.java, v 0.1 2024年03月22日 11:41 huzijie Exp $
*/
@Endpoint(id = "threadpool")
public class ThreadPoolEndpoint {

private final ThreadPoolGovernor threadPoolGovernor;

public ThreadPoolEndpoint(ThreadPoolGovernor threadPoolGovernor) {
this.threadPoolGovernor = threadPoolGovernor;
}

@ReadOperation
public ThreadPoolsDescriptor threadPools () {
Collection<ThreadPoolMonitorWrapper> threadPoolWrappers = threadPoolGovernor.getAllThreadPoolWrappers();

List<ThreadPoolInfo> threadPoolInfoList = threadPoolWrappers.stream().map(this::convertToThreadPoolInfo).toList();
return new ThreadPoolsDescriptor(threadPoolInfoList);
}

private ThreadPoolInfo convertToThreadPoolInfo(ThreadPoolMonitorWrapper wrapper) {
ThreadPoolConfig threadPoolConfig = wrapper.getThreadPoolConfig();
String threadPoolName = threadPoolConfig.getThreadPoolName();
String spaceName = threadPoolConfig.getSpaceName();
long period = threadPoolConfig.getPeriod();
long taskTimeout = threadPoolConfig.getTaskTimeoutMilli();

ThreadPoolExecutor threadPoolExecutor = wrapper.getThreadPoolExecutor();
String threadPoolClassName = threadPoolExecutor.getClass().getName();
int coreSize = threadPoolExecutor.getCorePoolSize();
int maxSize = threadPoolExecutor.getMaximumPoolSize();
int queueSize = threadPoolExecutor.getQueue().size();
int queueRemainingCapacity = threadPoolExecutor.getQueue().remainingCapacity();
String queueClassName = threadPoolExecutor.getQueue().getClass().getName();

return new ThreadPoolInfo(threadPoolName, spaceName, threadPoolClassName, coreSize, maxSize, queueClassName, queueSize, queueRemainingCapacity, period, taskTimeout);
}

public record ThreadPoolsDescriptor(List<ThreadPoolInfo> threadPoolInfoList) implements OperationResponseBody {}

public record ThreadPoolInfo(String threadPoolName,
String spaceName,

String threadPoolClassName,
int coreSize,
int maxSize,
String queueClassName,
int queueSize,
int queueRemainingCapacity,
long monitorPeriod,
long taskTimeout) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 com.alipay.sofa.boot.actuator.threadpool;

import com.alipay.sofa.common.thread.ThreadPoolConfig;
import com.alipay.sofa.common.thread.ThreadPoolGovernor;
import com.alipay.sofa.common.thread.ThreadPoolMonitorWrapper;
import com.alipay.sofa.common.thread.ThreadPoolStatistics;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

/**
* @author huzijie
* @version ThreadPoolEndpointTests.java, v 0.1 2024年03月22日 12:01 huzijie Exp $
*/
@ExtendWith(MockitoExtension.class)
public class ThreadPoolEndpointTests {

@Mock
private ThreadPoolGovernor threadPoolGovernor;

@InjectMocks
private ThreadPoolEndpoint threadPoolEndpoint;

@BeforeEach
public void setUp() {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 6, 10, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(8));

ThreadPoolConfig threadPoolConfig = Mockito.mock(ThreadPoolConfig.class);
when(threadPoolConfig.getThreadPoolName()).thenReturn("mockThreadPoolName");
when(threadPoolConfig.getSpaceName()).thenReturn("mockSpaceName");
when(threadPoolConfig.getTaskTimeoutMilli()).thenReturn(10L);
when(threadPoolConfig.getPeriod()).thenReturn(9L);

ThreadPoolStatistics threadPoolStatistics = new ThreadPoolStatistics(threadPoolExecutor);
ThreadPoolMonitorWrapper threadPoolMonitorWrapper = new ThreadPoolMonitorWrapper(
threadPoolExecutor, threadPoolConfig, threadPoolStatistics);
when(threadPoolGovernor.getAllThreadPoolWrappers()).thenReturn(
List.of(threadPoolMonitorWrapper));
}

@Test
public void threadPools() {
List<ThreadPoolEndpoint.ThreadPoolInfo> descriptor = threadPoolEndpoint.threadPools()
.threadPoolInfoList();
assertThat(descriptor).hasSize(1);
ThreadPoolEndpoint.ThreadPoolInfo threadPoolInfo = descriptor.get(0);
assertThat(threadPoolInfo.threadPoolName()).isEqualTo("mockThreadPoolName");
assertThat(threadPoolInfo.spaceName()).isEqualTo("mockSpaceName");
assertThat(threadPoolInfo.threadPoolClassName()).isEqualTo(
"java.util.concurrent.ThreadPoolExecutor");
assertThat(threadPoolInfo.coreSize()).isEqualTo(5);
assertThat(threadPoolInfo.maxSize()).isEqualTo(6);
assertThat(threadPoolInfo.queueSize()).isEqualTo(0);
assertThat(threadPoolInfo.queueRemainingCapacity()).isEqualTo(8);
assertThat(threadPoolInfo.queueClassName()).isEqualTo(
"java.util.concurrent.LinkedBlockingQueue");
assertThat(threadPoolInfo.monitorPeriod()).isEqualTo(9);
assertThat(threadPoolInfo.taskTimeout()).isEqualTo(10);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public class SofaBootConstants {
/**
* Default exposure web endpoint list
*/
public static final String SOFA_DEFAULT_ENDPOINTS_WEB_EXPOSURE_VALUE = "info,health,readiness,startup,beans,components,rpc,isle";
public static final String SOFA_DEFAULT_ENDPOINTS_WEB_EXPOSURE_VALUE = "info,health,readiness,startup,beans,components,rpc,isle,threadpool";

/**
* CPU core
Expand Down
2 changes: 1 addition & 1 deletion sofa-boot-project/sofaboot-dependencies/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<sofa.registry.version>6.1.8</sofa.registry.version>
<tracer.core.version>4.0.0</tracer.core.version>
<rpc.core.version>5.12.0</rpc.core.version>
<sofa.common.tools.version>2.1.0</sofa.common.tools.version>
<sofa.common.tools.version>2.1.1-SNAPSHOT</sofa.common.tools.version>
<sofa.bolt.version>1.6.6</sofa.bolt.version>
<sofa.hessian.version>3.5.1</sofa.hessian.version>
<sofa.ark.version>2.2.7</sofa.ark.version>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 com.alipay.sofa.smoke.tests.actuator.threadpool;

import com.alipay.sofa.common.thread.SofaThreadPoolExecutor;
import com.alipay.sofa.smoke.tests.actuator.ActuatorSofaBootApplication;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import static org.assertj.core.api.Assertions.assertThat;

/**
* Integration tests for {@link com.alipay.sofa.boot.actuator.threadpool.ThreadPoolEndpoint} web response.
*
* @author huzijie
* @version ThreadPoolEndpointWebTests.java, v 0.1 2024年03月22日 14:06 huzijie Exp $
*/
@SpringBootTest(classes = ActuatorSofaBootApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { "management.endpoints.web.exposure.include=threadpool" })
@Import(ThreadPoolEndpointWebTests.Config.class)
public class ThreadPoolEndpointWebTests {

@Autowired
private TestRestTemplate restTemplate;

@Test
public void threadPoolActuator() {
ResponseEntity<String> response = restTemplate.getForEntity("/actuator/threadpool", String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody())
.contains("""
{"threadPoolName":"demoThreadPool","threadPoolClassName":"com.alipay.sofa.common.thread.SofaThreadPoolExecutor","coreSize":20,"maxSize":20,"queueClassName":"java.util.concurrent.LinkedBlockingQueue","queueSize":0,"queueRemainingCapacity":10,"monitorPeriod":5000,"taskTimeout":30000}""");
}

@Configuration
static class Config {

@Bean
public SofaThreadPoolExecutor sofaThreadPoolExecutor() {
return new SofaThreadPoolExecutor(20, 20, 30, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10), "demoThreadPool");
}
}
}

0 comments on commit 0fbe6e7

Please sign in to comment.