13.Netty源码之Netty中的类与API


highlight: arduino-light

ServerBootstrap

Bootstrap 意思是引导,一个 Netty 应用通常由一个 Bootstrap 开始,主要作用是配置整个 Netty 程序,串联各个组件,Netty 中ServerBootstrap 是服务端启动引导类。

java //泛型 AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> ​ ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> ​ //B指的是 ServerBootstrap,C指的是ServerChannel ​ //该方法用于服务器端,用来设置两个 EventLoop,分别是bossGroup和workerGroup public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup)     //该方法用来设置一个服务器端的通道实现即指定服务器端的ServerSocketchannel的类型 public ServerBootstrap channel(Class<? extends ServerChannel> channelClass) ​ //用来给ServerSocketchannel添加配置 public <T> ServerBootstrap option(ChannelOption<T> option, T value) ​ //用来给接收到的客户端Socketchannel添加配置 public <T> ServerBootstrap childOption(ChannelOption<T> childOption, T value) ​ //该方法用来设置业务处理类。即自定义用来处理客户端Socketchannel的handler public ServerBootstrap childHandler(ChannelHandler childHandler) ​ //该方法用于服务器端,用来设置占用的端口号 public ChannelFuture bind(int inetPort)

Bootstrap

Bootstrap 意思是引导,一个 Netty 应用通常由一个 Bootstrap 开始,主要作用是配置整个 Netty 程序,串联各个组件,Netty 中 Bootstrap 类是客户端程序的启动引导类。

java //泛型 AbstractBootstrap<Bootstrap, Channel> ​ Bootstrap extends AbstractBootstrap<Bootstrap, Channel> B指的是 ,C指的是Channel ​ //该方法用于客户端,用来设置一个 EventLoop public Bootstrap  group(EventLoopGroup group) //该方法用来设置一个客户端的端的通道实现即指定客户端的Socketchannel的类型 Bootstrap channel(Class<? extends Channel> channelClass)     //该方法用来设置业务处理类。即自定义用来处理客户端Socketchannel的handler Bootstrap handler(ChannelHandler handler)     //该方法用于客户端,用来连接服务器端 public ChannelFuture connect(String inetHost, int inetPort)

Future和ChannelFuture

Future 说明

表示异步的执行结果, 可以通过它提供的方法来检测执行是否完成,比如检索计算等等。

java /****     * 对账业务生成数据     */    @Override    public void pullData(String time) {        if (StringUtils.isEmpty(time)) {            time = DateTimeUtils.getDateString(-1);       }        String lockKey = time;        Mutex mutex = null;        try {            mutex = iLockService.lock(MutexLockEnum.PULL_DATA.getKey(), lockKey,                    MuteLockTimeConstant.RECORD_ACCOUNT_FLOW_WAIT_TIME,                    MuteLockTimeConstant.RECORD_ACCOUNT_FLOW_LEASE_TIME, TimeUnit.SECONDS);       } catch (Exception e) {            ReconciliationException.throwException(ExceptionEnum.GET_LOCK_FAIL);       } ​        try {            //step1. 查询所有sql            List<ReconciliationSqlEo> sqlList = reconciliationSqlDas.selectReconciliationListData();            ArrayList<Future<ClearResult>> list = new ArrayList();            if (CollectionUtils.isNotEmpty(sqlList)) {                for (ReconciliationSqlEo reconciliationSqlEo : sqlList) {                    ReconciliationPullControlEo history = getHistory(time, reconciliationSqlEo);                    if (Objects.nonNull(history)) {                        continue;                   }                    final String finalTime = time;                    Future<ClearResult> future = ThreadPoolUtils.submit(() -> {                                String totalRecordSql = reconciliationSqlEo.getTotalRecordSql();                                Integer belongCenterCode = reconciliationSqlEo.getBelongCenterCode();                                String node = reconciliationSqlEo.getNode();                                RestResponse<List<ReconciliationDataDto>> recordResponse = null;                                if (BelongCenterCode.PAYMENT.getCode().equals(belongCenterCode)) {                                    recordResponse = paymentReconciliationApi.queryList(totalRecordSql);                               } else if (BelongCenterCode.SETTLEMENT.getCode().equals(belongCenterCode)) {                                    recordResponse = iSettlementQueryApi.queryReconciliationData(totalRecordSql);                               } else if (BelongCenterCode.ACCOUNT.getCode().equals(belongCenterCode)) {                                    recordResponse = accountReconciliationApi.queryReconciliationData(totalRecordSql);                               } else if (BelongCenterCode.U9_THROW.getCode().equals(belongCenterCode)                                        && BusinessNode.THROW_ACCOUNT_COLLECTION.getCode().equals(node)) {                                    recordResponse = u9ViewService.selectCollectionThrow(finalTime);                               } else if (BelongCenterCode.U9_THROW.getCode().equals(belongCenterCode)                                        && BusinessNode.THROW_ACCOUNT_PAY.getCode().equals(node)) {                                    recordResponse = u9ViewService.selectPaymentThrow(finalTime);                               } else if (BelongCenterCode.OFFLINE_SETTLE.getCode().equals(belongCenterCode)                                        && BusinessNode.OFFLINE_SETT.getCode().equals(node)) {                                    recordResponse = u9ViewService.selectSettle(finalTime);                               }                                log.info("拉取内部数据sql:{},响应:{}", totalRecordSql, recordResponse);                                ClearResult clearResult = new ClearResult(Boolean.FALSE, belongCenterCode, node);                                try {                                    addData(reconciliationSqlEo, recordResponse, clearResult, finalTime);                               } catch (Exception e) {                                    log.info("-----------内部数据清洗异常:配置:{},{}", reconciliationSqlEo, e.getMessage());                               }                                return clearResult;                           }                   );                    list.add(future);               }                if (CollectionUtils.isNotEmpty(list)) {                    for (Future<ClearResult> future : list) {                        FutureTask<ClearResult> futureTask = (FutureTask) future;                        try {                            ClearResult clearResult = futureTask.get();                            log.info("内部数据清洗结果:{}", clearResult.toString());                       } catch (Exception e) {                            log.info("内部数据清洗结果获取失败:{}", e.getMessage());                       }                   }               }           }       } finally {            if (Objects.nonNull(mutex)) {                iLockService.unlock(mutex);           }       }   }

ChannelFuture

是1个接口。public interface ChannelFuture extends Future

在使用 Netty 进行编程时,拦截操作和转换出入站数据只需要您提供 callback 或利用future 即可。

这使得链式操作简单、高效, 并有利于编写可重用的、通用的代码。

Netty 框架的目标就是让你的业务逻辑从网络基础应用编码中分离出来、解脱出来

Netty 中所有的 IO 操作都是异步的,不能立刻得知消息是否被正确处理。

但是可以过一会等它执行完成或者直接注册一个监听,具体的实现就是通过 Future 和 ChannelFutures,他们可以注册一个监听,当操作执行成功或失败时监听会自动触发注册的监听事件。

Future-Listener 机制

当 Future 对象刚刚创建时,处于非完成状态,调用者可以通过返回的 ChannelFuture 来获取操作执行的状态,注册监听函数来执行完成后的操作。

常见有如下操作:

通过 isDone 方法来判断当前操作是否完成; 通过 isSuccess 方法来判断已完成的当前操作是否成功; 通过 getCause 方法来获取已完成的当前操作失败的原因; 通过 isCancelled 方法来判断已完成的当前操作是否被取消; 通过 addListener 方法来注册监听器,当操作已完成(isDone 方法返回完成),将会通知指定的监听器;如果 Future 对象已完成,则通知指定的监听器

java //返回当前正在进行 IO 操作的通道 Channel channel() //等待异步操作执行完毕 ChannelFuture sync() ​            //示例代码            ChannelFuture cf = bootstrap.bind(6668).sync();            Channel channel = cf.channel();            //给cf 注册监听器,监控我们关心的事件            cf.addListener(new ChannelFutureListener() {                @Override             public void operationComplete(ChannelFuture future) throws Exception {                    if (cf.isSuccess()) {                        System.out.println("服务器监听端口 6668 成功");                   } else {                        System.out.println("服务器监听端口 6668 失败");                   }               }           });            //对关闭通道进行监听            ChannelFuture sync = cf.channel().closeFuture().sync();       }

java  public static void main(String[] args) {        EventExecutorGroup group = new DefaultEventExecutorGroup(1);        Callable callable = new Callable() { ​            @Override            public Integer call() throws Exception {                Thread.sleep(10000);                System.out.println("Sleep......");                return new Integer(99);           }       };        io.netty.util.concurrent.Future f = (io.netty.util.concurrent.Future) group.submit(callable); ​ ​        GenericFutureListener listener = new GenericFutureListener() {            @Override           public void operationComplete(io.netty.util.concurrent.Future future) throws Exception {                System.out.println(Thread.currentThread().getName() + "result:" + future.get());               //不调用程序会一直存活               //实际应用 可不调用 让它一直存在                group.shutdown();           }       };        GenericFutureListener[] listeners = {listener};        f.addListeners(listeners);        System.out.println( f.isSuccess()); ​   }

Channel

Netty 网络通信的组件,能够用于执行网络 I/O 操作。

通过Channel 可获得当前网络连接的通道的状态 通过Channel 可获得 网络连接的配置参数 (例如接收缓冲区大小)

Channel 提供异步的网络 I/O 操作(如建立连接,读写,绑定端口),异步调用意味着任何 I/O 调用都将立即返回,并且不保证在调用结束时所请求的 I/O 操作已完成。

调用立即返回一个 ChannelFuture 实例,通过注册监听器到 ChannelFuture 上,可以 I/O 操作成功、失败或取 消时回调通知调用方。

支持关联 I/O 操作与对应的处理程序 不同协议、不同的阻塞类型的连接都有不同的 Channel 类型与之对应,常用的 Channel 类型:

java NioSocketChannel,异步的客户端 TCP Socket 连接。 NioServerSocketChannel,异步的服务器端 TCP Socket 连接。 NioDatagramChannel,异步的 UDP 连接。 NioSctpChannel,异步的客户端 Sctp 连接。 NioSctpServerChannel,异步的 Sctp 服务器端连接,这些通道涵盖了 UDP 和 TCP 网络 IO 以及文件 IO。

Selector

一个eventLoop会绑定1个selector。

Netty 基于 Selector 对象实现 I/O 多路复用,通过 Selector 一个线程可以监听多个 [Channel] 的不同事件。

当向一个 Selector 中注册 Channel 后,Selector 内部的机制就可以自动不断地查询这些注册的 Channel 是否有已就绪的 I/O 事件(例如可读,可写,网络连接完成等),这样程序就可以很简单地使用一个线程高效地管理多个 Channel 。

ChannelHandler及其实现类

1.ChannelHandler 是一个接口,处理 I/O 事件或拦截 I/O 操作,并将其转发到其 ChannelPipeline(业务处理链)中的下一个处理程序。

2.ChannelHandler 本身并没有提供很多方法,因为这个接口有许多的方法需要实现,方便使用期间,可以继承它的子类。

3.我们经常需要自定义一个 Handler 类去继承 ChannelInboundHandlerAdapter,然后通过重写相应方法实现业务逻辑,我们接下来看看一般都需要重写哪些方法。

java public class ChannelInboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelInboundHandler { ​ public ChannelInboundHandlerAdapter() { } ​ public void channelRegistered(ChannelHandlerContext ctx) throws Exception {     ctx.fireChannelRegistered(); } ​ public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {       ctx.fireChannelUnregistered(); } ​ //通道就绪事件 public void channelActive(ChannelHandlerContext ctx) throws Exception {  ctx.fireChannelActive(); } ​ public void channelInactive(ChannelHandlerContext ctx) throws Exception {      ctx.fireChannelInactive(); }     ​ //通道读取数据事件 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {      ctx.fireChannelRead(msg); }   ​ //数据读取完毕事件   public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {        ctx.fireChannelReadComplete();     }     ​ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {        ctx.fireUserEventTriggered(evt); }   ​ public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { ctx.fireChannelWritabilityChanged(); } ​ //通道发生异常事件 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception{ ctx.fireExceptionCaught(cause); } } ​

Pipeline 和 ChannelPipeline

ChannelPipeline 是一个 Handler 的集合,它负责处理和拦截 inbound 或者 outbound 的事件和操作,相当于一个贯穿 Netty 的链。

也可以这样理解:ChannelPipeline是保存ChannelHandler的List,用于处理或拦截Channel的入站事件和出站操作。

ChannelPipeline 实现了一种高级形式的拦截过滤器模式。

使用户可以完全控制事件的处理方式,以及 Channel 中各个的 ChannelHandler 如何相互交互。

在 Netty 中每个 Channel 都有且仅有一个 ChannelPipeline 与之对应,它们的组成关系如下

image.png

一个 Channel 包含了一个ChannelPipeline,而 ChannelPipeline 中又维护了一个由 ChannelHandlerContext 组成的双向链表,并且每个 ChannelHandlerContext 中又关联着一个 ChannelHandler。

入站事件和出站事件在一个双向链表中,入站事件会从链表 head 往后传递到最后一个入站的 handler,出站事件会从链表 tail 往前传递到最前一个出站的 handler,两种类型的 handler 互不干扰。

//把一个业务处理类handler添加到链中的第一个位置 ChannelPipeline addFirst(ChannelHandler... handlers) //把一个业务处理类handler添加到链中的最后一个位置 ChannelPipeline addLast(ChannelHandler... handlers)

ChannelHandlerContext

保存 Channel 相关的所有上下文信息,1个ChannelHandlerContext关联一个 ChannelHandler对象。 ChannelHandlerContext 中包含一个具体的事件处理器ChannelHandler ,ChannelHandlerContext 中也绑定了对应的 pipeline和Channel 的信息,方便对 ChannelHandler进行调用。

//关闭通道 ChannelFuture close() ​ //刷新 ChannelOutboundInvoker flush()     //将 数 据 写 到 ChannelPipeline中当前ChannelHandler的下一个ChannelHandler开始处理(出站) ChannelFuture writeAndFlush(Object msg)

ChannelOption

Netty 在创建 Channel 实例后,一般都需要设置 ChannelOption 参数。 ChannelOption 参数如下:

//ChannelOption.SO_BACKLOG 对应 TCP/IP 协议 listen 函数中的 backlog 参数,用来初始化服务器端可连接队列大小。服 务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接。多个客户 端来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理,backlog参数指定 了队列的大小。 ​ //ChannelOption.SO_KEEPALIVE 一直保持连接活动状态

EventLoopGroup

实现类是NioEventLoopGroup

EventLoopGroup 是一组 EventLoop 的抽象,Netty 为了更好的利用多核 CPU 资源,一般会有多个 EventLoop 同时工作,每个 EventLoop 维护着一个 Selector 实例。

EventLoopGroup 提供 next 接口,可以从组里面按照一定规则获取其中一个 EventLoop来处理任务。在 Netty 服务器端编程中,我们一般都需要提供两个EventLoopGroup即BossEventLoopGroup 和 WorkerEventLoopGroup。

通常一个服务端口即一个 ServerSocketChannel对应一个Selector 和一个BossEventLoopGroup 。BossEventLoop 负责接收客户端的连接并将 SocketChannel 交给 WorkerEventLoopGroup 来进行 IO 处理,如下图所示

image.png

BossEventLoopGroup 通常是一个单线程的 EventLoop。

z换个EventLoop关联1个Selector 实例。

BossEventLoop 不断轮询 Selector 将连接事件 OP_ACCEPT 分离出来。

然后将接收到的SocketChannel 交给 WorkerEventLoopGroup, WorkerEventLoopGroup 会由 next 选择其中一个 EventLoop来将这个 SocketChannel 注册到其维护的Selector并对其后续的 IO 事件进行处理。

//构造方法 public NioEventLoopGroup() //断开连接,关闭线程 public Future<?> shutdownGracefully()

Unpooled 类

Netty提供一个专门用来操作缓冲区(即Netty的数据容器)的工具类。

java package unpooledByte; ​ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; ​ public class NettyByteBuf01 {    public static void main(String[] args) { ​        /*        说明:        1)读取数据geteByte(i)需要指定i        2)读取数据readByte() , 不需要指定i, 内部的readIndex 会移动        3)通过Debug 看一下readIndex writeIndex 和 capacity        4)大家看到和NIO的Buffer比较,不再需要flip 进行读写切换了,内部通过readIndex和writeIndex来控制读和写操作        */        ByteBuf buffer = Unpooled.buffer(10); ​        for (int i = 0; i < 10; ++i) {            buffer.writeByte(i);       } ​        for (int i = 0; i < buffer.capacity(); ++i) {            System.out.println(buffer.getByte(i));       } ​       for(int i = 0; i < buffer.capacity(); ++i) {            System.out.println(buffer.readByte());       }   } } ​

java package unpooledByte; ​ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; ​ import java.nio.charset.Charset; ​ public class NettyByteBuf01 {    public static void main(String[] args) {        ByteBuf byteBuf = Unpooled.copiedBuffer("hello,world!", Charset.forName("utf-8")); ​        if (byteBuf.hasArray()) { ​            byte[] content = byteBuf.array();            //hello,world!            System.out.println(new String(content, Charset.forName("utf-8")));            //UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 12, cap: 36)            System.out.println(byteBuf);            //0            System.out.println(byteBuf.arrayOffset());            //0            System.out.println(byteBuf.readerIndex());            //12            System.out.println(byteBuf.writerIndex());            //36            System.out.println(byteBuf.capacity()); ​            int length = byteBuf.readableBytes();            //12            System.out.println(length); ​            for (int i = 0; i < byteBuf.readableBytes(); ++i) {                //hello,world!                System.out.print((char) byteBuf.getByte(i));           }            System.out.println();            //hell            System.out.println(byteBuf.getCharSequence(0, 4, Charset.forName("utf-8")));            //o,worl            System.out.println(byteBuf.getCharSequence(4, 7, Charset.forName("utf-8")));       }   } }

java //通过给定的数据和字符编码返回一个ByteBuf 对象(类似于 NIO 中的 ByteBuffer 但有区别) public static ByteBuf copiedBuffer(CharSequence string, Charset charset)

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

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

相关文章

Vue2到3 全套学习内容(持续更新)

Vue 初次上手 1. Vue 概念 概念: Vue 是一个用于 构建用户界面 的 渐进式 框架 ①构建用户界面&#xff1a;基于数据动态渲染出用户看到的页面 ②渐进式&#xff1a;循序渐进 Vue的两种使用方式: ①Vue 核心包开发 场景: 局部 模块改造 ②Vue核心包&Vue插件工程化开发…

C++-----list

本期我们来讲解list&#xff0c;有了string和vector的基础&#xff0c;我们学习起来会快很多 目录 list介绍 ​编辑 list常用接口 insert erase reverse sort merge unique remove splice 模拟实现 基础框架 构造函数 push_back 迭代器 常见问题 const迭代器 …

MySQL存储过程

文章目录 MySQL存储过程一.MySQL存储过程1.概述2.简介3.存储过程中的控制结构及应用场景4.存储过程的优点5.存储过程语法6.不带参数创建&#xff08;示例&#xff09;6.1 创建存储过程6.2 调用存储过程6.3 查看存储过程6.4 存储过程的参数 7.实例8.修改存储过程9.删除存储过程 …

4 Linux基础篇-目录结构

4 Linux基础篇-目录结构 文章目录 4 Linux基础篇-目录结构4.1 Linux目录结构4.2 具体的目录结构 学习视频来自于B站【小白入门 通俗易懂】2021韩顺平 一周学会Linux。可能会用到的资料有如下所示&#xff0c;下载链接见文末&#xff1a; 《鸟哥的Linux私房菜 基础学习篇 第四版…

springboot 之以enable开头的注解

Spring​ 有很多 Enable 开头的注解&#xff0c;平时在使用的时候也没有注意过为什么会有这些注解 Enable 注解 首先我们先看一下有哪些常用的 Enable 开头的注解&#xff0c;以及都是干什么用的。 EnableRetry​&#xff1a;开启Spring 的重试功能&#xff1b; EnableSch…

web攻击面试|网络渗透面试(一)

Web攻击面试大纲 常见Web攻击类型 1.1 SQL注入攻击 1.2 XSS攻击 1.3 CSRF攻击 1.4 命令注入攻击SQL注入攻击 2.1 基本概念 2.2 攻击原理 2.3 防御措施XSS攻击 3.1 基本概念 3.2 攻击原理 3.3 防御措施CSRF攻击 4.1 基本概念 4.2 攻击原理 4.3 防御措施命令注入攻击 5.1 基本概…

手机python怎么用海龟画图,python怎么在手机上编程

大家好&#xff0c;给大家分享一下手机python怎么用海龟画图&#xff0c;很多人还不知道这一点。下面详细解释一下。现在让我们来看看&#xff01; 1、如何python手机版创造Al&#xff1f; 如果您想在手机上使用Python来创建AI&#xff08;人工智能&#xff09;程序&#xff0…

Golang并发控制

开发 go 程序的时候&#xff0c;时常需要使用 goroutine 并发处理任务&#xff0c;有时候这些 goroutine 是相互独立的&#xff0c;需要保证并发的数据安全性&#xff0c;也有的时候&#xff0c;goroutine 之间要进行同步与通信&#xff0c;主 goroutine 需要控制它所属的子gor…

nginx怎么做负载均衡

Nginx怎么做负载均衡 Nginx 是一个高性能的开源反向代理服务器&#xff0c;可以用于实现负载均衡。负载均衡指的是将用户请求平均分配给多个服务器&#xff0c;以提高整体系统性能和可靠性。下面是一个详细介绍如何使用 Nginx 实现负载均衡的步骤&#xff1a; 步骤 1&#xf…

Redis7.0版本集群

环境准备 centos 7.5redis-7.0.12.tar.gz 操作步骤 1、采用克隆模式创建三台虚拟机&#xff08;redis-cluster01、redis-cluster02、redis-cluster03&#xff09; 2、上传 redis-7.0.12.tar.gz 文件到 /home 目录下 3、解压文件 4、进入 /home/redis-7.0.12 目录下执行命令 m…

K8S集群创建和管理,以及常用命令

目录 1. 安装 K8s 集群2. 创建 K8s 集群3. 管理 K8s 集群4. 维护 K8s 集群 K8s 集群 (Kubernetes Cluster) 是一个由多个节点组成的容器编排平台&#xff0c;它提供了一种简单、可靠、可扩展的方式来部署、管理和监控容器化应用程序。K8s 集群通常由一个或多个 Master 节点和一…

VB+ACCESS教学管理系统设计与实现

【内容摘要】 本学籍管理系统是采用VISAUL BASIC6.0开发的一个数据库管理系统。本设计说明书主要讲述了VISAUL BASIC6.0的基本功能及设计方法。紧接着以本系统为例,逐一介绍开发本系统系统的步骤:系统分析、系统设计、系统实现、系统维护。在系统分析中先后用数据流图、数据…

pg_archivecleanup清理wal日志

一、 注意事项 pg_archivecleanup代码中仅进行了wal日志文件名的对比&#xff0c;没有实现对WAL日志名及对应生成时间的判断。在WAL日志未被重命名时&#xff0c;时间与日志名顺序名一致&#xff0c;没有问题。一旦WAL日志被重命名&#xff0c;pg_archivecleanup清理就可能清理…

Hadoop 之 Hbase 配置与使用(四)

Hadoop 之 Hbase 配置与使用 一.Hbase 下载1.Hbase 下载 二.Hbase 配置1.单机部署2.伪集群部署&#xff08;基于单机配置&#xff09;3.集群部署1.启动 hadoop 集群2.启动 zookeeper 集群3.启动 hbase 集群4.集群启停脚本 三.测试1.Pom 配置2.Yml 配置3.Hbase 配置类4.Hbase 连…

C#中try catch finally 用法详解

本文详解try catch finally 用法 目录 定义 这些代码块的用法如下:

大数据课程D1——hadoop的初识

文章作者邮箱&#xff1a;yugongshiyesina.cn 地址&#xff1a;广东惠州 ▲ 本章节目的 ⚪ 了解大数据的概念&#xff1b; ⚪ 了解大数据的部门结构&#xff1b; ⚪ 了解hadoop的定义&#xff1b; ⚪ 了解hadoop的发展史&#xff1b; 一、大数据简介 1. 概述…

STM32 Flash学习(二)

STM32F1的官方固件库操作FLASH的几个常用函数。这些函数和定义分布在源文件stm32f1xx_hal_flash.c/stm32f1xx_hal_flash_ex.c以及头文件stm32f1xx_hal_flash.h/stm32f1xx_hal_flash_ex.h中。 锁定解函数 对FLASH进行写操作前必须先解锁&#xff0c;解锁操作&#xff1a;在FLA…

GB/T 25000.51解读——软件产品的兼容性怎么测?

GB/T 25000.51-2016《软件产品质量要求和测试细则》是申请软件检测CNAS认可一定会用到的一部国家标准。在前面的文章中&#xff0c;我们为大家整体介绍了GB/T 25000.51-2016《软件产品质量要求和测试细则》国家标准的结构和所涵盖的内容以及对软件产品的八大质量特性中的功能性…

GPU压力测试篇- TensorFlow

简介 该文档介绍使用Tensorflow框架&#xff0c;测试 NVIDIA 驱动的常见python 代码。 环境信息 编号 软件 软件版本 备注 01 驱动 470.57.02 02 cuda 版本 11.2 03 cudnn 版本 8.1.1.33 04 tensorflow 2.6 功能测试代码&#xff1a; import tensor…

基于Matlab实现30种电力电子仿真模型(附上完整源码)

本文介绍了如何使用Matlab来实现电力电子仿真模型&#xff0c;并提供了一个简单的代码示例&#xff0c;展示了如何建立一个逆变器的仿真模型并进行仿真分析。 文章目录 1. 引言2. Matlab实现电力电子仿真模型的基本步骤3. 逆变器仿真模型的示例代码4. 结果分析5. 结论6. 30个完…