Spring Boot 中使用 Netty
2025/4/15
向
一、什么是Netty
Netty 是 Java 中一个非常高性能的网络通信框架,用来开发服务器和客户端程序,主要用于处理 TCP/UDP 的网络连接,比如:
-
聊天服务
-
实时推送
-
高并发网络通信(比如游戏、IoT、金融系统)
你可以把 Netty 理解为一种比 Java 原生 Socket 更方便、性能更强的“网络搭建工具”。再详细了解Netty的工作原理之前,我们先来看一下Java中最简单的客户端和服务器之间的连接。
二、最简单的 Java 网络通信
2.1什么是“客户端”和“服务端”?
我们先理解一个现实生活的比喻:奶茶店点单系统
-
服务端(Netty 服务):奶茶店(固定位置,等待别人来点单)
-
客户端(浏览器、手机 App、Netty 客户端):顾客(谁想喝奶茶谁来)
-
通信方式(TCP):电话(通过电话点单)
还可以更加省略一点来说就是 💬 一个人发送消息(客户端) ➜ 另一个人接收并回复(服务端)
2.2服务端
import java.io.*;
import java.net.*;public class Server {public static void main(String[] args) throws Exception {ServerSocket serverSocket = new ServerSocket(8080); // 在8080端口等别人来找System.out.println("服务端启动,等待客户端连接...");Socket socket = serverSocket.accept(); // 有人来连接,就接收它System.out.println("客户端连接进来了");// 输入输出流:用来读写数据BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); // 读PrintWriter out = new PrintWriter(socket.getOutputStream(), true); // 写String line;while ((line = in.readLine()) != null) {System.out.println("收到客户端消息:" + line);out.println("我收到了:" + line); // 回给客户端}socket.close(); // 关闭连接serverSocket.close();}
}
2.3客户端
import java.io.*;
import java.net.*;public class Client {public static void main(String[] args) throws Exception {Socket socket = new Socket("127.0.0.1", 8080); // 连接本机服务端System.out.println("连接服务端成功!");// 输入输出BufferedReader userInput = new BufferedReader(new InputStreamReader(System.in)); // 你键盘输入PrintWriter out = new PrintWriter(socket.getOutputStream(), true); // 发消息BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); // 收消息String msg;while ((msg = userInput.readLine()) != null) {out.println(msg); // 发给服务端String reply = in.readLine(); // 读取服务端返回System.out.println("服务端说:" + reply);}socket.close();}
}
2.4 服务端和客户端之间的通信
首先是服务端先启动,会有如下显示,同时告诉顾客我家的店的端口号是8080。
服务端启动,等待客户端连接...
然后有顾客想买东西,通过 new Socket("127.0.0.1", 8080); // 连接本机服务端,即走进服务器店的大门8080。而在服务器这端,通过serverSocket.accept(); 看见有人来连接,就接收它,服务它。这时候客户端会输出如下
连接服务端成功!
服务端会输出如下:
客户端连接进来了
在客户端通过控制台输入:hello后,即通过如下代码接收到了你的输入,并存放在userInput变量中。
BufferedReader userInput = new BufferedReader(new InputStreamReader(System.in));
客户端通过out对象发消息
PrintWriter out = new PrintWriter(socket.getOutputStream(), true); // 发消息
客户端通过in对象接受消息
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); // 收消息
当 msg = userInput.readLine()) != null ,即当检测到客户端要发送消息就执行如下代码:
out.println(msg); // 发给服务端
String reply = in.readLine(); // 读取服务端返回
System.out.println("服务端说:" + reply);
out.println(msg)后,就将信息发送到了服务器端,服务器端就会输出如下
收到客户端消息:hello
同时在服务器端通过 out.println("我收到了:" + line); 回给客户端,客户端通过reply接收到消息,客户端就会输出
服务端说:我收到了:hello
2.5 客户端和服务器端的关系如下:
角色 | 作用 |
---|
Server | 永远在等别人来(监听端口) |
Client | 主动发起连接 |
Input/Output | 收发消息用的“通道” |
二、为什么需要线程模型?(Thread Model)
理解了基础的服务端和客户端通信,我们可以继续深入,了解一些稍微复杂一点的概念,即线程。
在前面那个简单的服务端/客户端例子中,服务端是“串行”的,意思是:
-
它在等待一个客户端连接。
-
收到消息后再回复,接着等待下一个连接。
但是如果你有很多客户端同时发消息,服务端就会变得很慢,因为它只能一个一个地处理请求。
所以,我们需要更高效的处理方式:并发编程并发编程意味着能够同时处理多个任务,不等一个任务完成再开始下一个。而且每个任务都不会相互阻塞。这就是 线程池 和 事件循环模型 的价值所在。在 Netty 中:
-
线程池:多个线程可以同时处理多个连接。
-
事件循环模型:每个线程(事件循环)只负责自己的任务,它会不停地轮询事件,比如客户端连接、数据读取等。
三、什么是“阻塞”和“非阻塞”?
❌ 阻塞:你去餐厅吃饭,服务员给你一个菜单,但你必须等着他们准备好菜才能吃,期间你不能干别的事。
✅ 非阻塞:你点菜后,服务员会告诉你“稍等一会儿”,然后你可以做其他事。只要菜做好了,服务员会告诉你,打断你做其他事,给你菜。
TCP 通信中的阻塞和非阻塞:
-
阻塞:当你发起连接或请求时,程序会一直等待,直到连接建立或数据返回。
-
非阻塞:发起请求后,程序不再等待,会继续执行其他任务。如果有返回结果,程序会处理返回。
Netty 默认就是 非阻塞 的,这样它能同时处理很多连接,不会被一个请求堵住。
四、Netty 是如何处理高并发的?
Netty通过使用一个线程模型 EventLoop(事件循环)来处理高并发。EventLoopGroup:管理多个线程(可以理解为多个服务员),负责处理网络事件。EventLoop:每个线程负责自己的一部分任务,比如处理某一个客户端的请求。
举例来看就是:
- 一个服务端线程,负责监听连接(等待“顾客”进店)。
- 多个工作线程,负责实际的通信(帮“顾客”点单、做菜)。
4.1 EventLoop 和 NIO 的关系
Netty 使用了 NIO(非阻塞 IO) 模型。NIO 让一个线程能处理多个连接。具体来说:
-
使用 Selector 轮询(检查)每个连接的状态,看是否有数据到达。
-
使用 Channel 来表示网络连接。
-
使用 Buffer 来读取和写入数据。
这个模型让 Netty 在面对数千个并发连接时,也能保持高效。
总结来看,Netty的EventLoopGroup管理多个线程,每个线程只干特定的事情,假设某个线程只干连接客户端这个事情,又由于Netty引入了NIO模型,所以又让这个负责处理连接的线程具备了同时处理多个连接请求的能力。
五、实际的 Netty 服务端示例
public class EchoServer {public static void main(String[] args) throws InterruptedException {EventLoopGroup bossGroup = new NioEventLoopGroup(1); // 负责接收连接EventLoopGroup workerGroup = new NioEventLoopGroup(); // 负责处理请求try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new EchoServerHandler());ChannelFuture f = b.bind(8080).sync(); // 绑定端口,开始监听f.channel().closeFuture().sync();} finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}// 处理客户端发来的消息public static class EchoServerHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {// 接收到数据后直接写回给客户端System.out.println("收到消息:" + msg);ctx.writeAndFlush(msg);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {cause.printStackTrace();ctx.close(); // 出现异常关闭连接}}
}
- ServerBootstrap:是 Netty 中用于启动服务端的核心类,启动 Netty 服务端。
- bossGroup 和 workerGroup:管理事件循环,分别处理接收连接和处理数据的任务。
- EchoServerHandler:是我们自定义的业务处理逻辑,收到客户端的消息就原封不动地回传。
六、实际使用的Netty
6.1 NettyServer类
ServerBootstrap:Netty服务器启动的核心类。
ServerBootstrap serverBootstrap = new ServerBootstrap().group(bossGroup, workGroup).channel(NioServerSocketChannel.class).childHandler(new ServerChannelInitializer(delimiter, maxFrameLength)).localAddress(socketAddress).option(ChannelOption.SO_BACKLOG, 1024).childOption(ChannelOption.SO_KEEPALIVE, true);
- .group(bossGroup, workGroup) 配置监听线程和工作线程。
.channel(NioServerSocketChannel.class)
: 这里指定了服务端的 Channel 类型。NioServerSocketChannel
适用于 NIO(非阻塞 IO),这是一种处理高并发的方式。.childHandler(new ServerChannelInitializer(delimiter, maxFrameLength))
: 为每个连接配置一个ChannelInitializer
,在每个连接初始化时(每个客户端连接时)会被调用。ServerChannelInitializer
是自定义的初始化类,配置如何处理数据的编解码、业务逻辑等。.localAddress(socketAddress)
: 配置绑定的本地地址和端口.option(ChannelOption.SO_BACKLOG, 1024)
: 配置服务器端的连接队列大小。队列最大长度设置为 1024。.childOption(ChannelOption.SO_KEEPALIVE, true)
: 设置 TCP KeepAlive,确保连接在空闲时依然存活。
6.1.1启动并绑定端口
ChannelFuture channelFuture = serverBootstrap.bind(socketAddress).sync();
-
.bind(socketAddress)
: 绑定到指定的socketAddress
,开始监听客户端的连接。 -
.sync()
: 阻塞方法,直到端口绑定成功并启动后,才会继续执行。ChannelFuture
用于获取当前操作的结果(是否成功绑定)
6.2 SeverChannelInitializer类
在NettyServer类中,我们是调用了SeverChannelInitializer类的,我们使用SeverChannelInitializer类来配置如何处理数据的编解码、业务逻辑等。当每个客户端连接进来时,配置它的 Channel 的“流水线”——也就是这个连接收到/发送数据时,按什么顺序怎么处理。可以把它理解为工厂生产线的“组装说明书”。
package com....nettyService;import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.logging.LoggingHandler;public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {private String DELIMITER;private int MAXFRAMELENGTH;public ServerChannelInitializer(String delimiter, int maxFrameLength) {DELIMITER = delimiter;MAXFRAMELENGTH = maxFrameLength;}@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {socketChannel.pipeline().addLast("logging", new LoggingHandler("DEBUG"));socketChannel.pipeline().addLast("decoder", new HL7Decoder());socketChannel.pipeline().addLast("encoder", new HL7Encoder());socketChannel.pipeline().addLast(new NettyServerHandler());}}
SeverChannelInitializer首先继承了ChannelInitializer<SocketChannel>,这样没有一个新的连接的时候Netty 就会调用 initChannel()
方法,给这个连接安装一套“处理器组合”(pipeline)。
而这一套“处理器组合”当接收到客户端发送的消息执行顺序如下:
【客户端】==> socketChannel
↓
[LoggingHandler](打印日志)
↓
[HL7Decoder](解码消息)
↓
[NettyServerHandler](业务处理)
当服务端要回复消息,其执行顺序如下:
NettyServerHandler.write()
↓
[HL7Encoder](编码为字节)
↓
[LoggingHandler](打印)
↓
【客户端】
6.3 NettySeverHandler类
在SeverChannelInitializer类中,其写好了业务处理顺序,在处理业务时,其处理业务的核心是NettySeverHandler类来实现的
package com.....nettyService;import com...component.commons.utils.BeanUtils;
import com...emergency.service.impl.BS2800MPacketParse;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;@Component
public class NettyServerHandler extends SimpleChannelInboundHandler<String> {private static final Logger logger = LoggerFactory.getLogger(NettyServerHandler.class);private BS2800MPacketParse bs2800MPacketParse = BeanUtils.getBean(BS2800MPacketParse.class);/*** 装载所有客户端channel的组*/private static final Map<String, Channel> ipChannelMap = new HashMap<>();/*** 客户端连接过来会触发*/@Overridepublic void channelActive(ChannelHandlerContext channelHandlerContext) throws Exception {Channel channel = channelHandlerContext.channel();ipChannelMap.put(channel.remoteAddress().toString(), channel);logger.info("客户端连接:" + channelHandlerContext);}/*** 客户端发消息过来会触发*/@Overridepublic void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception {Channel channel = channelHandlerContext.channel();logger.info("服务端接收到客户端消息");
// logger.info("发送消息的客户端地址:" + channel.remoteAddress());logger.info("发送消息的客户端所发消息:" + msg);String result = msg;String msa = handleParams(channelHandlerContext, result);if (ObjectUtils.isNotEmpty(msa)) {channel.writeAndFlush(msa);}}@Overridepublic void channelReadComplete(ChannelHandlerContext channelHandlerContext) throws Exception {super.channelReadComplete(channelHandlerContext);}@Overridepublic void channelInactive(ChannelHandlerContext channelHandlerContext) throws Exception {Channel channel = channelHandlerContext.channel();// 当通道变为非活动状态(断开连接)时,将其从 ChannelGroup 中移除String ip = channel.remoteAddress().toString();if (ipChannelMap.containsKey(ip)) {ipChannelMap.remove(ip);if (!channel.isActive() || channel == null) {channelHandlerContext.close();}}logger.info("客户端地址为:" + ip + "的连接已断开");}/*** 发生异常触发*/@Overridepublic void exceptionCaught(ChannelHandlerContext channelHandlerContext, Throwable cause) throws Exception {logger.warn(cause.toString());}/*** 处理接收报文消息*/public String handleParams(ChannelHandlerContext channelHandlerContext, String msg) {String msa = null;SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");Channel channel = channelHandlerContext.channel();if (channel.remoteAddress().toString().contains("10.10.51.213")) {if (ObjectUtils.isNotEmpty(msg)) {String result[] = msg.split("\r");if (ObjectUtils.isNotEmpty(result) && result.length > 0) {String msh = null;for (String string : result) {if (string.contains("MSH")) {msh = string;}}if (msh.contains("ORU^R01")) {Date date = new Date();String temp[] = msh.split("\\|", -1);if (ObjectUtils.isNotEmpty(temp) && temp.length > 9) {msa = "MSH|^~\\&|||||" + dateFormat.format(date) + "||ACK^R01|" + temp[9] + "|P|2.3.1||||0||ASCII|||";String str = "MSA|AA|" + temp[9] + "|Message accepted|||0|";msa = msa + "\r" + str;Map<String, String> paramMap = new HashMap<>();paramMap.put(temp[9], msg);bs2800MPacketParse.parse(msg);return msa;}}}}}return msa;}}
6.3.1继承
NettyServerHandler继承SimpleChannelInboundHandler<String> 每次接收到客户端消息(已经是 String
类型,说明解码器已完成解码),就会触发 channelRead0()
方法。我们可以在这里处理逻辑、保存数据、做回复等
6.3.2channelActive
有客户端连接进来时,Netty 会自动调用这个方法。将客户端的 Channel 保存到 ipChannelMap
中,方便后面用 IP 找到连接。同时打印客户端连接信息。
6.3.3channelRead0
每当客户端发一条消息过来,就会自动执行这里!先获取当前的 Channel(对应客户端)
Channel channel = channelHandlerContext.channel();
打印日志,方便调试看到收到的数据
相关文章:
Spring Boot 中使用 Netty
2025/4/15 向 一、什么是Netty Netty 是 Java 中一个非常高性能的网络通信框架,用来开发服务器和客户端程序,主要用于处理 TCP/UDP 的网络连接,比如: 聊天服务 实时推送 高并发网络通信(比如游戏、IoT、金融系统&a…...
【Leetcode-Hot100】最大子数组和
题目 解答 class Solution(object):def maxSubArray(self, nums):""":type nums: List[int]:rtype: int"""len_nums len(nums)result -1e5left_fit, right_fit 0, len_nums-1if len_nums 1:return nums[0]sum_left, sum_right 0, 0while r…...
Android 项目 Camera 问题:Fail to connect to camera service
问题与处理策略 问题描述 在 Android 项目中,使用相机时,报如下错误 java.lang.RuntimeException: Fail to connect to camera service# 翻译无法连接到相机服务问题原因 通常情况是应用没有获取到相机权限,导致连接相机服务失败 Android…...
Java二叉树深度解析:结构、算法与应用实践指南
一、二叉树核心概念体系 1. 二叉树基础定义 graph TBA((根节点)) --> B((左子节点))A --> C((右子节点))B --> D((叶子节点))B --> E((叶子节点))C --> F[null]C --> G((叶子节点)) 2. 二叉树类型对比 类型结构特性典型应用场景普通二叉树任意节点最多两…...
阿里FPGA XCKU3P开箱- 25G 光纤
阿里FPGA XCKU3P开箱 - Hello-FPGA - 博客园 25G 光纤 板子有2个SFP的光纤接口,最大支持25G速率,使用ibert 进行验证,SFP在BANK227的GTY 接口。 ibert 配置如下: 测试 测试符合预期,确认了SFP的具体位置 和 支持的速…...
深度学习之微积分
2.4.1 导数和微分 2.4.2 偏导数 回调函数(3)C#
原接口定义请参照高级语言调用C接口(二)回调函数(2) 我们直接来看C#的接口定义 [DllImport("XXX.dll")]public static extern IntPtr Init(string pcPayDeviceIP, int usTlsPort, OnPayResult onPayResult); 委托定义 [UnmanagedFunctionPointer(CallingConvention…...
ns-3中UDP饱和流发包时间间隔设置最合理值
ns3的官方手册很全,相关书籍也是有的,官网先贴在这里: ns-3 | a discrete-event network simulator for internet systemsa discrete-event network simulator for internet systemshttps://www.nsnam.org/相关的脚本介绍也都有一些…...
深度学习(第1章——神经网络原理和Pytorch入门)
前言: 本章将讲解神经网络原理,神经元如何处理输入并输出,什么是梯度,多层感知机中梯度的计算,Pytoch自动梯度效果,如何使用原生Python实现一个简单的神经网络,以及对应Pytorch实现。 神经网络原…...
使用DeepSeek AI高效降低论文重复率
一、论文查重原理与DeepSeek降重机制 1.1 主流查重系统工作原理 文本比对算法:连续字符匹配(通常13-15字符)语义识别技术:检测同义替换和结构调整参考文献识别:区分合理引用与不当抄袭跨语言检测:中英文互译内容识别1.2 DeepSeek降重核心技术 深度语义理解:分析句子核心…...
【3D文件】3D打印迪迦奥特曼,3D打印的迪迦圣像,M78遗迹管理局,5款不同的3D打印迪迦免费下载,总有一款适合你
【3D文件】3D打印迪迦奥特曼,3D打印的迪迦圣像,M78遗迹管理局,5款不同的3D打印迪迦免费下载,总有一款适合你 资源下载: 3D文件AI生成器,机器学习生成,AI生成3D文件,3D打印迪迦奥特…...
【未解决】Spring AI 1.0.0-M6 使用 Tool Calling 报错,请求破解之法
1.报错 2.Java 代码 2.1 pom.xml <dependencyManagement><dependencies><!-- Spring AI --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-bom</artifactId><version>1.0.0-M6</ver…...
第 2 篇:快速上手 Framer Motion(实操入门)
1. 环境准备 在开始使用 Framer Motion 之前,你需要先确保你的开发环境中已经设置好了 React 项目。我们将使用 Next.js 作为示例,如果你是使用其他 React 框架,步骤也基本相同。 1.1 创建一个 Next.js 项目 如果你还没有创建 Next.js 项目…...
如何写好合同管理系统需求分析
引言 在当今企业数字化转型的浪潮中,合同管理系统作为企业法律合规和商业运营的重要支撑工具,其需求分析的准确性和完整性直接关系到系统建设的成败。本文基于Volere需求过程方法论,结合江铃汽车集团合同管理系统需求规格说明书实践案例&…...
C语言自定义类型详解一:结构体(内存对齐)
结构体的声明: 结构体是一些值的集合,这些值是成员变量,结构体的每个成员可以是不同类型的变量(包括其他结构体变量) 类如:描述一个学生 struct Stu {char name[200];int age;char sex[5];//性别char id…...
GitHub配置密钥
1.生成SSH密钥 1)检查 SSH 密钥是否存在 首先,确认是否已经在本地系统中生成了 SSH 密钥对。可以通过以下命令检查: ls -al ~/.ssh 在命令输出中,应该能看到类似 id_rsa 和 id_rsa.pub 这样一对文件。如果这些文件不存在&#…...
PyTorch逻辑回归总结
目录 PyTorch逻辑回归总结神经网络基础基本结构学习路径 线性回归简单线性回归多元线性回归 逻辑回归核心原理损失函数 梯度下降法基本思想关键公式学习率影响 PyTorch实现数据准备模型构建代码优化 核心概念对比 PyTorch逻辑回归总结 神经网络基础 基本结构 输入节点隐藏节…...
Browser-use 是连接你的AI代理与浏览器的最简单方式
AI MCP 系列 AgentGPT-01-入门介绍 Browser-use 是连接你的AI代理与浏览器的最简单方式 AI MCP(大模型上下文)-01-入门介绍 AI MCP(大模型上下文)-02-awesome-mcp-servers 精选的 MCP 服务器 AI MCP(大模型上下文)-03-open webui 介绍 是一个可扩展、功能丰富且用户友好的…...
nginx自编译重现gzip和chunked的现象
前言 最近做项目,发现一个比较好玩的事,nginx的module gzip模式默认支持1KB压缩,和chunked返回,本来现在的很多框架都很完善了,但是,一些新语言框架或者一些老旧框架会不能完整支持chunked,导致…...
RNN - 循环神经网络(概念介绍)
RNN 潜变量自回归模型 使用潜变量 h t h_t ht 总结过去信息 p ( h t ∣ h t − 1 , x t − 1 ) p(h_t | h_{t-1}, x_{t-1}) p(ht∣ht−1,xt−1) p ( x t ∣ h t , x t − 1 ) p(x_t | h_t, x_{t-1}) p(xt∣ht,xt−1) 循环神经网络 更新隐藏状态࿱…...
OpenCV的详细介绍与安装(一)
1.OpenCV概述 OpenCV是一个开源的计算机视觉和机器学习软件库, 它轻量级而且高效——由一系列 C 函数和少量 C 类构成,它支持多种编程语言(如C、Python、Java),并可在Windows、Linux、macOS、Android和iOS等平台上运行…...
50、Spring Boot 详细讲义(七) Spring Boot 与 NoSQL
七 Spring Boot 与 NoSQL 目录 MongoDB 集成Redis 集成Elasticsearch 集成1、 MongoDB 集成 1.1 MongoDB 概述 1.1.1 MongoDB 的基本概念 文档型数据库: 数据存储为类似 JSON 的文档结构(BSON 格式)。每个文档由字段和值对组成,类似于键值对。支持嵌入式文档和数组,灵活…...
微信小程序组件传参
微信小程序组件传参感觉和vue还是挺像的 父组件向子组件传参 在小程序中父组件子组件传参,主要使用properties属性。演示下: 创建组件文件夹component,创建组件demoComponent,记得创建的时候选择组件,不是page页面 …...
C++实用函数:bind
本篇来介绍了C++中bind功能。 1 std::bind 在 C++ 里,std::bind 是一个函数模板,其作用是创建一个可调用对象,该对象可绑定到一组参数上。std::bind 的函数原型如下: template< class F, class... Args > /*unspecified*/ bind( F&& f, Args&&...…...
C# 程序结构||C# 基本语法
原文:C# 程序结构_w3cschool (注:本文为教程文章,请勿标记为付费文章!特此声明) 本节我们将学习 C# 编程语言的结构,为了让大家能够对 C# 程序结构有个更好的理解,我们会先演示一个…...
分库分表-除了hash分片还有别的吗?
在分库分表的设计中,除了常见的 Hash 分片,还有多种策略根据业务场景灵活选择。以下是几种主流的分库分表策略及其应用场景、技术实现和优缺点分析,结合项目经验(如标易行投标服务平台的高并发场景)进行说明: 一、常见分库分表策略 1. 范围分片(Range Sharding) 原理:…...
单片机非耦合业务逻辑框架
在小型单片机项目开发初期,由于业务逻辑相对简单,我们往往较少关注程序架构层面的设计。 然而随着项目经验的积累,开发者会逐渐意识到模块间的耦合问题:当功能迭代时,一处修改可能引发连锁反应。 此时,构…...
WordPress - 此站点出现严重错误
本篇讲 当WordPress出现 此站点出现严重错误 时,该如何解决。 目录 1,现象 2, FAQ 3,管理Menu无法打开 下面是详细内容。 1,现象 此站点出现严重错误(このサイトで重大なエラーが発生しました&#x…...
Java EE(8)——线程安全总结(翻新版)——定时器(Timer)线程池(ThreadPoolExecutor)
1.Timer 1.1Timer基本介绍 1.Timer的主要作用 任务调度:Timer允许你安排一个任务在未来的某个时间点执行,或者以固定的间隔重复执行 后台执行:Timer可以使用一个后台线程来执行任务,这意味着调度和执行任务不会阻塞主线程(主线程…...
#[特殊字符]Rhino建模教程 · 第一章:正方体建模入门
🦏Rhino建模教程 第一章:正方体建模入门 本章将从最基础的操作入手,带你一步步掌握Rhino建模的核心流程,适合新手或需要复习基础的用户。 🎯 目标:制作一个带凹槽、圆角、封盖的正方体模型,并…...
How to run ERSEM
Build ERSEM Make a “build” folder, and go into the build folder. Create “build_archer2.edit.sh” #!/usr/bin/env bash# Script for compiling FVCOM-FABM-ERSEM for ARCHER2 # # The build is split into three phases: # # 1) Build the FABM-ERSEM library. Her…...
关于QT5项目只生成一个CmakeLists.txt文件
编译器自动检测明明可以检测,Kit也没有报红 但是最后生成项目只有一个文件 一:检查cmake版本,我4.1版本cmake一直报错 cmake3.10可以用 解决之后还是有问题 把环境变量加上去:...
C++ string类
1.标准库中的string类 在 C 里,string类属于标准库的一部分,它在<string>头文件中定义,用于处理和操作字符串。 1.1string类的常用接口说明 1.1.1. string类对象的常见构造 string() (重点) 构造空的string类…...
如何使用ChatGPT撰写短视频爆款文案
短视频已经成为了互联网内容消费的重要形式,吸引观众的眼球成为内容创作者的首要任务。在短视频平台的内容过载中,如何写出一篇能够迅速吸引观众点击、分享并获得高互动的爆款文案,是每个短视频创作者都在追求的目标。今天,我们将…...
基于Tesseract与Opencv的电子发票识别[1]
本文我们将尝试使用tesseract识别电子发票上的信息并不断提高识别准确率,是一个逐渐调整的过程,仅用于记录研究过程。 图像识别:使用tesseract识别。图像预处理:使用OpenCV等图像处理库对发票图像进行预处理,如灰度化…...
数据库—函数笔记
一,数据库函数的分类 内置函数(Built-in Functions) 数据库系统自带的函数,无需额外定义即可直接调用。 聚合函数:对数据集进行计算(如 SUM, AVG, COUNT)。 字符串函数:处理文本数据…...
产品研发流程说明记录
1. 前言 在小型公司,产品研发流程通常较为简单,需求提出后经过简单评审便直接开发上线。而在中大型互联网公司,研发流程更加规范和系统,涉及多部门协作和多环节把控。本文将详细介绍一个标准的产品需求研发流程,帮助相…...
智慧城市:如同为城市装上智能大脑,开启智慧生活
智慧城市的概念随着信息技术的飞速发展而逐渐兴起,它通过集成物联网、大数据、人工智能和数字孪生等先进技术,为城市管理和居民生活带来了前所未有的智能化变革。本文将深入探讨这些核心技术及其在智慧城市的典型应用场景,展示智慧城市如何提…...
游戏测试入门知识
高内聚指的是一个模块或组件内部的功能应该紧密相关。这意味着模块内的所有元素都应该致力于实现同一个目标或功能,并且该模块应当尽可能独立完成这一任务。 低耦合则是指不同模块之间的依赖程度较低,即一个模块的变化对其它模块造成的影响尽可能小。理…...
Sentinel源码—2.Context和处理链的初始化二
大纲 1.Sentinel底层的核心概念 2.Sentinel中Context的设计思想与源码实现 3.Java SPI机制的引入 4.Java SPI机制在Sentinel处理链中的应用 5.Sentinel默认处理链ProcessorSlot的构建 4.Java SPI机制在Sentinel处理链中的应用 (1)初始化Entry会初始化处理链 (2)初始化处…...
Java基础第20天-JDBC
JDBC为访问不同的数据库提供了统一的接口,为使用者屏蔽了细节问题,程序员使用JDBC可以连接任何提供了JDBC驱动程序的数据库系统,从而完成对数据库的各种操作 ResultSet 表示数据库结果集的数据表,通常通过执行查询数据库的语句生…...
VMware下Ubuntu空间扩容
目的: Ubuntu空间剩余不足,需要对Ubuntu进行扩容。 使用工具: 使用Ubuntu系统中的gparted工具进行系统扩容。 前提: 1、电脑有多余的未分配磁盘空间,比如我的Ubuntu磁盘G盘是200G,现在快满了,…...
第十一章 网络编程
在TCP/IP协议中,“IP地址TCP或UDP端口号”唯一标识网络通讯中的一个进程。 因此可以用Socket来描述网络连接的一对一关系。 常用的Socket类型有两种:流式Socket(SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)…...
Bad Request 400
之前一直以为400就是前端代码有问题 这下遇到了,发现是因为前后端不一致 后端代码注意:现在我写的int 前端请求 原因 :前后端不一致 💡 问题核心:后端 amount 类型是 int,但前端传了小数 237.31...
行业深度:金融数据治理中的 SQL2API 应用创新
金融行业作为数据密集型领域,面临着监管合规要求严苛、数据交互频次高、安全风险防控难度大等多重挑战。SQL2API 技术通过 “数据服务化 合规化” 的双重赋能,成为金融机构破解数据治理难题的核心工具,在多个关键场景实现突破性创新。 &…...
记录学习的第二十六天
还是每日一题。 今天这道题有点难度,我看着题解抄的。 之后做了两道双指针问题。 这道题本来是想用纯暴力做的,结果出错了。😓...
MySQLQ_数据库约束
目录 什么是数据库约束约束类型NOT NULL 非空约束UNIQUE 唯一约束PRIMARY KEY主键约束FOREIGN KEY外键约束CHECK约束DEFAULT 默认值(缺省)约束 什么是数据库约束 数据库约束就是对数据库添加一些规则,使数据更准确,关联性更强 比如加了唯一值约束&#…...
数据库ocp证书是什么水平
专业知识与技能:OCP 证书是对持证人在 Oracle 数据库管理、安装、配置、性能调优、备份恢复等方面专业知识和技能的权威认证。它要求考生通过一系列严格的考试,包括理论知识和实际操作能力的考核,以证明其具备扎实的 Oracle 数据库专业知识和…...