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

什么是Netty

一.Netty介绍

1.什么是netty

Netty 是由 JBOSS 提供的一个 Java 开源框架。Netty 提供异步的、基于事件驱动的网络应用程序框架,用以快速开发高性能、高可靠性的网络 IO 程序,是目前最流行的 NIO 框架,Netty 在互联网领域、大数据分布式计算领域、游戏行业、通信行业等获得了广泛的应用,知名的 Elasticsearch 、Dubbo 框架内部都采用了 Netty。

2.为什么要用netty

原生 NIO 存在问题:

1.NIO 的类库和 API 繁杂

2.需要熟悉 Java 多线程编程,因为 NIO 编程涉及到 Reactor 模式,必须对多线程和网络编程非常熟悉, 才能编写出高质量的 NIO 程序

3.开发工作量和难度都非常大。例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常  流的处理等等处理起来难度会比较大。

4.JDK NIO 的 Bug:例如臭名昭著的 Epoll Bug,它会导致 Selector 空轮询,最终导致 CPU 100%。直到 JDK 1.7 版本该问题仍旧存在,没有被根本解决。

3.Netty的优点

Netty 对 JDK 自带的 NIO 的 API 进行了封装,解决了上述问题。

1.设计优雅:适用于各种传输类型的统一 API 阻塞和非阻塞 Socket;基于灵活且可扩展的事件模型,可以清晰地分离关注点;高度可定制的线程模型 - 单线程,一个或多个线程池.

2.使用方便:详细记录的 Javadoc,用户指南和示例;没有其他依赖项,JDK 5(Netty 3.x)或 6(Netty 4.x)就足够了。

3.高性能、吞吐量更高:延迟更低;减少资源消耗;最小化不必要的内存复制。

4.安全:完整的 SSL/TLS 和 StartTLS 支持。

5.社区活跃、不断更新:社区活跃,版本迭代周期短,发现的 Bug 可以被及时修复,同时更多的新功能会被加入

二.Reactor三种线程模型

1.现有的三种线程模型

不同的线程模式,对程序的性能有很大影响,目前存在的线程模型有:

.传统阻塞 I/O 服务模型

Reactor 模式

Reactor 模式又有 3 种典型的实现

单 Reactor 单线程;

单 Reactor 多线程;

主从 Reactor 多线程

Netty 的线程模型是主要是基于主从 Reactor 多线程模型改成了主从 Reactor 多线程模型有多个 Reactor模式

2.传统阻塞 I/O 服务模型介绍

特点:

采用阻塞IO模式获取输入的数据

每个连接都需要创建单独的线程完成数据的输入,业务处理和数据的返回

缺点:

当并发数很大,就会创建大量的线程,占用很大系统资源,在线程开销和上下文切换上降低处理性能

当连接创建后,如果当前线程暂时没有数据可读,该线程会阻塞在read 操作,造成线程资源的浪费。

 

黄色的框表示对象, 蓝色的框表示线程 白色的框表示方法(API)

3. Reactor 模式

针对传统阻塞 I/O 服务模型的 2 个缺点,解决方案:

I/O 复用模型:多个连接共用一个阻塞对象,应用程序只需要在一个阻塞对象上等待,无需阻塞等待所有连接。当某个连接有新的数据可以处理时,操作系统通知应用程序,线程从阻塞状态返回,开始进行业务处理。

Reactor 对应的叫法: 1. 反应器模式 2. 分发者模式(Dispatcher) 3. 通知者模式(notifier)

基于线程池复用线程资源模式:不必再为每个连接创建线程,将连接完成后的业务处理任务分配给线程进行处理,一个线程可以处理多个连接的业务。

I/O 复用结合线程池,就是 Reactor 模式基本设计思想

 

Reactor 模式,通过一个或多个输入同时传递给服务处理器的模式,(基于事件驱动)

服务器端程序处理传入的多个请求,并将它们同步分派到相应的处理线程, 因此Reactor模式也叫 Dispatcher模式

Reactor 模式使用IO复用监听事件, 收到事件后,分发给某个线程(进程), 这点就是网络服务器高并发处理关键

4.单 Reactor 单线程

1.工作原理:

①Select 是前面 I/O 复用模型介绍的标准网络编程 API,可以实现应用程序通过一个阻塞对象监听多路连接请求

②Reactor 对象通过 Select 监控客户端请求事件,收到事件后通过 Dispatch 进行分发

③如果是建立连接请求事件,则由 Acceptor 通过 Accept 处理连接请求,然后创建一个 Handler 对象处理连接完成后的后续业务处理

④如果不是建立连接事件,则 Reactor 会分发调用连接对应的 Handler 来响应

⑤Handler 会完成 Read→业务处理→Send 的完整业务流程

2.优点:

模型简单,没有多线程、进程通信、竞争的问题,全部都在一个线程中完成

3.缺点:

①性能问题,只有一个线程,无法完全发挥多核 CPU 的性能。

②可靠性问题,线程意外终止,或者进入死循环,会导致整个系统通信模块不可用,不能接收和处理外部消息,造成节点故障

③服务器端用一个线程通过多路复用搞定所有的 IO 操作(包括连接,读、写等),编码简单,清晰明了,但是如果客户端连接数量较多时,当对应多个读时,还是会出现阻塞现象,当这种情况发生时将无法支撑高并发的场景。

4.应用场景:

客户端的数量有限,业务处理非常快速(比如 Redis在业务处理的时间复杂度 O(1) 的情况)

 

5.单Reactor多线程

1.工作原理:

①Reactor 对象通过select 监控客户端请求事件, 收到事件后,通过dispatch进行分发

②如果是建立连接请求, 则由Acceptor 通过accept 处理连接请求, 然后创建一个Handler对象处理完成连接后的各种事件

③如果不是连接请求,则由Reactor分发调用连接对应的handler 来处理

④handler 只负责响应事件,不做具体的业务处理, 通过read 读取数据后,会分发给后面的worker线程池的某个线程处理业务。

⑤worker 线程池会分配独立线程完成真正的业务,并将结果返回给handler,handler收到响应后,通过send 将结果返回给client.

2.优点:

可以充分的利用多核cpu 的处理能力

3.缺点:

多线程数据共享和访问比较复杂,Reactor处理所有的事件的监听和响应,在单线程运行时,在高并发场景容易出现性能瓶颈.

 

6.主从 Reactor 多线程

1.工作原理:

①Reactor主线程 MainReactor 对象通过select 监听连接事件, 收到事件后,通过Acceptor 处理连接事件

②当 Acceptor  处理连接事件后,MainReactor 将连接分配给SubReactor

③subReactor 将连接加入到连接队列进行监听,并创建handler进行各种事件处理

④当有新事件发生时, subreactor 就会调用对应的handler处理

⑤handler 通过read 读取数据,分发给后面的worker 线程处理

⑥worker 线程池分配独立的worker 线程进行业务处理,并返回结果

⑦handler 收到响应的结果后,再通过send 将结果返回给client

⑧Reactor 主线程可以对应多个Reactor 子线程, 即MainRecator 可以关联多个SubReactor

 

三.Netty线程模型

1.工作原理

Netty抽象出两组线程池 BossGroup 专门负责接收客户端的连接, WorkerGroup 专门负责网络的读写

BossGroup 和 WorkerGroup 类型都是 NioEventLoopGroup

NioEventLoopGroup 相当于一个事件循环组, 这个组中含有多个事件循环 ,每一个事件循环是 NioEventLoop

NioEventLoop 表示一个不断循环的执行处理任务的线程, 每个NioEventLoop 都有一个selector , 用于监听绑定在该通道上的socket的网络通讯

NioEventLoopGroup 可以有多个线程, 即可以含有多个NioEventLoop

每个Boss NioEventLoop 循环执行的步骤有3步

轮询accept 事件

处理accept 事件 , 与client端建立连接 , 生成NioScocketChannel , 并将其注册到某个worker NIOEventLoop 上的 selector 上

处理任务队列的任务 , 即 runAllTasks

每个 Worker NIOEventLoop 循环执行的步骤

轮询read, write 事件

处理i/o事件, 即read , write 事件,在对应NioScocketChannel 处理

处理任务队列的任务 , 即 runAllTasks

每个Worker NIOEventLoop  处理业务时,会使用pipeline(管道), pipeline 中包含了boss group上NioEventLoop注册到worker 的selector 的channel , 即通过pipeline 可以获取到对应通道, 管道中维护了很多的处理器

 

四.Netty入门

1.引入java包

<dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.20.Final</version>
</dependency>

2.hello world 编写

入门的编写一共需要4个类

netty server

netty server handler

netty client

netty client handler

2.1.netty server 端编写

package com.zpb.netty.netty.helloWorld;import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoop;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;/*** @dec : netty入门* @Date: 2019/11/24* @Auther: pengbo.zhao* @version: 1.0* @demand:**    {@link #main(String[] args)}**/
public class NettyServer {public static void main(String[] args) throws  Exception{//1.创建BossGroup 和 WorkerGroup//1.1 创建2个线程组//bossGroup只处理连接请求//workerGroup 处理客户端的业务逻辑//2个都是无限循环EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();//2.创建服务端的启动对象,可以为服务端启动配置一些服务参数ServerBootstrap bootStrap = new ServerBootstrap();//2.1使用链式编程来配置服务参数bootStrap.group(bossGroup,workerGroup)                          //设置2个线程组.channel(NioServerSocketChannel.class)                 //使用NioServerSocketChannel作为服务器的通道.option(ChannelOption.SO_BACKLOG,128)            //设置线程等待的连接个数.childOption(ChannelOption.SO_KEEPALIVE,Boolean.TRUE) //设置保持活动连接状态.childHandler(new ChannelInitializer<SocketChannel>() {//给PipeLine设置处理器@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {//通过socketChannel得到pipeLine,然后向pipeLine中添加处理的handlesocketChannel.pipeline().addLast(new NettyServerHandle());}}); //给workerGroup 的EventLoop对应的管道设置处理器(可以自定义/也可使用netty的)System.err.println("server is ready......");//启动服务器,并绑定1个端口且同步生成一个ChannelFuture 对象ChannelFuture channelFuture = bootStrap.bind(8888).sync();//对关闭通道进行监听(netty异步模型)//当通道进行关闭时,才会触发这个关闭动作channelFuture.channel().closeFuture().sync();}
}

2.2.netty server handler编写

package com.zpb.netty.netty.helloWorld;import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelPipeline;
import io.netty.util.CharsetUtil;/*** @dec :* @Date: 2019/11/24* @Auther: pengbo.zhao* @version: 1.0* @demand:*/
public class NettyServerHandle extends ChannelInboundHandlerAdapter {/*** 读取数据** @param: 1.ChannelHandlerContext ctx:上下文对象, 含有 管道 pipeline , 通道 channel, 地址* @param: 2. Object msg: 就是客户端发送的数据 默认 Object*/@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {System.err.println("服务器读取线程 " + Thread.currentThread().getName());System.out.println("server ctx =" + ctx);System.out.println("看看 channel 和 pipeline 的关系");Channel channel = ctx.channel();ChannelPipeline pipeline = ctx.pipeline(); //本质是一个双向链接, 出站入站//将 msg 转成一个 ByteBuf,ByteBuf 是 Netty 提供的,不是 NIO 的 ByteBuffer.ByteBuf buf = (ByteBuf) msg;System.out.println("客户端发送消息是:" + buf.toString(CharsetUtil.UTF_8));System.out.println("客户端地址:" + channel.remoteAddress());}/*** 读取数据完成后** @param:* @return:* @auther:* @date:*/@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {//writeAndFlush 是 write + flush//将数据写入到缓存,并刷新//一般讲,我们对这个发送的数据进行编码ctx.writeAndFlush(Unpooled.copiedBuffer("hello, 客户端~(>^ω^<)喵", CharsetUtil.UTF_8));}//处理异常, 一般是需要关闭通道@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {ctx.close();}
}

2.3.netty client端编写

package com.zpb.netty.netty.helloWorld;import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;/*** @dec :* @Date: 2019/11/24* @Auther: pengbo.zhao* @version: 1.0* @demand:*/
public class NettyClient {public static void main(String[] args) throws Exception {//1.客户端定义一个循环事件组EventLoopGroup group = new NioEventLoopGroup();try {//2.创建客户端启动对象Bootstrap bootstrap = new Bootstrap();bootstrap.group(group)                      //设置线程组.channel(NioSocketChannel.class)   //设置客户端通道实现类.handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {socketChannel.pipeline().addLast(new NettyClientHandle());}});System.err.println("client is ready......");//3.启动客户端去连接服务端ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 8888).sync();//4.设置通道关闭监听(当监听到通道关闭时,关闭client)channelFuture.channel().closeFuture().sync();} finally {group.shutdownGracefully();}}
}

2.4.netty client handler端编写

package com.zpb.netty.netty.helloWorld;import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;/*** @dec :* @Date: 2019/11/24* @Auther: pengbo.zhao* @version: 1.0* @demand:*/
public class NettyClientHandle extends ChannelInboundHandlerAdapter{//如果client 端服务启动完成后@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {System.err.println("client "+ctx);ctx.writeAndFlush(Unpooled.copiedBuffer("hello,netty server...",CharsetUtil.UTF_8));}//当通道有读事件时@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf byteBuf = (ByteBuf) msg;System.err.println("服务器端回复消息:"+byteBuf.toString(CharsetUtil.UTF_8));System.err.println("服务器端地址是:"+ctx.channel().remoteAddress());}//当通道有异常时@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}
}

五.Netty三种任务队列的使用

当我们在处理的handle中如果出现了阻塞的情况,或者处理业务逻辑比较耗时,我们不能让程序处于阻塞,

当有客户端请求时,我们想让程序定时的去执行业务逻辑,

当需要对一些用户需要进行推送活动时,根据用户标识,找到对应的 Channel 引用,向该用户推送特定消息时

可以采用以下三种任务队列:

1.提交到execute(Runnable command)中时

ctx.channel().eventLoop().execute(new Runnable() { })业务逻辑交给线程去处理,线程不会阻塞在这里,而是直接返回,直到有数据才返回给客户端,如果有多个线程runnable需要处理,那么只能等上一个处理完才会处理下一个,(假如第1个任务需要10S,第2个需要20s,执行完共需30S)

2.提交到 scheduledTaskQueue中

schedule(Runnable command, long delay, TimeUnit unit)①Runnable command:执行业务逻辑处理的线程② long delay:定时时长③TimeUnit unit:定时类型业务逻辑交给定时线程去处理。

3.通过传输的内容的标识

在解码客户端发送的内容中,读取到客户端的特殊标识,利用这个标识来进行推送消息处理,这个在粘包、拆包中进行说明

相关文章:

什么是Netty

一&#xff0e;Netty介绍 1.什么是netty Netty 是由 JBOSS 提供的一个 Java 开源框架。Netty 提供异步的、基于事件驱动的网络应用程序框架&#xff0c;用以快速开发高性能、高可靠性的网络 IO 程序,是目前最流行的 NIO 框架&#xff0c;Netty 在互联网领域、大数据分布式计算…...

SpringCloud:统一网关Gateway

目录 1、网关介绍 2、搭建网关服务 3、路由断言工厂 4、路由过滤器 5、全局过滤器GlobalFilter 6、过滤器执行顺序 7、跨域问题处理 1、网关介绍 网关(Gateway)又称网间连接器、协议转换器。网关在网络层以上实现网络互连&#xff0c;是复杂的网络互 连设备&#xff0…...

【独家】华为OD机试 - 最差产品奖(C 语言解题)

最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧文章目录 最近更新的博客使用说明本期…...

​力扣解法汇总1599. 经营摩天轮的最大利润

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;力扣 描述&#xff1a; 你正在经营一座摩天轮&#xff0c;该摩天轮共有 4 个座舱 &#xff0c;每个座舱…...

MySQL-常见的五种索引

什么是索引&#xff1f; 百度百科&#xff1a;在关系数据库中&#xff0c;索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构&#xff0c;它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。索引的作用相当于…...

Linux学习第二十三节-压缩和解压缩和tar打包工具

1.压缩与解压缩&#xff08;不常用&#xff09;①Linux独有压缩格式及命令工具: gzip---> .gz bzip2---> .bz2 xz---> .xz②压缩命令格式&#xff1a; 压缩命令&#xff1a;gzip [选项] 文件名 常用选项&#xff1a;-d 解压缩 压缩命令&#xff1a;bzip2 [选项] 文件名…...

没有钱怎么创业?一分钱没有如何能创业成功?

限制人创业成功的从来都不是资金&#xff0c;而是能力&#xff0c;这个道理很多人都可能不懂&#xff0c;多数人习惯了庸庸碌碌、日复一日地打工行为&#xff0c;却不知如何创业&#xff0c;那么&#xff0c;没有钱怎么创业&#xff1f;一分钱没有如何能创业成功呢&#xff1f;…...

【操作系统原理实验】银行家算法模拟实现

选择一种高级语言如C/C等&#xff0c;编写一个银行家算法的模拟实现程序。1) 设计相关数据结构&#xff1b;2) 实现系统资源状态查看、资源请求的输入等模块&#xff1b;3) 实现资源的预分配及确认或回滚程序&#xff1b;4) 实现系统状态安全检查程序&#xff1b;5) 组装各模块…...

java医院云HIS系统:融合B/S版电子病历系统 能与公卫、PACS等各类外部系统融合

医院HIS系统源码 云HIS系统源码&#xff1a;SaaS运维平台完整文档 有源码&#xff0c;有演示 java基层医院云his系统 融合B/S版电子病历系统&#xff0c;支持电子病历4级 拥有自主知识产权。 看演示及源码可私信我哦&#xff01; 一、系统概述 一款满足二甲医院、基层医疗机构…...

单线激光雷达(SICK)驱动安装及时空标定

一、引言 1、AGV需要同时具备定位、避障与导航的功能&#xff0c;其中避障对于雷达本身的分辨率、精度要求并不是很高&#xff0c;只需要能够根据预设定的雷达扫描范围准确避开障碍物即可&#xff0c;故本文以TIM240&#xff08;SICK激光类雷达&#xff09;为例介绍实现多雷达…...

Java IO流

Java IO流 文章目录Java IO流什么是IO流InputStreamFlieInputStream示例OutputStream示例字符的读取与写入READER方法WRITER方法利用Scanner和PrintWriter简化字符的读写ScannerPrintWriter什么是IO流 前面我们介绍了Java中对文件的操作以及file类的了解&#xff0c;但是file类…...

LeetCode - 1653 使字符串平衡的最少删除次数

目录 题目来源 题目描述 示例 提示 题目解析 算法源码 题目来源 1653. 使字符串平衡的最少删除次数 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给你一个字符串 s &#xff0c;它仅包含字符 a 和 b​​​​ 。 你可以删除 s 中任意数目的字符&#xff0c;使得 …...

【微信小程序】-- 页面事件 - 上拉触底 - 案例(二十七)

&#x1f48c; 所属专栏&#xff1a;【微信小程序开发教程】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &…...

《超导电子技术及其应用》学习日志(二)

约瑟夫森效应 约瑟夫森理论 约瑟夫森方程 &#xff08;1&#xff09;每一个库柏对都可视为质量为2m、电量为2e的复合载流子&#xff0c;定向运动速度v就是库柏相对质心的速度。处于超导态的库柏对凝聚于同一量子态&#xff0c;运载电流时具有完全相同的动量P。用微观波函数来…...

微信小程序this指向问题

前言 最近在开发微信小程序时不时会遇到一个很奇怪的问题&#xff0c;有些情况下用 this.setData 可以改变视图显示&#xff0c;有些情况下使用 this.setData 无效&#xff0c;这又是为什么呢&#xff1f; 问题描述 在解释这个问题前&#xff0c;我们先来看两段代码&#xff1…...

【报错】paddle相关报错和处理方法

1 报错 😱😱😱 ModuleNotFoundError: No module named paddle 2 解决方法 💉💉💉 pip --default-timeout=100 install paddlepaddle -i http://pypi.douban.com/simple --trusted-host pypi.douban.com 🎉🎉🎉🎉🎉🎉 1 报错 😱😱😱 from paddl…...

unity的安装配置和第一个游戏-unity开学第一课

许多的小伙伴学编程语言其实是因为玩游戏&#xff0c;玩着玩着就想写游戏了&#xff0c;于是开始学习c学习C#学习java&#xff0c;但相比之下C#的操作会更加容易&#xff0c;所以就开始学习unity来编游戏了。这里就就算是unity开学第一课啦-unity的安装配置和第一个游戏。 文章…...

Elsevier上传LaTeX 修改稿踩坑

背景 千辛万苦修改完论文&#xff0c;结果发现要求上传可编辑文件&#xff0c;tex上传真的太难了&#xff0c;一堆坑&#xff0c;尤其是编译错误&#xff0c;要等系统创建pdf后才能找到。中间还打了北京的客服电话&#xff0c;结果他们那边并不懂相关的东西。说latex是第三方公…...

秒懂算法 | 搜索基础

本篇介绍了BFS和DFS的概念、性质、模板代码。 01、搜索简介 搜索,就是查找解空间,它是“暴力法”算法思想的具体实现。 暴力法(Brute force,又译为蛮力法):把所有可能的情况都罗列出来,然后逐一检查,从中找到答案。这种方法简单、直接,不玩花样,利用了计算机强大的…...

Flutter 自定义今日头条版本的组件,及底部按钮切换静态样式

这里写目录标题1. 左右滑动实现标题切换&#xff0c;点击标题也可实现切换&#xff1b;2. 自定义KeepAliveWrapper 缓存页面&#xff1b;2.2 使用3. 底部导航切换&#xff1b;4. 自定义中间大导航&#xff1b;5.AppBar自定义顶部按钮图标、颜色6. Tabbar TabBarView实现类似头条…...

基于服务器使用 apt 安装、配置 Nginx

&#x1f9fe; 一、查看可安装的 Nginx 版本 首先&#xff0c;你可以运行以下命令查看可用版本&#xff1a; apt-cache madison nginx-core输出示例&#xff1a; nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验

一、多模态商品数据接口的技术架构 &#xff08;一&#xff09;多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如&#xff0c;当用户上传一张“蓝色连衣裙”的图片时&#xff0c;接口可自动提取图像中的颜色&#xff08;RGB值&…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

Module Federation 和 Native Federation 的比较

前言 Module Federation 是 Webpack 5 引入的微前端架构方案&#xff0c;允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

微服务商城-商品微服务

数据表 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 商…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

06 Deep learning神经网络编程基础 激活函数 --吴恩达

深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...

【Java学习笔记】BigInteger 和 BigDecimal 类

BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点&#xff1a;传参类型必须是类对象 一、BigInteger 1. 作用&#xff1a;适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...