You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
181 lines
6.5 KiB
181 lines
6.5 KiB
package com.fr.design.carton.latency; |
|
|
|
import com.fr.concurrent.NamedThreadFactory; |
|
import com.fr.config.MarketConfig; |
|
import com.fr.design.DesignerEnvManager; |
|
import com.fr.design.carton.SwitchForSwingChecker; |
|
import com.fr.design.mainframe.SiteCenterToken; |
|
import com.fr.event.Event; |
|
import com.fr.event.EventDispatcher; |
|
import com.fr.event.Listener; |
|
import com.fr.general.CloudCenter; |
|
import com.fr.general.GeneralContext; |
|
import com.fr.general.GeneralUtils; |
|
import com.fr.general.http.HttpToolbox; |
|
import com.fr.json.JSONObject; |
|
import com.fr.log.FineLoggerFactory; |
|
import com.fr.stable.StringUtils; |
|
import com.fr.workspace.WorkContext; |
|
import com.fr.workspace.Workspace; |
|
import com.fr.workspace.WorkspaceEvent; |
|
|
|
import java.util.HashMap; |
|
import java.util.Locale; |
|
import java.util.Map; |
|
import java.util.concurrent.ConcurrentHashMap; |
|
import java.util.concurrent.ExecutorService; |
|
import java.util.concurrent.Executors; |
|
import java.util.concurrent.ScheduledExecutorService; |
|
import java.util.concurrent.TimeUnit; |
|
import java.util.concurrent.atomic.AtomicInteger; |
|
|
|
import static com.fr.design.carton.CartonConstants.APPID; |
|
import static com.fr.design.carton.CartonConstants.DESIGNER_ID; |
|
import static com.fr.design.carton.CartonConstants.DESIGNER_VERSION; |
|
import static com.fr.design.carton.CartonConstants.DESIGN_METHOD; |
|
import static com.fr.design.carton.CartonConstants.LOCAL; |
|
import static com.fr.design.carton.CartonConstants.OPERANDS_NUM; |
|
import static com.fr.design.carton.CartonConstants.REMOTE; |
|
import static com.fr.design.carton.CartonConstants.TIME; |
|
import static com.fr.design.carton.CartonConstants.USERID; |
|
|
|
/** |
|
* 设计器延迟时间记录Metric |
|
* |
|
* @author Levy.Xie |
|
* @since 11.0 |
|
* Created on 2024/07/01 |
|
*/ |
|
public class DesignerLatencyMetric { |
|
|
|
private String latencyUrl; |
|
private ExecutorService executorService; |
|
private ScheduledExecutorService scheduler; |
|
private static final Map<LatencyLevel, AtomicInteger> LATENCY_CONTAINER = new ConcurrentHashMap<>(); |
|
|
|
private static final String DEFAULT_MONITOR_URL = "https://cloud.fanruan.com/api/monitor/"; |
|
private static final String LATENCY_TABLE_SUFFIX = "record_of_designer_latency/single"; |
|
|
|
private final static class InstanceHolder { |
|
static final DesignerLatencyMetric INSTANCE = new DesignerLatencyMetric(); |
|
} |
|
|
|
/** |
|
* 获取单例 |
|
*/ |
|
public static DesignerLatencyMetric getInstance() { |
|
return DesignerLatencyMetric.InstanceHolder.INSTANCE; |
|
} |
|
|
|
private DesignerLatencyMetric() { |
|
} |
|
|
|
/** |
|
* 启动 |
|
*/ |
|
public void start() { |
|
if (needMonitor()) { |
|
// 初始化容器 |
|
initializeContainer(); |
|
// 启动异步性能记录线程池 |
|
executorService = Executors.newFixedThreadPool(8); |
|
// 启动定时埋点 |
|
this.scheduler = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("LatencyMetricWorker")); |
|
this.scheduler.scheduleWithFixedDelay(this::collectAndSubmit, 60, 60, TimeUnit.MINUTES); |
|
// 注册设计器工作目录切换事件监听 |
|
EventDispatcher.listen(WorkspaceEvent.BeforeSwitch, new Listener<Workspace>() { |
|
@Override |
|
public void on(Event event, Workspace param) { |
|
collectAndSubmit(); |
|
} |
|
}); |
|
FineLoggerFactory.getLogger().info("[Latency] designer latency metric started."); |
|
} |
|
} |
|
|
|
/** |
|
* 关闭 |
|
*/ |
|
public void stop() { |
|
if (needMonitor()) { |
|
if (this.executorService != null) { |
|
this.executorService.shutdown(); |
|
} |
|
if (this.scheduler != null) { |
|
this.scheduler.shutdown(); |
|
} |
|
collectAndSubmit(); |
|
FineLoggerFactory.getLogger().info("[Latency] designer latency metric stopped."); |
|
} |
|
} |
|
|
|
private static boolean needMonitor() { |
|
// 海外版本不回传云中心 |
|
return SwitchForSwingChecker.isLatencyMonitoring() && Locale.CHINA.equals(GeneralContext.getLocale()); |
|
} |
|
|
|
private String getLatencyUrl() { |
|
if (StringUtils.isEmpty(latencyUrl)) { |
|
String monitorEntry = CloudCenter.getInstance().acquireUrlByKind("cloud.monitor.api.entrypoint"); |
|
latencyUrl = (StringUtils.isNotEmpty(monitorEntry) ? monitorEntry : DEFAULT_MONITOR_URL) |
|
+ LATENCY_TABLE_SUFFIX; |
|
} |
|
return latencyUrl; |
|
} |
|
|
|
private void initializeContainer() { |
|
for (LatencyLevel level : LatencyLevel.values()) { |
|
LATENCY_CONTAINER.put(level, new AtomicInteger()); |
|
} |
|
} |
|
|
|
private void resetContainer() { |
|
LATENCY_CONTAINER.values().forEach(count -> count.set(0)); |
|
} |
|
|
|
/** |
|
* 记录性能信息 |
|
*/ |
|
public void record(long cost) { |
|
executorService.submit(() -> { |
|
try { |
|
LatencyLevel level = LatencyLevel.measure(cost); |
|
LATENCY_CONTAINER.computeIfAbsent(level, k -> new AtomicInteger()).incrementAndGet(); |
|
} catch (Throwable ignore) { |
|
// 记录失败不影响业务 |
|
} |
|
}); |
|
} |
|
|
|
/** |
|
* 汇总并提交性能监控埋点 |
|
*/ |
|
public void collectAndSubmit() { |
|
Map<String, Object> para = new HashMap<>(); |
|
para.put("token", SiteCenterToken.generateToken()); |
|
para.put("content", collect()); |
|
try { |
|
HttpToolbox.post(getLatencyUrl(), para); |
|
FineLoggerFactory.getLogger().debug("[Latency] submit latency log to cloud."); |
|
} catch (Throwable t) { |
|
FineLoggerFactory.getLogger().debug(t,"[Latency] failed to submit latency log to cloud."); |
|
} |
|
resetContainer(); |
|
} |
|
|
|
private JSONObject collect() { |
|
JSONObject info = new JSONObject(); |
|
info.put(TIME, System.currentTimeMillis()); |
|
info.put(APPID, MarketConfig.getInstance().getCloudOperationMaintenanceId()); |
|
info.put(USERID, MarketConfig.getInstance().getBbsUid()); |
|
info.put(DESIGNER_ID, DesignerEnvManager.getEnvManager().getUUID()); |
|
info.put(DESIGNER_VERSION, GeneralUtils.getVersion()); |
|
info.put(DESIGN_METHOD, WorkContext.getCurrent().isLocal() ? LOCAL : REMOTE); |
|
info.put(OPERANDS_NUM, LATENCY_CONTAINER.values().stream().mapToInt(AtomicInteger::get).sum()); |
|
for (Map.Entry<LatencyLevel, AtomicInteger> entry : LATENCY_CONTAINER.entrySet()) { |
|
info.put(entry.getKey().getMark(), entry.getValue().get()); |
|
} |
|
return info; |
|
} |
|
|
|
}
|
|
|