rocketmq-netty通信设计-request和response

1、NettyRemotingServer启动分析

org.apache.rocketmq.remoting.netty.NettyRemotingServer#start

public void start() {this.defaultEventExecutorGroup = new DefaultEventExecutorGroup(nettyServerConfig.getServerWorkerThreads(),new ThreadFactory() {private AtomicInteger threadIndex = new AtomicInteger(0);@Overridepublic Thread newThread(Runnable r) {return new Thread(r, "NettyServerCodecThread_" + this.threadIndex.incrementAndGet());}});prepareSharableHandlers();ServerBootstrap childHandler =this.serverBootstrap.group(this.eventLoopGroupBoss, this.eventLoopGroupSelector).channel(useEpoll() ? EpollServerSocketChannel.class : NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, nettyServerConfig.getServerSocketBacklog()).option(ChannelOption.SO_REUSEADDR, true).option(ChannelOption.SO_KEEPALIVE, false).childOption(ChannelOption.TCP_NODELAY, true).localAddress(new InetSocketAddress(this.nettyServerConfig.getListenPort())).childHandler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(defaultEventExecutorGroup, HANDSHAKE_HANDLER_NAME, handshakeHandler).addLast(defaultEventExecutorGroup,encoder,new NettyDecoder(),new IdleStateHandler(0, 0, nettyServerConfig.getServerChannelMaxIdleTimeSeconds()),connectionManageHandler,serverHandler);}});

ServerBootstrap是netty作为服务端的启动入口。首先绑定了两个线程池。也就是boss线程池、worker线程池。
这里分析,也就是eventLoopGroupBoss和eventLoopGroupSelector

ServerBootstrap启动分析

查看初始化源码

if (useEpoll()) {this.eventLoopGroupBoss = new EpollEventLoopGroup(1, new ThreadFactory() {private AtomicInteger threadIndex = new AtomicInteger(0);@Overridepublic Thread newThread(Runnable r) {return new Thread(r, String.format("NettyEPOLLBoss_%d", this.threadIndex.incrementAndGet()));}});this.eventLoopGroupSelector = new EpollEventLoopGroup(nettyServerConfig.getServerSelectorThreads(), new ThreadFactory() {private AtomicInteger threadIndex = new AtomicInteger(0);private int threadTotal = nettyServerConfig.getServerSelectorThreads();@Overridepublic Thread newThread(Runnable r) {return new Thread(r, String.format("NettyServerEPOLLSelector_%d_%d", threadTotal, this.threadIndex.incrementAndGet()));}});
} else {this.eventLoopGroupBoss = new NioEventLoopGroup(1, new ThreadFactory() {private AtomicInteger threadIndex = new AtomicInteger(0);@Overridepublic Thread newThread(Runnable r) {return new Thread(r, String.format("NettyNIOBoss_%d", this.threadIndex.incrementAndGet()));}});this.eventLoopGroupSelector = new NioEventLoopGroup(nettyServerConfig.getServerSelectorThreads(), new ThreadFactory() {private AtomicInteger threadIndex = new AtomicInteger(0);private int threadTotal = nettyServerConfig.getServerSelectorThreads();@Overridepublic Thread newThread(Runnable r) {return new Thread(r, String.format("NettyServerNIOSelector_%d_%d", threadTotal, this.threadIndex.incrementAndGet()));}});
}

不同平台,实现方式不一样。linux上会使用Epoll,其他会使用java提供的跨平台的NIO
eventLoopGroupBoss默认线程数是1
eventLoopGroupSelector默认线程数是3

再看defaultEventExecutorGroup。
.childHandler()方式指定了channelHandle的线程池。看线程名称NettyServerCodecThread_,可猜测该线程池主要功能是做编码解码。

  • .addLast(defaultEventExecutorGroup, HANDSHAKE_HANDLER_NAME, handshakeHandler)
    目的是做TSL的加解密
  • .addLast(defaultEventExecutorGroup,
    encoder,
    new NettyDecoder(),
    new IdleStateHandler(0, 0, nettyServerConfig.getServerChannelMaxIdleTimeSeconds()),
    connectionManageHandler,
    serverHandler
    );

encoder是做编码。将本地msg转成byte[],传输到远端
NettyDecoder是做解码,将远端byte[]解码成本地msg。这里比较关键。将请求体转换成RemotingCommand类。详情看org.apache.rocketmq.remoting.netty.NettyDecoder#decode

connectionManageHandler 看代码可知,是channel有特定事件时打印日志。注册、取消注册、激活、取消激活等
serverHandler就是业务的request、response处理类。处理RemotingCommand消息

class NettyServerHandler extends SimpleChannelInboundHandler<RemotingCommand> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, RemotingCommand msg) throws Exception {processMessageReceived(ctx, msg);}}

查看NettyServerHandler 的处理流程

org.apache.rocketmq.remoting.netty.NettyRemotingAbstract#processRequestCommand

public void processRequestCommand(final ChannelHandlerContext ctx, final RemotingCommand cmd) {final Pair<NettyRequestProcessor, ExecutorService> matched = this.processorTable.get(cmd.getCode());final Pair<NettyRequestProcessor, ExecutorService> pair = null == matched ? this.defaultRequestProcessor : matched;final int opaque = cmd.getOpaque();if (pair != null) {Runnable run = new Runnable() {@Overridepublic void run() {try {String remoteAddr = RemotingHelper.parseChannelRemoteAddr(ctx.channel());doBeforeRpcHooks(remoteAddr, cmd);final RemotingResponseCallback callback = new RemotingResponseCallback() {@Overridepublic void callback(RemotingCommand response) {doAfterRpcHooks(remoteAddr, cmd, response);if (!cmd.isOnewayRPC()) {if (response != null) {response.setOpaque(opaque);response.markResponseType();response.setSerializeTypeCurrentRPC(cmd.getSerializeTypeCurrentRPC());try {ctx.writeAndFlush(response);} catch (Throwable e) {log.error("process request over, but response failed", e);log.error(cmd.toString());log.error(response.toString());}} else {}}}};if (pair.getObject1() instanceof AsyncNettyRequestProcessor) {AsyncNettyRequestProcessor processor = (AsyncNettyRequestProcessor)pair.getObject1();processor.asyncProcessRequest(ctx, cmd, callback);} else {NettyRequestProcessor processor = pair.getObject1();RemotingCommand response = processor.processRequest(ctx, cmd);callback.callback(response);}} catch (Throwable e) {log.error("process request exception", e);log.error(cmd.toString());if (!cmd.isOnewayRPC()) {final RemotingCommand response = RemotingCommand.createResponseCommand(RemotingSysResponseCode.SYSTEM_ERROR,RemotingHelper.exceptionSimpleDesc(e));response.setOpaque(opaque);ctx.writeAndFlush(response);}}}};if (pair.getObject1().rejectRequest()) {final RemotingCommand response = RemotingCommand.createResponseCommand(RemotingSysResponseCode.SYSTEM_BUSY,"[REJECTREQUEST]system busy, start flow control for a while");response.setOpaque(opaque);ctx.writeAndFlush(response);return;}try {final RequestTask requestTask = new RequestTask(run, ctx.channel(), cmd);pair.getObject2().submit(requestTask);} catch (RejectedExecutionException e) {if ((System.currentTimeMillis() % 10000) == 0) {log.warn(RemotingHelper.parseChannelRemoteAddr(ctx.channel())+ ", too many requests and system thread pool busy, RejectedExecutionException "+ pair.getObject2().toString()+ " request code: " + cmd.getCode());}

对于request,会根据cmd.getCode()来获取对应的NettyRequestProcessor。找不到的话,会找默认的。对于nameserver,它就是设置了一个默认,处理所有的请求。对于broker,有多个NettyRequestProcessor。

NettyRequestProcessor注册到org.apache.rocketmq.remoting.netty.NettyRemotingAbstract#processorTable时,会绑定一个线程池
Pair<NettyRequestProcessor, ExecutorService>。RemotingCommand任务提交给该线程池处理

/*** This container holds all processors per request code, aka, for each incoming request, we may look up the* responding processor in this map to handle the request.*/protected final HashMap<Integer/* request code */, Pair<NettyRequestProcessor, ExecutorService>> processorTable =new HashMap<Integer, Pair<NettyRequestProcessor, ExecutorService>>(64);

总结服务端启动后netty的线程模型

默认情况下:
eventLoopGroupBoss线程池1个线程,负责channel的链接
eventLoopGroupSelector线程池3个线程,负责IO处理
defaultEventExecutorGroup线程池8个线程,负责请求的RemotingCommand编解码
NettyRequestProcessor绑定的ExecutorService,默认8个线程,负责处理RemotingCommand的业务处理

证据如下。以下是nameServer的启动线程截图。RemotingExecutorThread是NettyRequestProcessor绑定的
在这里插入图片描述

2、NettyRemotingClient启动分析

查看启动代码
org.apache.rocketmq.remoting.netty.NettyRemotingClient#start

public void start() {this.defaultEventExecutorGroup = new DefaultEventExecutorGroup(nettyClientConfig.getClientWorkerThreads(),new ThreadFactory() {private AtomicInteger threadIndex = new AtomicInteger(0);@Overridepublic Thread newThread(Runnable r) {return new Thread(r, "NettyClientWorkerThread_" + this.threadIndex.incrementAndGet());}});Bootstrap handler = this.bootstrap.group(this.eventLoopGroupWorker).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true).option(ChannelOption.SO_KEEPALIVE, false).option(ChannelOption.CONNECT_TIMEOUT_MILLIS, nettyClientConfig.getConnectTimeoutMillis()).handler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();if (nettyClientConfig.isUseTLS()) {if (null != sslContext) {pipeline.addFirst(defaultEventExecutorGroup, "sslHandler", sslContext.newHandler(ch.alloc()));log.info("Prepend SSL handler");} else {log.warn("Connections are insecure as SSLContext is null!");}}pipeline.addLast(defaultEventExecutorGroup,new NettyEncoder(),new NettyDecoder(),new IdleStateHandler(0, 0, nettyClientConfig.getClientChannelMaxIdleTimeSeconds()),new NettyConnectManageHandler(),new NettyClientHandler());}});if (nettyClientConfig.getClientSocketSndBufSize() > 0) {log.info("client set SO_SNDBUF to {}", nettyClientConfig.getClientSocketSndBufSize());handler.option(ChannelOption.SO_SNDBUF, nettyClientConfig.getClientSocketSndBufSize());}if (nettyClientConfig.getClientSocketRcvBufSize() > 0) {log.info("client set SO_RCVBUF to {}", nettyClientConfig.getClientSocketRcvBufSize());handler.option(ChannelOption.SO_RCVBUF, nettyClientConfig.getClientSocketRcvBufSize());}if (nettyClientConfig.getWriteBufferLowWaterMark() > 0 && nettyClientConfig.getWriteBufferHighWaterMark() > 0) {log.info("client set netty WRITE_BUFFER_WATER_MARK to {},{}",nettyClientConfig.getWriteBufferLowWaterMark(), nettyClientConfig.getWriteBufferHighWaterMark());handler.option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(nettyClientConfig.getWriteBufferLowWaterMark(), nettyClientConfig.getWriteBufferHighWaterMark()));}this.timer.scheduleAtFixedRate(new TimerTask() {@Overridepublic void run() {try {NettyRemotingClient.this.scanResponseTable();} catch (Throwable e) {log.error("scanResponseTable exception", e);}}}, 1000 * 3, 1000);if (this.channelEventListener != null) {this.nettyEventExecutor.start();}}

从Bootstrap的创建可知,和Server是很类似的。
区别是Bootstrap没有selector线程池。业务处理类是NettyClientHandler

3、Product发送消息,从namesrv获取topic的路由信息

Product发送端分析、编码

用rocketmq源码的例子,稍作变更:
org/apache/rocketmq/example/quickstart/Producer.java

producer.start();for (int i = 0; i < MESSAGE_COUNT; i++) {try {/** Create a message instance, specifying topic, tag and message body.*/Message msg = new Message(TOPIC /* Topic */,TAG /* Tag */,("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message body */);/** Call send message to deliver message to one of brokers.*/SendResult sendResult = producer.send(msg);

producer.send(msg)最终会调用如下方法
org.apache.rocketmq.client.impl.MQClientAPIImpl#sendMessageSync
org.apache.rocketmq.remoting.netty.NettyRemotingAbstract#invokeSyncImpl

private SendResult sendMessageSync(final String addr,final String brokerName,final Message msg,final long timeoutMillis,final RemotingCommand request
) throws RemotingException, MQBrokerException, InterruptedException {RemotingCommand response = this.remotingClient.invokeSync(addr, request, timeoutMillis);assert response != null;return this.processSendResponse(brokerName, msg, response, addr);
}
public RemotingCommand invokeSyncImpl(final Channel channel, final RemotingCommand request,final long timeoutMillis)throws InterruptedException, RemotingSendRequestException, RemotingTimeoutException {final int opaque = request.getOpaque();try {final ResponseFuture responseFuture = new ResponseFuture(channel, opaque, timeoutMillis, null, null);this.responseTable.put(opaque, responseFuture);final SocketAddress addr = channel.remoteAddress();channel.writeAndFlush(request).addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture f) throws Exception {if (f.isSuccess()) {responseFuture.setSendRequestOK(true);return;} else {responseFuture.setSendRequestOK(false);}responseTable.remove(opaque);responseFuture.setCause(f.cause());responseFuture.putResponse(null);log.warn("send a request command to channel <" + addr + "> failed.");}});RemotingCommand responseCommand = responseFuture.waitResponse(timeoutMillis);if (null == responseCommand) {if (responseFuture.isSendRequestOK()) {throw new RemotingTimeoutException(RemotingHelper.parseSocketAddressAddr(addr), timeoutMillis,responseFuture.getCause());} else {throw new RemotingSendRequestException(RemotingHelper.parseSocketAddressAddr(addr), responseFuture.getCause());}}return responseCommand;} finally {this.responseTable.remove(opaque);}}

将msg包装成RemotingCommand,channel是namesrv服务端的远程地址
源码所见,channel.writeAndFlush(request)。将请求发送到namesrv

发送前,还需要做编码处理。下图是debug下,编码的处理。请求是105
org.apache.rocketmq.common.protocol.RequestCode#GET_ROUTEINFO_BY_TOPIC
public static final int GET_ROUTEINFO_BY_TOPIC = 105;

org.apache.rocketmq.remoting.netty.NettyEncoder#encode
在这里插入图片描述

namesrv端解码,消息处理

接收到105请求。namesrv做解码操作。消息体转换成RemotingCommand处理
org.apache.rocketmq.remoting.netty.NettyDecoder#decode
io.netty.handler.codec.LengthFieldBasedFrameDecoder#decode
在这里插入图片描述
org.apache.rocketmq.remoting.netty.NettyRemotingServer.NettyServerHandler#channelRead0处理
class NettyServerHandler extends SimpleChannelInboundHandler {

    @Overrideprotected void channelRead0(ChannelHandlerContext ctx, RemotingCommand msg) throws Exception {processMessageReceived(ctx, msg);}
}

org.apache.rocketmq.remoting.netty.NettyRemotingAbstract#processMessageReceived
在这里插入图片描述
通用的一个请求命令处理
org.apache.rocketmq.remoting.netty.NettyRemotingAbstract#processRequestCommand

public void processRequestCommand(final ChannelHandlerContext ctx, final RemotingCommand cmd) {final Pair<NettyRequestProcessor, ExecutorService> matched = this.processorTable.get(cmd.getCode());final Pair<NettyRequestProcessor, ExecutorService> pair = null == matched ? this.defaultRequestProcessor : matched;final int opaque = cmd.getOpaque();if (pair != null) {Runnable run = new Runnable() {@Overridepublic void run() {try {String remoteAddr = RemotingHelper.parseChannelRemoteAddr(ctx.channel());doBeforeRpcHooks(remoteAddr, cmd);final RemotingResponseCallback callback = new RemotingResponseCallback() {@Overridepublic void callback(RemotingCommand response) {doAfterRpcHooks(remoteAddr, cmd, response);if (!cmd.isOnewayRPC()) {if (response != null) {response.setOpaque(opaque);response.markResponseType();response.setSerializeTypeCurrentRPC(cmd.getSerializeTypeCurrentRPC());try {ctx.writeAndFlush(response);} catch (Throwable e) {log.error("process request over, but response failed", e);log.error(cmd.toString());log.error(response.toString());}} else {}}}};if (pair.getObject1() instanceof AsyncNettyRequestProcessor) {AsyncNettyRequestProcessor processor = (AsyncNettyRequestProcessor)pair.getObject1();processor.asyncProcessRequest(ctx, cmd, callback);} else {NettyRequestProcessor processor = pair.getObject1();RemotingCommand response = processor.processRequest(ctx, cmd);callback.callback(response);}} catch (Throwable e) {log.error("process request exception", e);log.error(cmd.toString());if (!cmd.isOnewayRPC()) {final RemotingCommand response = RemotingCommand.createResponseCommand(RemotingSysResponseCode.SYSTEM_ERROR,RemotingHelper.exceptionSimpleDesc(e));response.setOpaque(opaque);ctx.writeAndFlush(response);}}}};if (pair.getObject1().rejectRequest()) {final RemotingCommand response = RemotingCommand.createResponseCommand(RemotingSysResponseCode.SYSTEM_BUSY,"[REJECTREQUEST]system busy, start flow control for a while");response.setOpaque(opaque);ctx.writeAndFlush(response);return;}try {final RequestTask requestTask = new RequestTask(run, ctx.channel(), cmd);pair.getObject2().submit(requestTask);} catch (RejectedExecutionException e) {if ((System.currentTimeMillis() % 10000) == 0) {log.warn(RemotingHelper.parseChannelRemoteAddr(ctx.channel())+ ", too many requests and system thread pool busy, RejectedExecutionException "+ pair.getObject2().toString()+ " request code: " + cmd.getCode());}if (!cmd.isOnewayRPC()) {final RemotingCommand response = RemotingCommand.createResponseCommand(RemotingSysResponseCode.SYSTEM_BUSY,"[OVERLOAD]system busy, start flow control for a while");response.setOpaque(opaque);ctx.writeAndFlush(response);}}} else {String error = " request type " + cmd.getCode() + " not supported";final RemotingCommand response =RemotingCommand.createResponseCommand(RemotingSysResponseCode.REQUEST_CODE_NOT_SUPPORTED, error);response.setOpaque(opaque);ctx.writeAndFlush(response);log.error(RemotingHelper.parseChannelRemoteAddr(ctx.channel()) + error);}}

processRequestCommand就会通用的处理请求,转发到NettyRequestProcessor处理,
并且用绑定的ExecutorService线程池去执行任务---->pair.getObject2().submit(requestTask);

响应结果,会调用callback处理
ctx.writeAndFlush(response);

namesrv启动时注册NettyRequestProcessor、ExecutorService

org.apache.rocketmq.namesrv.NamesrvController#registerProcessor

private void registerProcessor() {if (namesrvConfig.isClusterTest()) {this.remotingServer.registerDefaultProcessor(new ClusterTestRequestProcessor(this, namesrvConfig.getProductEnvName()),this.remotingExecutor);} else {this.remotingServer.registerDefaultProcessor(new DefaultRequestProcessor(this), this.remotingExecutor);}}

remotingExecutor的初始化

 this.remotingExecutor =Executors.newFixedThreadPool(nettyServerConfig.getServerWorkerThreads(), new ThreadFactoryImpl("RemotingExecutorThread_"));

如上所说,processRequestCommand找到DefaultRequestProcessor,执行到以下位置
org.apache.rocketmq.namesrv.processor.DefaultRequestProcessor#processRequest
在这里插入图片描述

再贴一下处理topic获取routInfo源码
org.apache.rocketmq.namesrv.processor.DefaultRequestProcessor#getRouteInfoByTopic

public RemotingCommand getRouteInfoByTopic(ChannelHandlerContext ctx,RemotingCommand request) throws RemotingCommandException {final RemotingCommand response = RemotingCommand.createResponseCommand(null);final GetRouteInfoRequestHeader requestHeader =(GetRouteInfoRequestHeader) request.decodeCommandCustomHeader(GetRouteInfoRequestHeader.class);TopicRouteData topicRouteData = this.namesrvController.getRouteInfoManager().pickupTopicRouteData(requestHeader.getTopic());if (topicRouteData != null) {if (this.namesrvController.getNamesrvConfig().isOrderMessageEnable()) {String orderTopicConf =this.namesrvController.getKvConfigManager().getKVConfig(NamesrvUtil.NAMESPACE_ORDER_TOPIC_CONFIG,requestHeader.getTopic());topicRouteData.setOrderTopicConf(orderTopicConf);}byte[] content;Boolean standardJsonOnly = requestHeader.getAcceptStandardJsonOnly();if (request.getVersion() >= Version.V4_9_4.ordinal() || (null != standardJsonOnly && standardJsonOnly)) {content = topicRouteData.encode(SerializerFeature.BrowserCompatible,SerializerFeature.QuoteFieldNames, SerializerFeature.SkipTransientField,SerializerFeature.MapSortField);} else {content = RemotingSerializable.encode(topicRouteData);}response.setBody(content);response.setCode(ResponseCode.SUCCESS);response.setRemark(null);return response;}response.setCode(ResponseCode.TOPIC_NOT_EXIST);response.setRemark("No topic route info in name server for the topic: " + requestHeader.getTopic()+ FAQUrl.suggestTodo(FAQUrl.APPLY_TOPIC_URL));return response;}

总结

通过以上三个大的小节,已经梳理完成rocketmq使用netty的处理过程。
举了Product发送消息的例子,组装成RemotingCommand命令,经过编码,变成byte[]数组,发送到namesrv
namesrv接收到byte[]数组,解码成RemotingCommand。再交给NettyRequestProcessor处理。根据code,选择对应的请求类型处理
响应结果也组装成RemotingCommand命令,经过编码,变成byte[]数组,返回到Product

后续

之前的文章,已分析完成 consumer、namesrv、Product。剩余broker,最复杂的放到最后分析。
后续就是broker的分析文章。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/19940.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

蓝桥杯之图

图&#xff1a; 对于图来说&#xff0c;重点在于之后的最短路径算法&#xff0c;这边简单做一下了解即可 代码&#xff1a; #include<iostream> #include<string> #include<vector> #include<list> #include<queue> using namespace std; clas…

mysql 学习15 SQL优化,插入数据优化,主键优化,order by优化,group by 优化,limit 优化,count 优化,update 优化

插入数据优化&#xff0c; insert 优化&#xff0c; 批量插入&#xff08;一次不超过1000条&#xff09; 手动提交事务 主键顺序插入 load 从本地一次插入大批量数据&#xff0c; 登陆时 mysql --local-infile -u root -p load data local infile /root/sql1.log into table tb…

143,【3】 buuctf web [GYCTF2020]EasyThinking

进入靶场 一开始那个题目名字就想到了框架 扫描目录 访问后自动下载了 找源码 <?php namespace app\home\controller;use think\exception\ValidateException; use think\facade\Db; use think\facade\View; use app\common\model\User; use think\facade\Request; use …

数据守护者:备份文件的重要性及自动化备份实践

在信息化社会&#xff0c;数据已成为企业运营和个人生活的重要组成部分。无论是企业的核心业务数据&#xff0c;还是个人的珍贵照片、重要文档&#xff0c;数据的丢失或损坏都可能带来无法估量的损失。因此&#xff0c;备份文件的重要性愈发凸显&#xff0c;它不仅是数据安全的…

PHP支付宝--转账到支付宝账户

官方参考文档&#xff1a; ​https://opendocs.alipay.com/open/62987723_alipay.fund.trans.uni.transfer?sceneca56bca529e64125a2786703c6192d41&pathHash66064890​ 可以使用默认应用&#xff0c;也可以自建新应用&#xff0c;此处以默认应用来讲解【默认应用默认支持…

vscode插件开发

准备 安装开发依赖 npm install -g yo generator-code 安装后&#xff0c;运行命令 yo code 运行 打开项目&#xff0c; 点击 vscode 调式 按 F5 或点击调试运行按钮 会打开一个新窗口&#xff0c;在新窗口按快捷键 CtrlShiftP &#xff0c;搜索 Hello World 选择执行 右下角出…

win11安装wsl报错:无法解析服务器的名称或地址(启用wsl2)

1. 启用wsl报错如下 # 查看可安装的 wsl --install wsl --list --online此原因是因为没有开启DNS的原因&#xff0c;所以需要我们手动开启DNS。 2. 按照如下配置即可 Google的DNS&#xff08;8.8.8.8和8.8.4.4) 全国通用DNS地址 (114.114.114.114) 3. 运行以下命令来重启 WSL…

mysql 存储空间增大解决方案

一&#xff1a;查询数据库中表占比比较多的表 SELECT table_name AS "Tables", round(((data_length index_length) / 1024 / 1024), 2) AS "Size (MB)" FROM information_schema.tables WHERE table_schema "自己的数据库名"; …

【MySQL】数据库基础库/表的操作数据类型详解

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;实战项目_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.什么是数据库 2.主流数据库 3.基本使用 3.1 MySQL安装 3.2 连接服务器 3.3 服务器管理 3.4 服务器、数据库、表关系 3.5 …

【kafka系列】消费者

目录 获取消息 1. 消费者获取消息的流程逻辑分析 阶段一&#xff1a;消费者初始化 阶段二&#xff1a;分区分配与重平衡&#xff08;Rebalance&#xff09; 阶段三&#xff1a;消息拉取与处理 阶段四&#xff1a;偏移量提交 核心设计思想 2. 流程 关键点总结 常见参数…

仿叮咚买菜鸿蒙原生APP

# DingdongShopping 这是一个原生鸿蒙版的仿叮咚买菜APP项目 鸿蒙Next发布至今已经有一年多的时间了&#xff0c;但有时候我们想要实现一些复杂的功能或者效果&#xff0c;在开发文档上查阅一些资料还是比较费时的&#xff0c;有可能还找不到我们想要的内容。而社会层面上分享…

Linux 进程控制(进程创建,进程等待)

目录 进程创建 fork函数初识 fork函数返回值 写时拷贝 fork常规用法 fork调用失败的原因 进程终止 进程退出场景 进程退出码 进程常见退出方法 exit函数 _exit函数 return退出 return、exit和_exit之间的区别与联系 进程异常退出 进程等待 进程等待的必要性 获…

ROS2下Rviz显示orbbec相机depth深度图

ROS2下Rviz显示orbbec相机depth深度图 视频讲解 ROS2下Rviz显示orbbec相机depth深度图 在《ROS2下编写orbbec相机C package并Rviz显示》的基础上&#xff0c;继续添加depth图像的获取及显示 rgb_publisher_ this->create_publisher<sensor_msgs::msg::Image>("…

算法——结合实例了解Minimax算法(极小化极大算法)

计算机科学中最有趣的事情之一就是编写一个人机博弈的程序。有大量的例子&#xff0c;最出名的是编写一个国际象棋的博弈机器。但不管是什么游戏&#xff0c;程序趋向于遵循一个被称为Minimax算法&#xff0c;伴随着各种各样的子算法在一块。本篇将简要介绍 minimax 算法&#…

场外个股期权下单后多久成交?场外个股期权对投资组合的影响

对普通老板们而言&#xff0c;它如同精密手术刀——用得好可精准优化投资组合&#xff0c;用不好则可能伤及本金。记住两个关键&#xff1a;一是永远用"亏得起的钱"参与&#xff0c;二是把合约条款当"药品说明书"逐字研读。 场外个股期权下单后多久成交&am…

SolidWorks C# How

目录 1.如何创建C#插件程序? 2.插件程序需要继承的类是什么? 3.如何创建C#.net WPF程序? 4.WPF界面程序参考 5.如何获取类的框图 6.如何安装XCAD.net的 nuget包 7.如何扩展命令到菜单栏和工具栏 8.如何添加自定义面板 9.如何对文档管理进行编程 10.XCAD 开发solid…

【Go并发编程】Goroutine 调度器揭秘:从 GMP 模型到 Work Stealing 算法

每天一篇Go语言干货&#xff0c;从核心到百万并发实战&#xff0c;快来关注魔法小匠&#xff0c;一起探索Go语言的无限可能&#xff01; 在 Go 语言中&#xff0c;Goroutine 是一种轻量级的并发执行单元&#xff0c;它使得并发编程变得简单高效。而 Goroutine 的高效调度机制是…

华为昇腾 910B 部署 DeepSeek-R1 蒸馏系列模型详细指南

本文记录 在 华为昇腾 910B(65GB) * 8 上 部署 DeepSeekR1 蒸馏系列模型&#xff08;14B、32B&#xff09;全过程与测试结果。 NPU&#xff1a;910B3 (65GB) * 8 &#xff08;910B 有三个版本 910B1、2、3&#xff09; 模型&#xff1a;DeepSeek-R1-Distill-Qwen-14B、DeepSeek…

桥接模式 Bridge Pattern

桥接模式Abstraction 和 Implementor 的理解 在图书馆看到一本 通过电商项目真正实战《贯穿设计模式》。拿起来翻到了 桥接模式&#xff0c;感觉味道不对&#xff0c;和我印象中不一样。 感谢这位同学提供的源码 贯穿设计模式-适配器模式桥接模式_-CSDN博客GitHub - WeiXiao…

gitee SSH 公钥设置教程

Gitee 提供了基于 SSH 协议的 Git 服务,在使用 SSH 协议访问仓库仓库之前,需要先配置好账户 SSH 公钥。 1、生成秘钥 Windows 用户建议使用 Windows PowerShell 或者 Git Bash,在 命令提示符 下无 cat 和 ls 命令。 ssh-keygen -t ed25519 -C "Gitee SSH Key"中间…