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

Vert.x学习笔记-Vert.x的基本处理单元Verticle

Verticle介绍

Verticle是Vert.x的基本处理单元,Vert.x应用程序中存在着处理各种事件的处理单元,比如负责HTTP API响应请求的处理单元负责数据库存取的处理单元负责向第三方发送请求的处理单元。Verticle就是对这些功能单元的封装,Verticle可被部署,有自己的生命周期,Verticle是Vert.x中构建异步事件处理程序及相关业务逻辑的基础。

Verticle特点

Verticle是Vert.x框架的核心概念,它是一种单一组件,所有业务功能都使用这种组件来完成。

  • 简单性(Simplicity) :Vert.x的设计采用了单一组件结构,即Verticle,这使得所有业务功能都使用同一种组件进行编程,从而让开发人员能够快速适应Vert.x编程,并加快项目的开发速度。
  • 并发性(Concurrency) :在处理多用户并发连接请求时,Vert.x摒弃了传统的多线程、同步工作、阻塞模式,而采用简单的单线程、异步工作、非阻塞模式,通过单线程内的事件驱动实现并发处理。这种处理方式更高效地利用了系统资源,并能够更好地应对高并发场景。
  • 线程隔离性(Thread Isolation) :Verticle的最大特色就是它的线程隔离性。在启动时,Verticle就被分配给了创建和start方法调用的Event Loop。当调用一个使用core API的handler的方法时,Vert.x保证这些handler将在同一个Event Loop上执行。这种线程隔离性可以保证在Verticle实例的代码是在同一个Event Loop执行,避免了多线程环境下的并发问题。

实现原理

Verticle的实现原理主要基于事件驱动和非阻塞I/O模型。在Vert.x中,应用程序将业务逻辑封装在事件驱动的处理程序中,称为Verticle。每个Verticle都是一个独立的运行单元,可以按照一定规则通过时间和状态变换来实现。
具体来说,当外部系统或客户端发起请求时,Vert.x会将请求转换为处理程序可以处理的标准格式,并将处理结果返回给客户端。在这个过程中,Vert.x采用了异步消息传递的方式,使得不同Verticle之间可以相互通信。
此外,Vert.x还提供了容器来为Verticle提供运行的环境和服务,并支持将多个Verticle部署在同一容器中。当一个Verticle需要访问网络服务器时,它可以通过容器来获取相应的服务,并且当多个Verticle实例被部署时,事件会按照轮询的方式分发到每个Verticle实例,这有助于在大量并发网络请求时最大化CPU使用率。
总之,Verticle的实现原理是基于事件驱动和非阻塞I/O模型,通过将业务逻辑封装在事件驱动的处理程序中,并借助容器提供运行环境和服务的支持,实现了高效、可扩展、易于使用的应用程序开发方式。

接口定义

public interface Verticle {/*** Get a reference to the Vert.x instance that deployed this verticle* 获取一个Vert.x实例的引用用来发布当前的verticle*/Vertx getVertx();/*** Initialise the verticle with the Vert.x instance and the context.* 通过Vert.x实例和上下文来初始化一个Verticle* <p>* This method is called by Vert.x when the instance is deployed. You do not call it yourself.** @param vertx  Vert.x实例* @param context 上下文*/void init(Vertx vertx, Context context);/*** Start the verticle instance.* 启动当前的verticle实例* <p>* Vert.x calls this method when deploying the instance. You do not call it yourself.* <p>* A promise is passed into the method, and when deployment is complete the verticle should either call* {@link io.vertx.core.Promise#complete} or {@link io.vertx.core.Promise#fail} the future.** @param startPromise  the future*/void start(Promise<Void> startPromise) throws Exception;/*** Stop the verticle instance.* 停止当前的verticle实例* <p>* Vert.x calls this method when un-deploying the instance. You do not call it yourself.* <p>* A promise is passed into the method, and when un-deployment is complete the verticle should either call* {@link io.vertx.core.Promise#complete} or {@link io.vertx.core.Promise#fail} the future.** @param stopPromise  the future*/void stop(Promise<Void> stopPromise) throws Exception;
}

在这里插入图片描述

Verticle编写

在日常的开发中,我们通常会采用继承AbstractVerticle抽象类的方式来实现自定义的Verticle,理论上来说,我们也可以通过实现Verticle接口的方式来定义一个Verticle实现类,但是开发人员通常会通过继承AbstractVerticle的方式来实现,因为它提供了所有Vert.x用户都会用到的事件处理程序、配置信息、执行流水线等。

Vert.x是一个库,而不是一个框架,所以你既可以选择从main方法中创建Vert.x实例,也可以选择从任何其它类中来创建,先创建好Vert.x,再部署Vertcle

一个Verticle的生命周期由start和stop事件组成,AbstractVerticle类提供了start和stop方法,我们可以覆盖它们

  • start:设置初始化工作,初始化事件处理程序,启动Http服务端口监听等

  • stop: 执行一些清理任务,例如关闭数据库连接,停止Http端口监听等

默认情况,这两个方法什么都不做

代码实例

import io.vertx.core.AbstractVerticle;
import io.vertx.core.Vertx;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class HelloVerticle extends AbstractVerticle {private final Logger logger = LoggerFactory.getLogger(HelloVerticle.class);//定义一个全局变量	private long counter = 1;@Overridepublic void start() {//定义一个每5秒执行一次的周期性任务vertx.setPeriodic(5000, id -> {logger.info("tick");});//定义一个HttpServer,监听8080端口vertx.createHttpServer().requestHandler(req -> {logger.info("Request #{} from {}", counter++, req.remoteAddress().host());req.response().end("Hello!");})//监听8080端口.listen(8080);logger.info("Open http://localhost:8080/");}public static void main(String[] args) {//实例化一个全局的Vert.x实例Vertx vertx = Vertx.vertx();//部署一个Verticlevertx.deployVerticle(new HelloVerticle());}
}

通过执行上面的实例代码,可以得出一个Verticle的重要属性:事件处理是在单个事件循环线程上进行的,从日志可以看出来,他们都是在 vert.x-eventloop-thread-0 这个线程上进行执行的,该设计的好处就在于在同一个线程上进行,减少了锁对性能的影响,同时减少了线程切换的开销,

代码实例交互过程

在这里插入图片描述

HelloVerticle调用Vert.x对象上的setPeriodic创建周期性任务处理程序,该对象又使用Vert.x定时器创建周期性任务,反过来,定时器定时回调HelloVerticle中的InternalTimerHandler处理程序

事件循环线程与Verticle的关系

import io.vertx.core.AbstractVerticle;
import io.vertx.core.Vertx;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class HelloVerticle extends AbstractVerticle {private final Logger logger = LoggerFactory.getLogger(HelloVerticle.class);private long counter = 1;public long delay;public int port;HelloVerticle (long delay, int port) {this.delay = delay;this.port = port;}public void start() {start(delay, port);}public void start(long delay, int port) {vertx.setPeriodic(delay, id -> {logger.info("tick : " + delay);});vertx.createHttpServer().requestHandler(req -> {logger.info("Request #{} from {}", counter++, req.remoteAddress().host());req.response().end("Hello!");}).listen(port);logger.info("Open http://localhost:" + port);}public static void main(String[] args) {Vertx vertx = Vertx.vertx();vertx.deployVerticle(new HelloVerticle(5000, 8080));vertx.deployVerticle(new HelloVerticle(3000, 8081));}
}

通过运行上面的服务并查看对应的日志,就会发现每一个Verticle实例对应了一个独立的事件循环线程

阻塞和事件循环

事件处理程序运行再事件循环线程上,所以这里需要特别注意的一个点:在事件循环的线程中执行的代码所用时间要尽可能短,这样事件循环才能有更高的吞吐量处理大量的事件。所以程序员不应该在事件循环线程中执行耗时太长的任务或进行阻塞IO的操作
下面通过一个代码实例来展示一下当我们故意阻塞事件循环线程后的情形:

public class BlockEventLoop extends AbstractVerticle {@Overridepublic void start() {//定义一个1000毫秒延时的定时器vertx.setTimer(1000, id -> {//进入死循环,模拟阻塞while (true);});}public static void main(String[] args) {Vertx vertx = Vertx.vertx();vertx.deployVerticle(new BlockEventLoop());}
}

运行结果:

WARN [vertx-blocked-thread-checker] BlockedThreadChecker - Thread Thread[vert.x-eventloop-thread-0,5,main] has been blocked for 2592 ms, time limit is 2000 ms
WARN [vertx-blocked-thread-checker] BlockedThreadChecker - Thread Thread[vert.x-eventloop-thread-0,5,main] has been blocked for 3596 ms, time limit is 2000 ms
WARN [vertx-blocked-thread-checker] BlockedThreadChecker - Thread Thread[vert.x-eventloop-thread-0,5,main] has been blocked for 4600 ms, time limit is 2000 ms
WARN [vertx-blocked-thread-checker] BlockedThreadChecker - Thread Thread[vert.x-eventloop-thread-0,5,main] has been blocked for 5603 ms, time limit is 2000 ms
io.vertx.core.VertxException: Thread blockedat app//chapter2.blocker.BlockEventLoop.lambda$start$0(BlockEventLoop.java:11)at app//chapter2.blocker.BlockEventLoop$$Lambda$78/0x00000008001a5040.handle(Unknown Source)at app//io.vertx.core.impl.VertxImpl$InternalTimerHandler.handle(VertxImpl.java:951)at app//io.vertx.core.impl.VertxImpl$InternalTimerHandler.handle(VertxImpl.java:918)at app//io.vertx.core.impl.EventLoopContext.emit(EventLoopContext.java:52)at app//io.vertx.core.impl.ContextImpl.emit(ContextImpl.java:294)at app//io.vertx.core.impl.EventLoopContext.emit(EventLoopContext.java:24)at app//io.vertx.core.impl.AbstractContext.emit(AbstractContext.java:49)at app//io.vertx.core.impl.EventLoopContext.emit(EventLoopContext.java:24)at app//io.vertx.core.impl.VertxImpl$InternalTimerHandler.run(VertxImpl.java:941)at app//io.netty.util.concurrent.PromiseTask.runTask(PromiseTask.java:98)at app//io.netty.util.concurrent.ScheduledFutureTask.run(ScheduledFutureTask.java:170)at app//io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:164)at app//io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:472)at app//io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:500)at app//io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)at app//io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)at app//io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)at java.base@11.0.21/java.lang.Thread.run(Thread.java:829)
WARN [vertx-blocked-thread-checker] BlockedThreadChecker - Thread Thread[vert.x-eventloop-thread-0,5,main] has been blocked for 6605 ms, time limit is 2000 ms
io.vertx.core.VertxException: Thread blockedat app//chapter2.blocker.BlockEventLoop.lambda$start$0(BlockEventLoop.java:11)at app//chapter2.blocker.BlockEventLoop$$Lambda$78/0x00000008001a5040.handle(Unknown Source)at app//io.vertx.core.impl.VertxImpl$InternalTimerHandler.handle(VertxImpl.java:951)at app//io.vertx.core.impl.VertxImpl$InternalTimerHandler.handle(VertxImpl.java:918)at app//io.vertx.core.impl.EventLoopContext.emit(EventLoopContext.java:52)at app//io.vertx.core.impl.ContextImpl.emit(ContextImpl.java:294)at app//io.vertx.core.impl.EventLoopContext.emit(EventLoopContext.java:24)at app//io.vertx.core.impl.AbstractContext.emit(AbstractContext.java:49)at app//io.vertx.core.impl.EventLoopContext.emit(EventLoopContext.java:24)at app//io.vertx.core.impl.VertxImpl$InternalTimerHandler.run(VertxImpl.java:941)at app//io.netty.util.concurrent.PromiseTask.runTask(PromiseTask.java:98)at app//io.netty.util.concurrent.ScheduledFutureTask.run(ScheduledFutureTask.java:170)at app//io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:164)at app//io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:472)at app//io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:500)at app//io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)at app//io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)at app//io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)at java.base@11.0.21/java.lang.Thread.run(Thread.java:829)

当事件处理程序进入阻塞状态后,日志开始输出告警信息,现在开始事件循环线程无法处理其它的事件啦,经过几轮告警(默认设置为5秒)后,就开始打印线程堆栈信息啦,但是需要注意的是,这些只是警告信息,事件循环线程的检查器并不能终止这些阻塞的操作

线程阻塞检查器配置

默认情况,事件循环线程阻塞器发出告警的时间2秒,打印线程堆栈信息的时间是5秒,根据不同设备的处理能力,该时间可以进行灵活的配置

  • -Dvertx.options.blockedThreadCheckInterval = 1000 //检查器阈值设置为1秒
  • -Dvertx.threadChecks=false //禁用线程阻塞器

生命周期事件异步通知

Verticle中的start和stop方法被称作生命周期方法,该方法在定义时是没有返回值的,根据约定,除非该方法在调用过程中发生了异常,否则则认为该调用是成功的,但是在start和stop方法中可能存在一些异步的操作,那我们如何来监听这些异步操作的成功与失败呢,下面我们就来介绍一下Promise

Vert.x的Promise是对Future-Promise模型的适配,这是一个用于处理异步结果的机制。Promise用来写入异步结果,而Future用来查看异步结果。可调用一个Promise对象的future方法以取得Vert.x中的Future类型的Future对象

package chapter2.future;import io.vertx.core.AbstractVerticle;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;public class FutureVerticle extends AbstractVerticle {@Overridepublic void start(Promise<Void> promise) {   // Promise的类型是Void,因为Vert.x只关心部署是否成功,不关心带了什么参数vertx.createHttpServer().requestHandler(req -> req.response().end("Ok")).listen(8080, ar -> {if (ar.succeeded()) {       // 这个listen函数支持异步回调,通过它就知道了结果是成功还是失败promise.complete();   // complete函数用于将promise标记为以完成状态,如果Promise的对象类型不是Void,complete函数也可以传递值} else {promise.fail(ar.cause()); // 如果listen操作失败,将promise标记为失败,并传递一个异常信息}});System.out.println("start执行完成");}public static void main(String[] args) {Vertx vertx = Vertx.vertx();//定义一个异步的回调,来监测start方法的执行情况vertx.deployVerticle(new FutureVerticle(), h -> {if (h.succeeded()) {System.out.println("成功");} else {System.out.println("失败:" + h.cause().getMessage());}});}
}

日常开发中尽量使用有回调参数的异步方法,这样当发生错误的时候,我们就可以得到通知

Verticle配置

应用程序启动的时候经常需要传入不同的配置参数,我们通过Vert.x来部署Verticle的时候也可以进行参数传递和配置,配置数据需要以JSON格式来进行传递,次数使用Vert.x中JsonObject和JsonArray类中的具体API

配置传递示例代码

public class ConfigVerticle extends AbstractVerticle {private final Logger logger = LoggerFactory.getLogger(ConfigVerticle.class);@Overridepublic void start() {logger.info("n = {}", config().getInteger("n", -1));}public static void main(String[] args) {Vertx vertx = Vertx.vertx();for (int n = 0; n < 4; n++) {JsonObject conf = new JsonObject().put("n", n);DeploymentOptions opts = new DeploymentOptions() //该对象可以让我们更多的控制部署过程.setConfig(conf).setInstances(n); // 该配置表示一次部署了Verticle多个实例vertx.deployVerticle("xxx.ConfigVerticle", opts); //当我们一次性部署Verticle多个实例时,该处需要用类的全名;如果是部署单个实例,则可以用全名或者new}}
}

Verticle部署

上面的示例中,我们通过在Verticle内嵌main方法来完成Verticle的部署,部署都是通过Vert.x对象来进行的,一个由多个Verticle组成的应用程序的典型部署方式是这样的:

  • 先部署一个主Verticle
  • 主Verticle再部署其它的Verticle
  • 被部署的Verticle可以继续部署更多的Verticle

这样的部署方式可能让我们感觉这些Verticle是存在层级关系的,实际上,这些Verticle在运行中不存在父子的概念

部署示例

待部署Verticle
public class BaseVerticle extends AbstractVerticle {private final Logger logger = LoggerFactory.getLogger(BaseVerticle.class);@Overridepublic void start() {logger.info("Start");}@Overridepublic void stop() {logger.info("Stop");}
专门用于部署Verticle的部署工具
public class Deployer extends AbstractVerticle {private final Logger logger = LoggerFactory.getLogger(Deployer.class);@Overridepublic void start() {long delay = 1000;for (int i = 0; i < 50; i++) {vertx.setTimer(delay, id -> deploy());  // 每隔1秒部署一个 BaseVerticle 实例delay = delay + 1000;}}private void deploy() {vertx.deployVerticle(new BaseVerticle(), ar -> {   // vertx中的Vertcle的部署是一个异步的操作,所以这里使用带异步结果的异步方法来进行部署,部署成功会生成一个唯一的Verticle IDif (ar.succeeded()) {String id = ar.result();logger.info("Successfully deployed {}", id);vertx.setTimer(5000, tid -> undeployLater(id));   // Verticle部署成功5秒以后就卸载该实例} else {logger.error("Error while deploying", ar.cause());}});}private void undeployLater(String id) {vertx.undeploy(id, ar -> {  //卸载的过程和部署的过程类似,也是一个异步的操作if (ar.succeeded()) {logger.info("{} was undeployed", id);} else {logger.error("{} could not be undeployed", id);}});}
}
主启动类

public class Main {public static void main(String[] args) {Vertx vertx = Vertx.vertx();vertx.deployVerticle(new Deployer());}
}

该方法也可以直接放在 Deployer 中,放在外面层次更分明一些

线程启动日志分析
线程ID操作行为
vert.x-eventloop-thread-2EmptyVerticle - Start
vert.x-eventloop-thread-0Deployer - Successfully deployed 7186663f-10a6-4cdc-985a-d3357fe240db
vert.x-eventloop-thread-3EmptyVerticle - Start
vert.x-eventloop-thread-0Deployer - Successfully deployed 88ba21cb-e8c1-403b-bf29-edde7c1f2ebc
vert.x-eventloop-thread-4EmptyVerticle - Start
vert.x-eventloop-thread-0Deployer - Successfully deployed e1ccd570-c631-4035-b7d2-7b06bbc3c326
vert.x-eventloop-thread-5EmptyVerticle - Start
vert.x-eventloop-thread-0Deployer - Successfully deployed 8ab2b0f5-856f-498b-8a50-0622a65233cc
vert.x-eventloop-thread-6EmptyVerticle - Start
vert.x-eventloop-thread-0Deployer - Successfully deployed 1191576b-a3c1-49bb-a882-29cdc041a9c2
vert.x-eventloop-thread-7EmptyVerticle - Start
vert.x-eventloop-thread-0Deployer - Successfully deployed 12512cf1-3ad7-41c6-82e0-bd1478524c73
vert.x-eventloop-thread-2EmptyVerticle - Stop
vert.x-eventloop-thread-0Deployer - 7186663f-10a6-4cdc-985a-d3357fe240db was undeployed
vert.x-eventloop-thread-1EmptyVerticle - Start
vert.x-eventloop-thread-0Deployer - Successfully deployed cd6fbb87-10fb-46da-b1c5-710a4d0e305c
vert.x-eventloop-thread-3EmptyVerticle - Stop
vert.x-eventloop-thread-0Deployer - 88ba21cb-e8c1-403b-bf29-edde7c1f2ebc was undeployed
vert.x-eventloop-thread-0EmptyVerticle - Start
vert.x-eventloop-thread-0Deployer - Successfully deployed a574512e-509a-45cb-968c-4ae6c89f7ca2
vert.x-eventloop-thread-2EmptyVerticle - Start

通过上面的日志,我们可以得到这样的一些结论:

  • 针对于不同的Verticle,并不是有多少个Verticle就会有多少个线程,默认情况下,Vert.x创建的事件循环线程的数量是CPU核心数的2倍,比如说:CPU是4核,那么Vert.x默认会创建8个事件循环线程。Verticle会被轮流分配给各事件循环
  • 一个Verticle总是运行在同一个事件循环线程上,而一个事件循环线程会被多个Verticle共享,这种设计使应用程序运行时的线程数量是可以预测的

开发人员可以调整事件循环的数量,但是无法手动指定一个Verticle应该分配到那个事件循环

Vert.x相关笔记

  • 异步变成与响应式系统
  • 什么是Vert.x

在这里插入图片描述

相关文章:

Vert.x学习笔记-Vert.x的基本处理单元Verticle

Verticle介绍 Verticle是Vert.x的基本处理单元&#xff0c;Vert.x应用程序中存在着处理各种事件的处理单元&#xff0c;比如负责HTTP API响应请求的处理单元、负责数据库存取的处理单元、负责向第三方发送请求的处理单元。Verticle就是对这些功能单元的封装&#xff0c;Vertic…...

干货分享:基于 LSTM 的广告库存预估算法

近年来&#xff0c;随着互联网的发展&#xff0c;在线广告营销成为一种非常重要的商业模式。出于广告流量商业化售卖和日常业务投放精细化运营的目的&#xff0c;需要对广告流量进行更精准的预估&#xff0c;从而更精细的进行广告库存管理。 因此&#xff0c;携程广告纵横平台…...

dataframe删除某一列

drop import pandas as pd data {‘A’: [1, 2, 3], ‘B’: [4, 5, 6], ‘C’: [7, 8, 9]} df pd.DataFrame(data) #使用drop方法删除列 df df.drop(‘B’, axis1) # 通过指定列名和axis1来删除列 del import pandas as pd data {‘A’: [1, 2, 3], ‘B’: [4, 5, 6]…...

提升ChatGPT答案质量和准确性的方法Prompt engineering

文章目录 怎么获得优质的答案设计一个优质prompt的步骤:Prompt公式:示例怎么获得优质的答案 影响模型回答精确度的因素 我们应该知道一个好的提示词,要具备一下要点: 清晰简洁,不要有歧义; 有明确的任务/问题,任务如果太复杂,需要拆分成子任务分步完成; 确保prompt中…...

SpringBoot + Vue2项目打包部署到服务器后,使用Nginx配置SSL证书,配置访问HTTP协议转HTTPS协议

配置nginx.conf文件&#xff0c;这个文件一般在/etc/nginx/...中&#xff0c;由于每个人的体质不一样&#xff0c;也有可能在别的路径里&#xff0c;自己找找... # 配置工作进程的最大连接数 events {worker_connections 1024; }# 配置HTTP服务 http {# 导入mime.types配置文件…...

HTML 表格

<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>表格标签</title>/* <style>.yun {widt…...

AIGC(生成式AI)试用 10 -- 安全性问题

上次遗留的问题&#xff1a;代码的安全性呢&#xff1f;下次找几个问题测试下看。 AI&#xff0c;你安全吗&#xff1f; AI生成的程序&#xff0c;安全吗&#xff1f; 也许这个世界最难做的事就是自己测试自己&#xff1a;测试什么&#xff1f;如何测&#xff1f; …...

STM32循迹小车原理介绍和代码示例

目录 1. 循迹模块介绍 2. 循迹小车原理 3. 循迹小车核心代码 4. 循迹小车解决转弯平滑问题 1. 循迹模块介绍 TCRT5000传感器的红外发射二极管不断发射红外线当发射出的红外线没有被反射回来或被反射回来但强度不够大时红外接收管一直处于关断状态&#xff0c;此时模块的输出…...

Nginx 配置详细讲解

Nginx.conf 配置文件分为三部分&#xff0c;分别为main块、events块、http块&#xff08;http块又包含server块和location块&#xff09;&#xff0c;如下图。 第一部分&#xff1a;main块(全局块) main块主要是设置一些影响Nginx服务器整体运行的配置指令&#xff0c;主要包括…...

gdb 日志记录不显示到屏幕的方法(gdb13最新版)

tags: gdb categories: [Debug] 写在前面 gdb 的更新好快啊… 之前的选项都有改动了, 比如 logging… 需要屏幕重定向不能简单设置: set logging on set logging redirect on了, 而是要多开一个配置, 踩坑了 方法 在此之前先看一下我的 gdbinit 配置: set debuginfod e…...

JAVA智慧工地管理系统源码基于微服务

智慧工地是将互联网的理念和科技引入施工现场&#xff0c;从施工现场源头抓起&#xff0c;大程度的收集人员、安全、环境、质量等关键业务数据。通过结合物联网、大数据、互联网、云计算等技术建立云端大数据管理平台&#xff0c;形成端云大数据的体系与模式&#xff0c;这就是…...

学习笔记三十四:Ingress和 Ingress Controller概述

Ingress和 Ingress Controller概述 回顾service四层负载在k8s中为什么要做负载均衡Service不足之处四层负载和七层负载的区别OSI七层模型&#xff1a; Ingress介绍Ingress Controller介绍Ingress-controller 作用Ingress和Ingress Controller总结使用Ingress Controller代理k8s…...

Webpack的Tree Shaking。它的作用是什么?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…...

研发效能DevOps: Git安装

目录 一、理论 1.Git 2.Git 工具 二、实验 1.Git安装 2.配置Git 3. VS Code加载Git 一、理论 1.Git &#xff08;1&#xff09;简介 Git 是一个分布式版本控制及源代码管理工具;Git 可以为你的项目保存若干快照&#xff0c;以此来对整个项目进行版本管理。 Git 是一个…...

ZZ038 物联网应用与服务赛题第D套

2023年全国职业院校技能大赛 中职组 物联网应用与服务 任 务 书 (D卷) 赛位号:______________ 竞赛须知 一、注意事项 1.检查硬件设备、电脑设备是否正常。检查竞赛所需的各项设备、软件和竞赛材料等; 2.竞赛任务中所使用的各类软件工具、软件安装文件等,都…...

基于STM32设计的室内环境监测系统(华为云IOT)_2023

一、设计需求 基于STM32+华为云物联网平台设计一个室内环境监测系统,以STM32系列单片机为主控器件,采集室内温湿度、空气质量、光照强度等环境参数,将采集的数据结果在本地通过LCD屏幕显示,同时上传到华为云平台并将上传的数据在Android移动端能够实时显示、查看。 【1…...

UE5C++学习(一)--- 增强输入系统

一、关于增强输入系统的介绍 增强输入系统官方文档介绍 二、增强输入系统的具体使用 注&#xff1a;在使用方面&#xff0c;不会介绍如何创建项目等基础操作&#xff0c;如果还没有UE的使用基础&#xff0c;可以参考一下我之前UE4的文章&#xff0c;操作差别不会很大。 如上…...

好物周刊#29:项目管理软件

https://github.com/cunyu1943/JavaPark https://yuque.com/cunyu1943 村雨遥的好物周刊&#xff0c;记录每周看到的有价值的信息&#xff0c;主要针对计算机领域&#xff0c;每周五发布。 一、项目 1. HelloGithub 分享 GitHub 上有趣、入门级的开源项目。每月 28 号以月刊…...

玻色量子“天工量子大脑”亮相中关村论坛,大放异彩

2023年5月25日至30日&#xff0c;2023中关村论坛&#xff08;科博会&#xff09;在北京盛大召开。中关村论坛&#xff08;科博会&#xff09;是面向全球科技创新交流合作的国家级平台行业盛会&#xff0c;由科技部、国家发展改革委、工业和信息化部、国务院国资委、中国科学院、…...

使用Gorm进行高级查询

深入探讨GORM的高级查询功能&#xff0c;轻松实现Go中的数据检索 高效的数据检索是每个应用程序性能的核心。GORM&#xff0c;强大的Go对象关系映射库&#xff0c;不仅扩展到基本的CRUD操作&#xff0c;还提供了高级的查询功能。本文是您掌握使用GORM进行高级查询的综合指南。…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

《Playwright:微软的自动化测试工具详解》

Playwright 简介:声明内容来自网络&#xff0c;将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具&#xff0c;支持 Chrome、Firefox、Safari 等主流浏览器&#xff0c;提供多语言 API&#xff08;Python、JavaScript、Java、.NET&#xff09;。它的特点包括&a…...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

ServerTrust 并非唯一

NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

微服务商城-商品微服务

数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解

本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…...

在Ubuntu24上采用Wine打开SourceInsight

1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...