Netty入门指南之传统通信的问题

news2024/11/26 0:52:31

作者简介:☕️大家好,我是Aomsir,一个爱折腾的开发者!
个人主页:Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏-CSDN博客
当前专栏:Netty应用专栏_Aomsir的博客-CSDN博客

文章目录

  • 参考文献
  • 前言
  • 多线程版
  • 线程池版
  • 总结

参考文献

  • 孙哥suns说Netty
  • Netty官方文档

前言

前一篇文章中,我学到了与Netty有关的基础知识,了解NIO这个非阻塞式IO,那今天我们来聊聊传统的网络通信开发方式以及它所存在的问题,也就是使用 Socket的方式。Socket是阻塞式的IO,我们要做通信肯定得涉及多线程或者线程池的方式,这两种方式对于Socket都不友好,都有问题,如下详细分析一下。

注意:由于我们平常开发都是面向Tomcat开发的,很少会有机会能够接触Socket编程

多线程版

下面是多线程版网络通信的情况。从图中可以明显看出,随着客户端请求服务端的增加,服务端为处理这些请求不断创建新线程,而这一过程缺乏充分的限制,会无节制的进行创建。每次虚拟机创建线程都需要与操作系统进行通信,这会耗费时间和占用内存,导致内存使用量不断上升。此外,当所创建的线程数量超过了CPU核心数时,CPU就不得不进行轮转处理,这将导致CPU占用率飙升。
在这里插入图片描述

public class AomsirServer {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;

        try {
            serverSocket = new ServerSocket(8080);

            Socket socket = null;
            while (true) {
                socket = serverSocket.accept();
                new Thread(new AomsirServerHandler(socket)).start();
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (serverSocket != null) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

/**
 * 用于处理通信请求的线程
 */
class AomsirServerHandler implements Runnable {
    private Socket socket;

    public AomsirServerHandler(Socket socket) {
        this.socket = socket;
    }

    public void run() {
        BufferedReader bufferedReader = null;
        try {

            bufferedReader = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
            while (true) {
                String line = bufferedReader.readLine();
                if (line != null) {
                    System.out.println("line = " + line);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}
public class AomsirClient {
    public static void main(String[] args) {
        Socket socket = null;
        PrintWriter printWriter = null;
        try {
            socket = new Socket("127.0.0.1", 8080);
            printWriter = new PrintWriter(socket.getOutputStream());
            printWriter.write("send date to server ");
            printWriter.flush();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            if (printWriter != null) {
                printWriter.close();
            }
        }
    }
}

线程池版

在上述多线程版网络通信中,服务端以一种无限制的方式为每个请求创建新线程,这导致高并发下线程数量无限增加。为了解决这一问题,我们提出了一种解决方案,即使用线程池。这是一种基于池化思想的方法,通过在服务端启动时创建固定数量的线程(例如N个),来限制后续线程的创建。这N个线程将专用于后续请求的处理,不再创建新的线程。当一个请求完成处理后,线程将被放回线程池,以供后续请求使用。如果请求数量超过了线程池的线程数量,后续请求将进入队列等待。这一方法有效地解决了无节制创建线程的问题。

然而,尽管线程池解决了线程创建问题,它引入了新的潜在问题,即阻塞问题。举例来说,如果线程1分配给了客户端A,但客户端1在某一时刻发生阻塞,无法继续处理请求,线程1将不得不一直等待客户端A,无法返回线程池,导致服务端处理请求的效率降低。
在这里插入图片描述

public class AomsirServer1 {

    // 创建线程池
    private static ExecutorService executorService;
    
    // 初始化线程池
    static{
        executorService = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),20,
                         120L, TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(1000));
    }

    public static void main(String[] args) {
        ServerSocket serverSocket = null;

        try {
            serverSocket = new ServerSocket(8080);

            Socket socket = null;
            while (true) {
                socket = serverSocket.accept();
                //new Thread(new SunsServerHandler(socket)).start();
                //线程池
                executorService.execute(new AomsirServerHandler(socket));
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (serverSocket != null) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}


/**
 * 服务端处理客户端请求的线程
 */
class AomsirServerHandler implements Runnable {
    private Socket socket;

    public AomsirServerHandler(Socket socket) {
        this.socket = socket;
    }

    public void run() {
        BufferedReader bufferedReader = null;
        try {

            bufferedReader = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
            while (true) {
                String line = bufferedReader.readLine();
                if (line != null) {
                    System.out.println("line = " + line);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}
public class AomsirClient1 {
    public static void main(String[] args) {
        Socket socket = null;
        PrintWriter printWriter = null;
        try {
            socket = new Socket("127.0.0.1", 8080);
            printWriter = new PrintWriter(socket.getOutputStream());
            printWriter.write("send date to server ");
            printWriter.flush();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            if (printWriter != null) {
                printWriter.close();
            }
        }
    }
}

总结

在客户端-服务器结构中,使用BIO(阻塞I/O)进行网络通信时,无论是无限制地创建线程,还是通过线程池限制线程创建,都难以避免一个共同的问题:当客户端连接到服务器后,在一段时间内不进行通信,线程将被空闲浪费,导致资源的低效利用。

为了解决这个问题,我们可以采用NIO(非阻塞I/O)来处理网络通信。NIO允许服务器同时管理多个连接,而不需要为每个连接创建一个单独的线程。这使得服务器能够更高效地处理大量连接,减少了资源浪费。Netty是一个常见的工具,它底层使用了NIO,为我们开发者提供了更容易使用和管理的方式来构建高性能的网络应用

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

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

相关文章

管理类联考——写作——技巧篇——书写标点符号使用要求规范文档

写作答题卡书写标点符号使用要求规范文档 常用标点符号有逗号、句号、叹号、问号等 11 种&#xff0c;下面一一列举其用法和书写规范。 一、句号 用法&#xff1a;用于陈述句的末尾。 占格情况&#xff1a;占一格&#xff0c;写在格子左下方。 举例&#xff1a; 我看见妈妈走…

web3案例中解决交易所中 ETH与token都是0问题 并帮助确认展示是否成功

可能写了这么久 很多人会发现一个问 我们前面的案例 个人在交易所中的 自定义token 和 ETH 一直是放了个0 大家也不太敢确认是否真的有效 那么 很简单 我们操作 存入一些进交易所 不就ok了 我们 来看之前交易所写的代码 我们写了 depositEther 存入 ETH 和 depositToken 存入…

03 贝尔曼公式

贝尔曼公式 前言1、Motivating examples2、state value3、Bellman equation:Derivation4、Bellman equation:Matrix-vector form4、Bellman equation:Solve the state value5、Action value 前言 本文来自西湖大学赵世钰老师的B站视频。本节课主要介绍贝尔曼公式。 本节课概要…

海外问卷项目是怎么赚钱的?

大家好&#xff0c;我是橙河老师&#xff0c;今天聊一聊海外问卷项目是怎么赚钱的&#xff1f; 在海外国家&#xff0c;问卷调查这种商业模式一直都很流行&#xff0c;很多商业公司为了收集消费者的意见&#xff0c;会对外发有偿的调查问卷&#xff0c;从最开始的纸质调查&…

MySQL第五讲·关于外键和连接, 如何做到关联查询?

你好&#xff0c;我是安然无虞。 文章目录 外键和连接&#xff1a;如何做关联查询&#xff1f;如何创建外键&#xff1f;连接关联查询中的误区 外键和连接&#xff1a;如何做关联查询&#xff1f; 在实际的数据库应用开发过程中&#xff0c;我们经常需要把2个或2个以上的表进…

C语言函数初使用

目录 1知识点&#xff1a; 2一个小代码&#xff0c;后续知识点讲解&#xff1a; 3知识点&#xff1a; 定义函数 实例 函数声明 调用函数 函数参数 4总结&#xff1a; 1知识点&#xff1a; 函数是一组一起执行一个任务的语句。每个 C 程序都至少有一个函数&#xff0c;…

网站源码备份 [极客大挑战 2019]PHP1

打开题目 题目提示我们备份网站 我们输入/www.zip 下载zip文件&#xff0c;打开发现 打开index.php <?phpinclude class.php;$select $_GET[select];$resunserialize($select);?> 文件包含class.php&#xff0c;get传参一个select函数&#xff0c;反序列化select参…

HMM与LTP词性标注之命名实体识别与HMM

文章目录 知识图谱介绍NLP应用场景知识图谱&#xff08;Neo4j演示&#xff09;命名实体识别模型架构讲解HMM与CRFHMM五大要素&#xff08;两大状态与三大概率&#xff09;HMM案例分享HMM实体识别应用场景代码实现 知识图谱介绍 NLP应用场景 图谱的本质&#xff0c;就是把自然…

JS逆向爬虫---请求参数加密②【某麦数据analysis参数加密】

主页链接: https://www.qimai.cn/rank analysis逆向 完整参数生成代码如下&#xff1a; const {JSDOM} require(jsdom) const dom new JSDOM(<!DOCTYPE html><p>hello</p>) window dom.windowfunction customDecrypt(n, t) {t t || generateKey(); //…

ZZULIOJ 1108: 打印数字图形(函数专题) (C/C++)

1108: 打印数字图形&#xff08;函数专题&#xff09; 题目描述 从键盘输入一个整数n(1≤n≤9),打印出指定的数字图形。要求在程序中定义并调用如下函数&#xff1a;PrintSpace(m)用来输出m个空格&#xff1b;PrintDigit(m)来输出一行中的数字串"12…m…21"&#xff…

element树形结构下拉组件组装对应格式数据

element树形结构下拉组件组装对应格式数据 <el-row><el-col :span"24"><el-form-item label"购买渠道" prop"treeData" class"grid-content bg-purple"><el-cascaderv-model"testForm.treeData":optio…

鳄鱼指标的3颜色线都代表什么?澳福官网一段话明白了

投资者一直在使用鳄鱼指标进行交易&#xff0c;但是对指标上面的3种颜色的K线都代表什么不明白&#xff1f;直到看到澳福官网一段话才明白&#xff0c;原来这么简单&#xff01; 鳄鱼指标&#xff0c;这一工具是由三条移动平均线组合而成。具体来说&#xff0c;蓝线&#xff0…

8-3、T型加减速单片机程序【51单片机控制步进电机-TB6600系列】

摘要&#xff1a;根据前两节内容&#xff0c;已完成所有计算工作&#xff0c;本节内容介绍具体单片机程序流程及代码 一、程序流程图 根据前两节文章内容可知&#xff0c;T型加减速的关键内容是运动类型的判断以及定时器初值的计算&#xff0c;在输出运动参数后即可判断出运动…

前端代码优化小技巧

导读 今天分享一下开测前端代码的一些优化&#xff0c;及使用的一些小技巧&#xff0c;来优化我们的网站&#xff0c;前端开发中最常见的问题就是很少使用ES6方法导致代码冗余&#xff0c;不够清晰&#xff0c;定时器和闭包导致内存溢出及泄露,网站中css导致排版错乱&#xff…

C++基础——对于C语言缺点的补充(2)

上篇文章中说到&#xff0c;为了解决C语言会出现人为定义的函数和库函数出现重定义的错误&#xff0c;C引入了一个新的概念&#xff0c;即命名空间&#xff0c;通过认为定义命名空间&#xff0c;来解决上述问题。 在本篇文章中&#xff0c;将继续介绍C相对于C语言不足来进行的补…

linux之信号

Linux之信号 什么是信号信号的产生方式signalsignactionkill信号集信号屏蔽 什么是信号 信号机制是一种使用信号来进行进程之间传递消息的方法&#xff0c;信号的全称为软中断信号&#xff0c;简称软中断。 信号的本质是软件层次上对中断的一种模拟&#xff08;软中断&#xff…

Hello Qt!

目录 1. 什么是Qt 2. Qt中的模块 3. 下载安装 4. QtCreator 4. Hello Qt 解释 .pro 解释 main.cpp 解释 mainwindow.ui 解释 mainwindow.h 解释 mainwindow.cpp 5. Qt 中的窗口类 5.1 基础窗口类 5.2 窗口的显示 6. Qt 的坐标体系 7. 内存回收 1. 什么是Qt 是一…

✔ ★【备战实习(面经+项目+算法)】 11.6 学习

✔ ★【备战实习&#xff08;面经项目算法&#xff09;】 坚持完成每天必做如何找到好工作1. 科学的学习方法&#xff08;专注&#xff01;效率&#xff01;记忆&#xff01;心流&#xff01;&#xff09;2. 每天认真完成必做项&#xff0c;踏实学习技术 认真完成每天必做&…

【斗破年番】萧炎给彩鳞承诺遭删,熏儿限时返场,古河沦为打工人

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析国漫资讯。 深度爆料&#xff0c;在斗破年番69话的最新剧情中&#xff0c;一场感人至深的情感戏份被删减了。在原著中&#xff0c;萧炎曾向美杜莎承诺&#xff0c;他会集齐材料&#xff0c;为她炼制出天雁九行翼。然而&a…

简单2招GET模型参数量计算和输入尺寸随卷积大小变化推导

本文将介绍两种简单且实用的方法&#xff0c;用于计算深度学习模型的参数量&#xff0c;并推导了输入尺寸随卷积大小的变化过程。这些方法可以帮助读者更好地理解模型的复杂度和输入尺寸的变化&#xff0c;为模型设计和优化提供指导。 比如论文中&#xff0c;通常会比较几种模…