当前位置: 首页 > news >正文

Jmeter的插件开发

一、Jmeter的启动流程

在说启动流程之前我们先来看看Jmeter源码的各个重要的包:

  1. components—包含与协议无关的组件,如可视化、断言等等。
  2. core —JMeter的核心代码,包括所有的核心接口和抽象类。
  3. examples —演示采样器如何使用新bean框架的例子(开发插件前可以好好看看该包下的样例代码)。
  4. functions —所使用的组件的标准功能。
  5. jorphan—提供常见实用功能的实用工具类
  6. protocol—包含了JMeter支持的不同协议(ftp  http、tcp—socket协议,没有webservice)

我们先看jmeter.bat这个脚本文件

%JM_START% "%JM_LAUNCH%" %ARGS% %JVM_ARGS% -jar "%JMETER_BIN%ApacheJMeter.jar" %JMETER_CMD_LINE_ARGS%

反正就是定义了一些JVM参数和java路径,重点看加载的jar包ApacheJMeter.jar

org.apache.jmeter.NewDriver.class里面有一个main方法,我们可以看到它使用自定义的ClassLoader加载JMeter.class,并且调用start方法

    public static void main(String[] args) {if(!EXCEPTIONS_IN_INIT.isEmpty()) {System.err.println("Configuration error during init, see exceptions:"+exceptionsToString(EXCEPTIONS_IN_INIT)); // NOSONAR Intentional System.err use} else {Thread.currentThread().setContextClassLoader(loader);setLoggingProperties(args);try {// Only set property if it has not been set explicitelyif(System.getProperty(HEADLESS_MODE_PROPERTY) == null && shouldBeHeadless(args)) {System.setProperty(HEADLESS_MODE_PROPERTY, "true");}Class<?> initialClass = loader.loadClass("org.apache.jmeter.JMeter");// $NON-NLS-1$Object instance = initialClass.getDeclaredConstructor().newInstance();Method startup = initialClass.getMethod("start", new Class[] { new String[0].getClass() });// $NON-NLS-1$startup.invoke(instance, new Object[] { args });} catch(Throwable e){ // NOSONAR We want to log home directory in case of exceptione.printStackTrace(); // NOSONAR No logger at this stepSystem.err.println("JMeter home directory was detected as: "+JMETER_INSTALLATION_DIRECTORY); // NOSONAR Intentional System.err use}}}

从JMeter.start方法我们可以得知JMeter支持两种启动方式——startGui(图形化界面启动)和startNonGui(使用命令行启动),以下便是start方法的主要流程:

 

https://www.processon.com/view/link/6005018af346fb566eb6f835

插件的加载

插件加载的基本原理:通过各种类别的工厂类加载约定路径/lib/ext下的jar包,这里以SamplerCreatorFactory为例

SamplerCreatorFactory#init()

    private void init() { // WARNING: called from ctor so must not be overridden (i.e. must be private or final)try {List<String> listClasses = ClassFinder.findClassesThatExtend(JMeterUtils.getSearchPaths(),new Class[] {SamplerCreator.class });//以下代码略.....

与此类似的工厂类还有MenuFactory#initializeMenus()

    private static void initializeMenus(Map<String, List<MenuInfo>> menus, Set<String> elementsToSkip) {try {List<String> guiClasses = ClassFinder.findClassesThatExtend(JMeterUtils.getSearchPaths(),new Class[] {JMeterGUIComponent.class, TestBean.class}).stream()// JMeterTreeNode and TestBeanGUI are special GUI classes,// and aren't intended to be added to menus.filter(name -> !name.endsWith("JMeterTreeNode")).filter(name -> !name.endsWith("TestBeanGUI")).filter(name -> !name.equals("org.apache.jmeter.gui.menu.StaticJMeterGUIComponent")).filter(name -> !elementsToSkip.contains(name)).distinct().map(String::trim).collect(Collectors.toList());//以下代码略.....

那还有一个问题,我们看到的GUI界面里面,插件跟图标是怎么对应上的呢?这里我们可以看到JMeter.getIconMappings()方法

    @Override@SuppressWarnings("JdkObsolete")public String[][] getIconMappings() {//图标与类映射关系的加载路径final String defaultIconProp = "org/apache/jmeter/images/icon.properties";final String iconSize = JMeterUtils.getPropDefault(TREE_ICON_SIZE, DEFAULT_TREE_ICON_SIZE);String iconProp = JMeterUtils.getPropDefault("jmeter.icons", defaultIconProp);//$NON-NLS-1$//图片文件的加载方法Properties p = JMeterUtils.loadProperties(iconProp);if (p == null && !iconProp.equals(defaultIconProp)) {log.info("{} not found - using {}", iconProp, defaultIconProp);iconProp = defaultIconProp;p = JMeterUtils.loadProperties(iconProp);}if (p == null) {log.info("{} not found - using inbuilt icon set", iconProp);return DEFAULT_ICONS;}log.info("Loaded icon properties from {}", iconProp);String[][] iconlist = new String[p.size()][3];Enumeration<?> pe = p.keys();int i = 0;while (pe.hasMoreElements()) {String key = (String) pe.nextElement();String[] icons = JOrphanUtils.split(p.getProperty(key), " ");//$NON-NLS-1$iconlist[i][0] = key;iconlist[i][1] = icons[0].replace(KEY_SIZE, iconSize);if (icons.length > 1) {iconlist[i][2] = icons[1].replace(KEY_SIZE, iconSize);}i++;}return iconlist;}

我们先来看看“org/apache/jmeter/images/icon.properties”这个文件到底是什么

与此同时JMeter定义了一个二维数组作为默认值。

我们可以看到只要是插件类的GUI部分继承了以上数组中的GUI类,JMeter框架便会自动将其映射为所对应的组件类型和图标。这些组件分别是:TestPlanGui、AbstractTimerGui、ThreadGroupGui、AbstractListenerGui、AbstractConfigGui、AbstractPreProcessorGui、AbstractPostProcessorGui、AbstractControllerGui、WorkBenchGui、AbstractSamplerGui、AbstractAssertionGui。

 

二、Jmeter各组件介绍

组件的类型

对于JMeter的基本组件,我们可以将其简单的划分为GUI和非GUI两大类,分别对应JMeter的GUI启动和命令行启动,也就是说如果我们要开发一款插件同时支持这两种启动的话,需要实现其对应的GUI和非GUI接口,而TestElement是所有组件的最基本单元,组件类都是TestElement类的子类。

GUI组件

即可以通过JMeter图形管理控制器在测试计划Tree中进行添加的组件,主要包括ThreadGroup(线程组)、Config(配置元件)、Timer(定时器)、Modifier(前置处理器)、Extractor(后置处理器)、Controller(逻辑控制器)、Sampler(测试抽样器)、Assertion(断言)和Listener(监听器)

非GUI组件

典型的代表是Function(函数)和某些子测试抽样器,如JavaSamplerClient。

对于组件一般有两种实现方法:

    1. GUI与逻辑控制分离:GUI部分通过继承各种组件GUI抽象类,逻辑控制部分通过继承组件逻辑抽象类和实现各种接口方式从而实现不同组件的内部逻辑控制;
    2. GUI与逻辑控制不分离:与分离方法的区别在于不单独实现GUI部分,在逻辑控制部分通过实现TestBean接口方法从而实现对GUI界面的配置。

各个类别介绍

ThreadGroup(线程组)组件

ThreadGroup(线程组)组件继承AbstractThreadGroup抽象类,通过重写各类控制方法,如void scheduleThread(JMeterThread thread) 、stopThread(String threadName, boolean now) 、threadFinished(JMeterThread thread)等,来达到控制和协调各线程(虚拟用户)的行为,线程组是构建一个性能测试模型的最基本组件。

 

Config(配置元件)组件

Config(配置元件)组件相对其他组件比较特殊,通过继承ConfigTestElement类或只需要GUI部分的实现即可完成本体任务,而对于一个需要配置的组件类则需要实现ConfigMergabilityIndicator接口的public boolean applies(ConfigTestElement configElement)方法,用来指明哪些Config组件可以用来对其进行配置,这里参考TCPSampler的源代码如下:

private static final Set<String> APPLIABLE_CONFIG_CLASSES = new HashSet<String>(Arrays.asList(new String[]{"org.apache.jmeter.config.gui.LoginConfigGui","org.apache.jmeter.protocol.tcp.config.gui.TCPConfigGui","org.apache.jmeter.config.gui.SimpleConfigGui"}));@Overridepublic boolean applies(ConfigTestElement configElement) {String guiClass = configElement.getProperty(TestElement.GUI_CLASS).getStringValue();return APPLIABLE_CONFIG_CLASSES.contains(guiClass);
}

以上代码指明LoginConfigGui、SimpleConfigGui和TCPConfigGui这三个配置元件可以对TCPSampler组件进行配置。

 

Timer(定时器)组件

Timer(定时器)组件通过继承AbstractTestElement抽象类,实现Timer接口的delay()方法来实现对时间的控制,主要的控制内容如下:

控制线程延时,即用来模仿思考时间(ThinkTime)或键盘时间(KeyTime);

控制线程行为,如SyncTimer(同步计时器),就是内部利用CyclicBarrier来控制阻塞和释放全部运行线程的逻辑行为,从而达到“集合点”的目的。

 

Modifier(前置处理器)组件

Modifier(前置处理器)组件通过继承AbstractTestElement抽象类,实现PreProcessor接口的process ()方法控制逻辑,常常需要对线程上下文中的当前Sampler和前一个SampleResult进行识别和判断,以做出正确的处理,一般的行为是通过取出SampleResult的某些值或直接在当前Sampler启动sample方法之前对其某些属性进行修饰。

 

Extractor(后置处理器)组件

Extractor(后置处理器)组件通过继承AbstractTestElement抽象类,实现PostProcessor接口的process ()方法控制逻辑,常常需要对线程上下文中的前一个SampleResult进行识别和判断,以做出正确的处理。

 

Controller(控制器)组件

Controller(控制器)组件通过继承GenericController类,通过重写Sampler next()、void setDone(boolean done)、int getIterCount()、void reInitialize()等方法来控制Sampler的测试行为。

 

Sampler(测试抽样器)组件

Sampler(测试抽样器)组件继承AbstractSampler抽象类,通过重写SampleResult sample(Entry e)方法,实现测试过程以及测试结果的采集功能。

 

Assertion(断言)组件

Assertion(断言)组件通过继承AbstractTestElement抽象类,实现Assertion接口的getResult(SampleResult result)方法对结果内容进行判断,从而实现断言方法,用于对Sampler组件所产生的抽样采集结果内容进行断言。

 

Listener(监听器)主要有两种方案:

  1. 直接继承AbstractTestElement,实现sampleListener或Visualizer等接口方法
  2. 实现ResultCollector和Runnable等接口方法

三、如何开发一个插件(以Sampler组件为例

1.创建一个项目,并添加JMeter的核心包

2.实现AbstractSampler抽象类

public class DubboSampler extends AbstractSampler {public final static String FUNCTION = "function";@Overridepublic SampleResult sample(Entry entry) {SampleResult res = new SampleResult();res.sampleStart();//输出GUI界面所输入的函数方法返回结果System.out.println(this.getProperty(FUNCTION));res.sampleEnd();res.setSuccessful(true);return res;}
}

 

3.实现AbstractSamplerGui抽象类

public class DubboSamplerGUI extends AbstractSamplerGui {private JTextField functionTextField = null;public DubboSamplerGUI() { init(); }@Overridepublic void configure(TestElement element) {super.configure(element);functionTextField.setText(element.getPropertyAsString(DubboSampler.FUNCTION));}private void init() {JPanel mainPanel = new JPanel(new GridBagLayout());functionTextField = new JTextField(20);mainPanel.add(functionTextField);add(mainPanel);}@Overridepublic TestElement createTestElement() {//创建所对应的SamplerTestElement sampler = new DubboSampler();modifyTestElement(sampler);return sampler;}@Overridepublic String getLabelResource() {return this.getClass().getSimpleName();}@Overridepublic void modifyTestElement(TestElement sampler) {super.configureTestElement(sampler);if (sampler instanceof DubboSampler) {DubboSampler dubboSampler = (DubboSampler) sampler;dubboSampler.setProperty(DubboSampler.FUNCTION, functionTextField.getText());}}@Overridepublic String getStaticLabel() {//设置显示名称return "DubboSampler";}private void initFields() {functionTextField.setText("");}@Overridepublic void clearGui() {super.clearGui();initFields();}
}

4.把工程打成jar包放到\lib\ext之下

四、开源插件DubboSampler的二次开发

需求背景:在自动化回归测试中,使用线上录制好的流量直接在压测环境进行回放,需要DubboSampler支持按照录制流量调用。

源码分析:

DubboSampler

public class DubboSample extends AbstractSampler implements Interruptible {private static final Logger log = LoggingManager.getLoggerForClass();private static final long serialVersionUID = -6794913295411458705L;public static ApplicationConfig application = new ApplicationConfig("DubboSample");@Overridepublic SampleResult sample(final Entry entry) {final SampleResult res = new SampleResult();res.setSampleLabel(this.getName());//构造请求数据res.setSamplerData(this.getSampleData());//调用dubbores.setResponseData(JsonUtils.toJson(this.callDubbo(res)), StandardCharsets.UTF_8.name());//构造响应数据res.setDataType(SampleResult.TEXT);return res;}

sample方法是取样器的核心方法,返回的SampleResult也就是测试数据的取样结果。

 

我们再来看看getSampleData方法

    /*** Construct request data*/private String getSampleData() {log.info("sample中的实例id" + this.toString() + ",element名称" + this.getName());final StringBuilder sb = new StringBuilder();sb.append("Registry Protocol: ").append(Constants.getRegistryProtocol(this)).append("\n");sb.append("Address: ").append(Constants.getAddress(this)).append("\n");sb.append("RPC Protocol: ").append(Constants.getRpcProtocol(this)).append("\n");sb.append("Timeout: ").append(Constants.getTimeout(this)).append("\n");sb.append("Version: ").append(Constants.getVersion(this)).append("\n");sb.append("Retries: ").append(Constants.getRetries(this)).append("\n");sb.append("Cluster: ").append(Constants.getCluster(this)).append("\n");sb.append("Group: ").append(Constants.getGroup(this)).append("\n");sb.append("Connections: ").append(Constants.getConnections(this)).append("\n");sb.append("LoadBalance: ").append(Constants.getLoadbalance(this)).append("\n");sb.append("Async: ").append(Constants.getAsync(this)).append("\n");sb.append("Interface: ").append(Constants.getInterface(this)).append("\n");sb.append("Method: ").append(Constants.getMethod(this)).append("\n");sb.append("Method Args: ").append(Constants.getMethodArgs(this).toString());sb.append("Attachment Args: ").append(Constants.getAttachmentArgs(this).toString());return sb.toString();}

这里是解析TestElement对应的参数用作Sampler的请求,这里用一个Constants里的枚举属性作为键名解析。我们可以注意到AbstractSampler extends AbstractTestElement,而AbstractTestElement implements TestElement,因此这里可以直接把this(Sampler类)丢进去解析。

如果我们想控制dubbo调用的入参,可能就得从这个方法切入,把简单的解析参数改成通过http接口或者从数据库查询出来,建议做一个本地缓存,以防频繁请求影响测试性能。

 

DubboSampleGui

这是DubboSampler的Gui组件,实现了AbstractSamplerGui抽象类的接口,我们可以重点看createTestElement方法,我们发现其实Gui的调用方式一样会调用到DubboSampler的方法 (DubboSample sample = new DubboSample()

public class DubboSampleGui extends AbstractSamplerGui {private static final Logger log = LoggingManager.getLoggerForClass();private static final long serialVersionUID = -3248204995359935007L;private final DubboPanel panel;public DubboSampleGui() {super();panel = new DubboPanel();init();}/*** Initialize the interface layout and elements*/private void init() {//所有设置panel,垂直布局JPanel settingPanel = new VerticalPanel(5, 0);settingPanel.setBorder(makeBorder());Container container = makeTitlePanel();settingPanel.add(container);//所有设置panelpanel.drawPanel(settingPanel);//全局布局设置setLayout(new BorderLayout(0, 5));setBorder(makeBorder());add(settingPanel,BorderLayout.CENTER);}/*** component title/name*/@Overridepublic String getLabelResource() {return this.getClass().getSimpleName();}/*** this method sets the Sample's data into the gui*/@Overridepublic void configure(TestElement element) {super.configure(element);log.debug("sample赋值给gui");panel.configure(element);panel.bundleElement(element);}/*** Create a new sampler. And pass it to the modifyTestElement(TestElement) method.*/@Overridepublic TestElement createTestElement() {log.debug("创建sample对象");//创建sample对象DubboSample sample = new DubboSample();modifyTestElement(sample);return sample;}/*** this method sets the Gui's data into the sample*/@SuppressWarnings("unchecked")@Overridepublic void modifyTestElement(TestElement element) {log.debug("gui数据赋值给sample");//给sample赋值super.configureTestElement(element);panel.modifyTestElement(element);panel.bundleElement(element);}/*** sample's name*/@Overridepublic String getStaticLabel() {return "Dubbo Sample";}/*** clear gui's data*/@Overridepublic void clearGui() {log.debug("清空gui数据");super.clearGui();panel.clearGui();}}

这个包的其他几个类是写Gui组件的界面的,这里跟需求无关就不多展开了。

相关文章:

Jmeter的插件开发

一、Jmeter的启动流程 在说启动流程之前我们先来看看Jmeter源码的各个重要的包:components—包含与协议无关的组件,如可视化、断言等等。 core —JMeter的核心代码,包括所有的核心接口和抽象类。 examples —演示采样器如何使用新bean框架的例子(开发插件前可以好好看看该包…...

Educational Codeforces Round 182 (Rated for Div. 2)

A. Cut the Array 题意:把数组分成三段,使得每段和模\(3\)后的值都相同或者都不相同。 \(n\)很小,暴力枚举分段就行了。点击查看代码 #include <bits/stdc++.h>using i64 = long long;void solve() {int n;std::cin >> n;std::vector<int> a(n);for (int …...

java第二周课前提问

一、代码引入 public class Main {static void changeStr(String x) {x = "xyz";}static void changeArr(String[] strs) {for (int i = 0; i < strs.length; i++) {strs[i] = strs[i]+""+i;}}public static void main(String[] args) { String x = …...

java GC

java GC...

Redis最佳实践——性能优化技巧之监控与告警详解

一、监控体系构建1. 核心监控指标矩阵指标类别 关键指标 计算方式/说明 健康阈值(参考值)内存相关 used_memory INFO Memory 获取 不超过 maxmemory 的 80%mem_fragmentation_ratio 内存碎片率 = used_memory_rss / used_memory 1.0-1.5命中率 keyspace_hits INFO Stats 获取…...

week1

任务一,编码规范: 我在网上找到了华为公司C++编码规范,我摘下几点我觉得我应该注意的 1、程序块要采用缩进风格编写, 缩进的空格数为4个 2、不允许把多个短语句写在一行中, 即一行只写一条语句 3、 if、for、do、while、case、switch、default等语句自占一行, 且if、for、do…...

EF Core 与 MySQL:迁移和关系配置详解

EF Core 与 MySQL:迁移和关系配置详解 1. EF Core 中的关系类型 Entity Framework Core 支持三种主要的关系类型: 一对一关系 (One-to-One) 一个实体实例只与另一个实体实例相关联。例如:一个用户有一个用户资料。csharppublic class User {public int Id { get; set; }pub…...

《原子习惯》-读书笔记2

2025.09.15 Day2 1、目标和体系有什么不同?我最初是从“呆伯特漫画”的创作者斯科特亚当斯(Scott Adams)那里了解到两者的区别的。目标是关于你想要达到的结果,而体系是涉及导致这些结果的过程。2、争取每天都有进步是你走向成功唯一的方法。3、如果你想要得到更好的结果,那…...

CF1626D 题解

CF1626D 题解 貌似题解区没有这种解法。 题面 CF1626D Martial Arts Tournament - 洛谷 (luogu.com.cn) 思路 问题就是把 \(a\) 分成 \(3\) 个子集(可以为空),每两个子集里的数并不重复,把每个子集的大小补到 \(2^x\) 最少要补的数的个数。 先把 \(a\) 给排序,那么就可以转…...

Python 集合运算:并集、交集、差集全解析

在 Python 中,集合(set)是一种无序的、不包含重复元素的数据结构。集合提供了丰富的运算方法,包括并集、交集、差集等。这些运算在数据处理、数学计算和算法设计中非常实用。今天,就让我们一起深入学习 Python 集合的运算方法,并通过实例代码展示它们的使用。 一、集合的…...

第一周数据可视化作业

一、个人介绍 My name is Ou Qi. (🙂) 我性格阳光开朗,始终保持着对学习的热忱和对未知事物的探索欲,尤其从小就对数学有着浓厚兴趣 —— 课堂上会紧跟老师的思路深度思考,课后也常主动琢磨题型、尝试举一反三,在不断推导中把知识学扎实。 二、我的专业选择与学习历程 步…...

用 C++ + OpenCV + Tesseract 实现英文数字验证码识别

本文展示如何用 C++ 结合 OpenCV 做图像预处理,再调用 Tesseract OCR 识别验证码。适用于希望在高性能后端或本地服务里集成 OCR 的场景。方案包含: 更多内容访问ttocr.com或联系1436423940 环境与依赖安装 图像预处理(灰度、二值化、形态学去噪、放大) 使用 Tesseract API…...

java 第一节课课前提问

一、使用Java能编写的程序 企业级后端应用 Java 在企业级开发中占据重要地位,常被用于构建大型服务器端应用,如电商平台、银行交易系统、CRM(客户关系管理)系统等。这类应用通常需要处理高并发、复杂业务逻辑和海量数据,Java 凭借稳定的性能、丰富的企业级框架(如 Spring…...

二进制解码器、选通器和分配器

二进制解码器 3比特的二进制解码器可以由下图表示。每种组合方式对应着解码器的不同输出。3-8解码器可以用三个非门和三个与门构成解码器可以拼接起来组成更大的解码器,比如两个3-8解码器可以拼起来组成一个4-16解码器。选通器和分配器。 选通器 一个8选1的选通器如下图所示。…...

2025最新版 Photoshop软件免费下载安装完整教程(PS2025)超详细安装教程

Adobe Photoshop 2025 凭借升级的 AI 编辑功能、更优的图像处理效率,成为设计与摄影领域的热门工具。但不少用户在安装时,易因路径选择、安全软件拦截等问题卡壳。本教程聚焦安装全流程,从前期准备到后续配置,用清晰步骤帮你避开误区,顺利完成安装,快速解锁 PS 2025 的创…...

nac一键卸载软件脚本

将下面的代码保存为uninstall.sh: echo delete shit.app..need your root pwd; sudo rm -rf /Applications/dvc-manageproxy-exe.app; sudo rm -rf /Applications/LVSecurityAgent.app; echo script is fighting...; sudo chflags noschg /opt/LVUAAgentInstBaseRoot; sudo chf…...

交叉编译openharmony版本的openssh

sudo mkdir /systemsudo chmod 777 /system/export CC=aarch64-linux-gnu-gcc编译zlib./configure --prefix=/systemmake && make install 编译openssl./config linux-aarch64 --prefix=/system/ --openssldir=/system/etc/ssl --libdir=…...

为什么不建议在 Docker 中跑 MySQL

前言 今天我们来聊聊一个很有趣的话题:为什么我不建议在Docker中运行MySQL数据库? 有些小伙伴在工作中可能为了部署方便,习惯将所有组件都容器化,但数据库真的适合放在容器里吗? 今天就专门跟大家一起聊聊这个话题,希望对你会有所帮助。 一、容器化与数据库:天生的矛盾?…...

CFD

算例汇总 1、一维Sod激波管 2、二维平板 3、NACA0012 4、高马赫数喷流 5、双马赫反射 6、二维Riemann 7、二维Rayleigh-Taylor 8、TENO算例...

[MCP][05]Elicitation示例

Elicitation能让工具在关键时刻暂停执行,并向用户请求特定信息前言 如果你之前接触过LangGraph的"Human in the loop"概念,那么理解MCP的Elicitation机制就会容易很多。这两个功能非常相似,都是让AI在需要时停下来,礼貌地向人类寻求帮助或确认。 想象一下,当你正…...

Warsaw主题关闭导航条

\setbeamertemplate{headline}{}...

Python Socket网络编程(2)

进程:提供计算资源的单位 线程:真正工作的单位(cpu调度最小单元) GIL锁:全局解释器锁(是CPython解释器特有的,平时说的Python解释器一般都是CPython解释器,还有GPython等等) 让一个进程中同一时刻只能有一个线程可以被CPU调动。所以Python中应该是没有严格意义的多线程…...

PS2025安装包下载及PS2025安装包安装教程详细步骤(包含安装包下载链接)

在图像处理领域,Adobe Photoshop 一直占据着举足轻重的地位,而 PS 2025 更是汇聚前沿技术与实用功能,成为众多设计师与图像处理爱好者的得力工具。但初次安装这款软件,可能会因步骤繁杂、细节众多而让人感到棘手。别担心,本教程将以清晰、简洁的方式,带你一步步完成 PS 2…...

Nature Genetics | 本周最新文献速递

Multiancestry brain pQTL fine-mapping and integration with genome-wide association studies of 21 neurologic and psychiatric conditions 中文标题: 多祖先脑蛋白遗传调控解码!pQTL精细映射揭示神经精神疾病机制 关键词: 脑蛋白定量性状位点、精细映射、多祖先整合、…...

关于go里切片作为函数参数时是引用传递还是值传递

go语言中切片参数的值传递问题问题起因 写一道回溯算法题,把ans二维数组作为函数参数传入,想在函数里面,不停地append,最后返回ans 实际发现ans打印出来是空的,就很奇怪,因为我是事先分配好空间的,理论上不会发生扩容,底层数组是共用的,咋回事 func permute(nums []in…...

DRAN读写循环

DRAM读写循环 以一个8 * 8 的二维阵列为例子,假设部分存储单元为1,部分为0,现在要读写其中某一个cell的值。为了确定存储的位置,我们需要内存地址,为了传输内存地址,我们需要地址总线。8 * 8阵列一共有64个cell,我们需要6线地址总线,一共能表示64种二进制值。三根地址总…...

数据结构操作相关

堆 1、插入元素上滤每一次与父亲比较,满足大小就往上交换,直至不能往上为止。每次往上交换不会影响下面的性质2、删除/输出堆顶下滤假设大根堆,根节点换入末尾节点,每次先找出大儿子,若大儿子比自己大,则往下和他交换,直至不能往下为止。 3、建堆 1)初始为空,逐个inse…...

Neisbitt 不等式的证法

\(a,b,c\in R^+求证:\frac{a}{b+c}+\frac{b}{a+c}+\frac{c}{a+b}\geq\frac{3}{2}\) 证明: \(\because a,b,c\in R^+,\therefore\exists x,y,使得b=ax,c=ay\) \(\therefore LHS=\frac{1}{x+y}+\frac{x}{1+y}+\frac{y}{1+x}\) \(\therefore 令f(x,y)=\frac{1}{x+y}+\frac{x}{1+…...

端口转发神器Rinetd:轻量级安装与配置指南

什么是Rinetd? Rinetd(Redirection Internet Daemon)是一款轻量级的TCP端口转发工具,可以将来自一个IP地址和端口的连接转发到另一个IP地址和端口。它配置简单、资源占用少,是系统管理员和开发人员进行端口转发的理想选择。 Rinetd的主要特点轻量级:体积小,资源占用低 配…...

C语言中递归思想的应用

C语言中递归思想的应用 一、递归思想 在C语言中,函数是程序的基本单位,每个函数负责解决特定问题。但如果程序中出现n个相同的问题,就需要调用对应函数n次,这会导致程序冗长、可读性差。那么,有没有更简洁的解决方案呢? 答案是递归函数。递归函数并非万能,它更适用于解决…...

WITH RECURSIVE 递归公用表表达式(CTE)

生成一个从 1 到 12352 的连续数字序列SQL server SQL Server 对递归 CTE 有默认的递归深度限制(默认是 100),当递归次数超过这个限制时会报错。当远超默认限制时,需要在查询前使用 OPTION (MAXRECURSION 0) 来取消递归深度限制。WITH RECURSIVE num_sequence AS (SELECT 1…...

#java作业

1方法相关问题、 public class Main { static void changeStr(String x) { x = "xyz"; } static void changeArr(String[] strs) { for (int i = 0; i < strs.length; i++) { strs[i] = strs[i]+""+i; } } public static void main(String[] args) { …...

leetcode 3541. 找到频率最高的元音和辅音 便捷

leetcode 3541. 找到频率最高的元音和辅音 便捷pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !…...

匿名递归与不动点组合子

先贴上 CS61A Homework 3 Recursion, Tree Recursion 中的最后一道思考题题面: ​Q6: Anonymous FactorialThis question demonstrates that its possible to write recursive functions without assigning them a name in the global frame.The recursive factorial function…...

Markdown学习Day01

Markdown学习第一天 【【狂神说Java】Java零基础学习视频通俗易懂】https://www.bilibili.com/video/BV12J41137hu?p=6&vd_source=e3ba980d960d7d6c98e4872bba8cf225 Markdown学习 二级标题 字体 her hus hou KLI 引用选择不需要辩护。分割线插图超链接 学Java 表格年级 班…...

flutter compass结构代码分析

1.config文件夹:1.1assets.dart返回一个Assets类,包含activities和destinations两个静态属性。两个静态属性分别代表虚拟数据存放的路径。在assets文件夹下1.2 dependencies.dart使用provider绑定远程访问和本地访问所需要的数据 2.data文件夹2.1 repositoriesactivity文件夹a…...

25.9.15

应该都知道我退役了吧。 我对于这一段竞赛生涯呢,情感太复杂了。 (话说退役半年才回来写是不是有点晚了) 怎么说呢,这段竞赛让我有了一段快乐的时光,也让我彻底改变了。 想说很多,但是又说不出口。 先说说坏处吧 我的抑郁症可以说是被竞赛加深的,以至于我在初三下和高一…...

二十八、共享内存多处理器的基本概念

目录1. 核心定义2. 两种主要的共享内存架构a) 均匀内存访问b) 非均匀内存访问3. 共享内存多处理器的核心挑战与解决方案a) 缓存一致性b) 内存一致性4. 编程模型与同步5. 优势与劣势优势:劣势:总结共享内存多处理器是多处理器系统中最常见和直观的一种架构,也是现代多核CPU设…...

详细介绍:【ARMv7】系统复位上电后的程序执行过程

详细介绍:【ARMv7】系统复位上电后的程序执行过程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospac…...

C#高级语法

https://www.cnblogs.com/NotEnough/p/7426853.html https://juejin.cn/post/7100033147101773831...

配置Maven

IDEA配置Maven原文链接:https://blog.csdn.net/leah126/article/details/132020904 一、Maven下载 首先我们进入maven官方网站,进入网页后,点击Download去下载。下载免安装版,解压即可,解压至磁盘任意目录,尽量不要取中文名如下图: 二、配置Maven环境变量 打开cmd命令行,…...

那两年的回忆录

小引 我的OI之路是短暂且波折的,像是一则故事,曲曲折折,却又很是有趣,或许那两年是我最愉快的时光吧。 我总是问自己当初为什么要学OI,许是一腔热血,许是奔赴热爱。仔细想想,从最开始接触scratch,到后来的Python,最后的C++,前面两个都是觉得好玩,觉得有趣,可C++呢,…...

DDR4基本介绍

DDR4简介 DRAM的存储原理 DRAM的基本存储单元:cell 一个cell由一个晶体管和一个电容(约为30pF)组成,电容存储了电量代表1,电容放空电量代表0,晶体管作为电容的充放电开关,以便实现1bit数据的读写,cell的结构如下图所示 :读的过程:首先打开MOS管,根据电容的充放电信息…...

网络同步预测-Prediction

预测(Prediction) 是解决网络延迟问题、提升玩家操作流畅度的核心机制客户端接收玩家输入玩家操作(如 WASD 移动、跳跃)被捕获为输入事件(FInputActionValue)。客户端本地预测执行客户端不等服务器响应,直接基于输入在本地模拟角色行为(如移动、动画播放),并立即更新…...

二十五、多处理器的基本概念 (SISD/SIMD/MIMD)

目录1. SISD - 单指令流单数据流2. SIMD - 单指令流多数据流3. MIMD - 多指令流多数据流总结与对比简单类比多处理器体系结构中的三个基本概念:SISD、SIMD 和 MIMD,这些概念由迈克尔弗林(Michael Flynn)于1966年提出,被称为弗林分类法(Flynns Taxonomy)。它根据指令流(…...

java课堂问题2

1.1changeStr(String x):方法内部尝试将传入的字符串参数重新赋值为 "xyz",但该操作仅在方法内部有效,不会影响外部实参 changeArr(String[] strs):遍历传入的字符串数组,对数组中的每个元素进行修改,在原字符串后拼接其索引值(例如将第 0 个元素改为 "原…...

集训总结(六)

9.15 听 CEO 讲了 sb 树,不知道该写什么,挂个祂博客的链接吧。 https://www.cnblogs.com/Augenstern-/p/18964066...

GAS_Aura-Prediction GAS

1讲了关于GAS中的网络之间,Client-Server的网络同步时,需要使用预测...

PromptPilot 产品发布:火山引擎助力AI提示词优化的新利器

周六非常荣幸参加了火山引擎官方举办的PromptPilot产品发布会。其实早在8月份,我就有幸参与了该产品的内测阶段,并撰写了一些体验心得和感受。此次发布会当天,不仅深入聆听到了产品负责人以及工程师们对PromptPilot的全新解读和详细介绍,还现场感受到了他们对产品未来发展方…...

安装window版本docker

下载 下载链接:https://hub.docker.com/?overlay=onboarding 在 Windows 上部署 Docker 的方法都是先安装一个虚拟机,VMware版本17.0以上可以兼容安装docker 另一个可以下载 Hyper-V,Hyper-V 是微软开发的虚拟机,类似于 VMWare 或 VirtualBox 选中Hyper-V 也可以通过命令…...