从IO到netty

一、pageCache

1.FileOutputStream与BufferedOutputStream的主要区别

系统调用次数区别,前者频繁调用造成频繁的用户态和内核态的切换,后者每8KB调用一次内核
- BufferOutputStream jvm  8kB   syscall  write(8KBbyte[])
- FileOutputStream每次写都进行系统调用

2.pageCache内核刷盘参数

$ sysctl -a | grep dirty
vm.dirty_background_bytes = 0
vm.dirty_background_ratio = 5
vm.dirty_bytes = 0
vm.dirty_expire_centisecs = 3000
vm.dirty_ratio = 10
vm.dirty_writeback_centisecs = 500

dirty_writeback_centisecs

  • 表示多久唤醒一次刷新脏页的后台线程。这里的500表示5秒唤醒一次。

dirty_expire_centisecs

  • 表示脏数据多久会被刷新到磁盘上。这里的3000表示 30秒。

dirty_background_ratio

  • 表示当脏页占总内存的的百分比超过这个值时,后台线程开始刷新脏页。这个值如果设置得太小,可能不能很好地利用内存加速文件操作。如果设置得太大,则会周期性地出现一个写 IO 的峰值。

dirty_ratio

  • 当脏页占用的内存百分比超过此值时,内核会阻塞掉写操作,并开始刷新脏页。

dirty_background_bytes、dirty_bytes

  • 是和 dirty_background_ratio、dirty_ratio 表示同样意义的不同单位的表示。两者不会同时生效。

3.作用

  • 优先使用内存,减少硬件IO调用(内存
  • 缺点:可靠性问题:未刷盘断电,易丢失数据。

二、ByteBuffer

java程序堆

-Xmx1024m分配jvm heap

 public void whatByteBuffer(){

//        ByteBuffer buffer = ByteBuffer.allocate(1024);//jvm 堆内
       ByteBuffer buffer = ByteBuffer.allocateDirect(1024);//jvm堆外,进程堆内

       System.out.println("postition: " + buffer.position());
       System.out.println("limit: " +  buffer.limit());
       System.out.println("capacity: " + buffer.capacity());
       System.out.println("mark: " + buffer);

       buffer.put("123".getBytes());

       System.out.println("-------------put:123......");
       System.out.println("mark: " + buffer);

       buffer.flip();   //读写交替,读时调用,防止读到还未写的地方pos指向0,limit指向已写位置

       System.out.println("-------------flip......");
       System.out.println("mark: " + buffer);

       buffer.get();

       System.out.println("-------------get......");
       System.out.println("mark: " + buffer);

       buffer.compact();//写时调用,将已读的挤出,pos指向未写的初始的位置,limit指向最大空间位置

       System.out.println("-------------compact......");
       System.out.println("mark: " + buffer);

       buffer.clear();

       System.out.println("-------------clear......");
       System.out.println("mark: " + buffer);

   }

三、FileChannel

map方法

  • 内核系统调用,文件在pageCache中映射一个地址
  • map.put 非系统调用,直接向pageCache中写数据
  • map.force 刷盘
    FileChannel rafchannel = raf.getChannel();
    //mmap  堆外  和文件映射的   byte  not  object
    MappedByteBuffer map = rafchannel.map(FileChannel.MapMode.READ_WRITE, 0, 4096);

    map.put("xxx".getBytes());  //不是系统调用  但是数据会到达 内核的pagecache

    map.force(); //  flush

可靠性排序

  • on heap < off heap < mmap(file)

应用

  • netty: (on heap; off heap)
  • kafka log: mmap

四、TCPIP

1.tcpdump

tcpdump -nn -i eth0 port 8080

2.socket

  • 四元组:客户端IP.PORT–>服务端IP.PORT

3.BACK_LOG

  • 服务端调用accept获取连接前内核可保持的连接数

4.窗口与拥塞

  • 客户端与服务端在发送数据和接收数据时会报告自己的窗口剩余大小,发送方将根据对方窗口大小决定发送多少个数据包,以防拥塞,导致对方丢弃发送包
  • 窗口大小:网卡MTU:1500

5.NODELAY

  • 发送优化,默认为false,积攒一定量发送数据包,设置为true关闭优化,表示尽快将缓冲区数据发送
  • 通讯数据包较小时,适合关闭优化,即尽快发送数据

6.KEEPALIVE

  • TCP级别的心跳,及时踢出死掉的服务

7.握手挥手状态变迁图

从IO到netty

五、网络IO

1.strace

  • 追踪每一个线程对内核的调用
    *
strace -ff -o out /usr/jdk1.8/bin/java Test

2.BIO

  • new Thread
  • BLOCKING

3.NIO

  • JDK : new io
  • OS : NONBLOCKING
  • 单线程可同时处理多个连接
  • 缺点:假设连接数为n,每一次循环,则将有O(n)的复杂度, 会对每一个连接描述符进行一次尝试read(系统调用,成本较高), 而实际上并非每次所有连接都有数据到达,会有很多无效的read,连接数越多,无效系统调用就越多。

4.open files

1.为什么会报错”too many open files”

  • linux系统默认最大open files通过 ulimit -n查看是1024,超过此数会报此错
    *
ulimit -n

2.root用户为什么创建了4000多个连接才报”too many open files”

  • linux系统root用户max open files受Hard限制,普通用户才受1024限制,而默认Hard设置正是4096,可通过ulimit -Hn查看
    *
ulimit -Hn

3.设置open files

/etc/security/limits.conf

5.SELECTOR

1.多路复用器概念

  • 多条路(IO)通过一个系统调用(select,poll,epoll),筛选获得其中状态可用的IO数据集,再由程序处理ACCEPT/READ/WRITE
    *
select()  and pselect() allow a program to monitor multiple file descriptors,
waiting until one or more of the file descriptors become "ready" for some class of I/O operation (e.g., input possible).

A file descriptor is considered ready if it is possible to perform the corresponding I/O operation (e.g., read(2)) without blocking.

  • 优点:跟NIO比,系统调用次数大大减少,效率提高

2.select、poll的缺点

  • 每次需要向内核传送全量的fds
  • 内核将重新对新传递的全量fds进行遍历
  • 只完成了将从网卡发来的数据通过内核网络协议栈(4层)关联到fd的缓冲区

3.epoll

  • epoll_create ->创建一个文件描述符epfd,指向一个红黑树结构
  • epoll_ctl(epfd,ADD,fd,event)->向epfd指向的红黑树结构添加指定文件描述符fd节点,并指定关注事件集event
  • epoll_wait->获取发生关注事件的fd事件集数组,并清除该fd的该类型事件,如需再关注该事件需要重新添加该事件类型epoll_ctl(epfd,EPOLL_CTL_MOD,listenfd,&ev)
  • 内核在epoll_ctl和epoll_wait之间做的事情:数据从网卡传输产生了IO中断后除了将数据关联到fd,还会将epfd红黑树中发生事件的fd添加到另一个数组(暂称为wait数组)中,在epoll_wait时直接从内存中拿到此数据返回, 避免了实时遍历红黑树,也就是说数据传输的过程中内核就已经将发生事件的fds准备好了,这也是内核针对epoll做的很棒的优化。

6.同步、异步、阻塞、非阻塞

  • 同步:应用自己处理都是同步
  • 异步:由内核完成R/W,应用没有调用IO操作(如:注册事件,内核完成读写Buffer)
  • 阻塞:BLOCKING,调用阻塞,直到IO状态可用才返回
  • 非阻塞:NONBLOCKING,调用必实时返回,不一定调用成功,需要应用循环多次调用来保证成功

7.中断

  • 软中断:系统调用,int x80
  • 硬中断:时钟中断,晶振,打断CPU正在执行的任务,保护现场,读取其他任务执行,以使单个CPU可以处理多任务
  • IO中断:网卡、键盘、鼠标(dpi)

六、服务器模型

从IO到netty

七、netty

1.ByteBuf

  • 更方便使用的ByteBuffer
// 默认初始化
ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer(8,20);

// 池化初始化
ByteBuf buf = UnpooledByteBufAllocator.DEFAULT.heapBuffer(8,20);
ByteBuf buf = PooledByteBufAllocator.DEFAULT.heapBuffer(8,20);

//方法
buf.isReadable();
buf.readIndex();
buf.readableBytes();
buf.isWritable();
buf.writeIndex();
buf.writableBytes();
buf.capacity();
buf.maxCapacity();
buf.isDirect();

2.一步一步推导使用netty

package com.wod.ncc.common.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
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;
import org.junit.Test;

import java.lang.reflect.ParameterizedType;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;

/**
 * netty client
 *
 * @author wod.Y
 * @date 2021/03/14 16:56
 **/
public class NettyDemo {
    /**
     * NioEventLoopGroup基本使用
     * @throws Exception
     */
    @Test
    public void loopExecutor() throws Exception {
        // GROUP 线程池
        NioEventLoopGroup selector = new NioEventLoopGroup(2);
        selector.execute(() -> {
            try {
                while (true) {
                    System.out.println("hello world 001!");
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        selector.execute(() -> {
            try {
                while (true) {
                    System.out.println("hello world 002!");
                    Thread.sleep(1000);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        System.in.read();
    }

    /**
     * 客户端版本1 - 一步一步操作
     * @throws InterruptedException
     */
    @Test
    public void clientMod() throws InterruptedException {
        NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup(1);
        NioSocketChannel client = new NioSocketChannel();

        ChannelPipeline pipeline = client.pipeline();
        pipeline.addLast(new MyHandler());

        nioEventLoopGroup.register(client);

        // 同步连接
        ChannelFuture connect = client.connect(new InetSocketAddress("10.0.0.121", 9090));
        ChannelFuture sync = connect.sync();

        // 同步发送
        ByteBuf buf = Unpooled.copiedBuffer("hello".getBytes(StandardCharsets.UTF_8));
        ChannelFuture send = client.writeAndFlush(buf);
        send.sync();

        // 同步接收
        Channel read = client.read();
        ByteBufAllocator alloc = read.alloc();

        //同步断开
        sync.channel().closeFuture().sync();

        System.out.println("client ended");
    }

    /**
     * netty 版本 client
     * @throws InterruptedException
     */
    @Test
    public void clientMod1() throws InterruptedException {
        NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup(1);
        Bootstrap bs = new Bootstrap();
        ChannelFuture connect = bs.group(nioEventLoopGroup)
                .handler(new MyInitHandler() {//此处可使用ChannelInitializer
                    @Override
                    protected void init(Channel channel) {
                        channel.pipeline().addLast(new MyClientHandler());
                    }
                })
                .channel(NioSocketChannel.class)
                .connect(new InetSocketAddress(9090));
        ChannelFuture send = connect.channel().writeAndFlush(Unpooled.copiedBuffer("netty demo by yhm", StandardCharsets.UTF_8));
        send.sync();

        connect.sync().channel().closeFuture().sync();
        System.out.println("client ended");
    }

    /**
     * 服务端版本1 自定义accepter
     * @throws InterruptedException
     */
    @Test
    public void serverMod() throws InterruptedException {
        NioEventLoopGroup thread = new NioEventLoopGroup(1);
        NioServerSocketChannel server = new NioServerSocketChannel();
        server.pipeline().addLast(new MyAcceptHandler(thread, new MyInitHandler() {
            @Override
            protected void init(Channel channel) {
                channel.pipeline().addLast(new MyHandler());
            }
        }));
        thread.register(server);
        ChannelFuture bind = server.bind(new InetSocketAddress(9090));

        bind.sync().channel().closeFuture().sync();
        System.out.println("server closed!");
    }

    /**
     * netty 版本服务端
     * @throws InterruptedException
     */
    @Test
    public void serverMod1() throws InterruptedException {
        NioEventLoopGroup group = new NioEventLoopGroup(1);
        ServerBootstrap bs = new ServerBootstrap();
        ChannelFuture bind = bs.group(group, group)
                .channel(NioServerSocketChannel.class)
                .childHandler(new MyInitHandler() {//此处可使用ChannelInitializer
                    @Override
                    protected void init(Channel channel) {
                        channel.pipeline().addLast(new MyHandler());
                    }
                })
                .bind(new InetSocketAddress(9090));

        bind.sync().channel().closeFuture().sync();
        System.out.println("server closed!");
    }

}

/**
 * 模仿 netty ChannelInitializer
 * @param
 */
@ChannelHandler.Sharable
abstract class MyInitHandler extends ChannelHandlerAdapter {

    protected abstract void init(T t);

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("client registered....");
        ChannelPipeline pipeline = ctx.pipeline();
        init((T) ctx.channel());
        if (pipeline.context(this) != null) {
            pipeline.remove(this);
        }
    }
}

class MyClientHandler extends ChannelHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        byte[] readBytes = new byte[buf.readableBytes()];
        buf.getBytes(0, readBytes);
        System.out.println(new String(readBytes));
    }
}

class MyHandler extends ChannelHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        byte[] readBytes = new byte[buf.readableBytes()];
        buf.getBytes(0, readBytes);
        System.out.println(new String(readBytes));
        ctx.writeAndFlush(buf);
    }
}

class MyAcceptHandler extends ChannelHandlerAdapter {
    private final NioEventLoopGroup selector;
    private final ChannelHandler initHandler;

    MyAcceptHandler(NioEventLoopGroup thread, ChannelHandler inHandler) {
        this.selector = thread;
        this.initHandler = inHandler;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        SocketChannel sc = (SocketChannel) msg;
        System.out.println(sc);
        sc.pipeline().addLast(initHandler);
        selector.register(sc);
    }
}

Original: https://www.cnblogs.com/wod-Y/p/14534735.html
Author: 杨海星
Title: 从IO到netty

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/592585/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 开发思想

    解决的问题:一类对象,不同对象有不同的处理 顶级接口 定义规范,面向接口编程 抽象策略 定义一套模板,不同的交给不同的策略实现 具体策略 枚举 对象标识 –具体策略 策…

    Java 2023年6月14日
    065
  • Sentinel vs Hystrix 限流对比,到底怎么选?

    Sentinel 是阿里中间件团队开源的,面向分布式服务架构的轻量级高可用流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来帮助用户保护服务的稳定性。…

    Java 2023年5月29日
    086
  • Java程序员的macOS手册

    针对Java程序员而言,开发服务器端程序,程序的运行环境是Linux,在Mac OS X这个Unix上面开发,其实很方便。 本技术专栏帮忙Java程序员,搭建工作环境,如JDK、M…

    Java 2023年5月29日
    062
  • 面向对象ooDay2

    构造方法到底要不要参数,要看对象的数据能不能写死 如果对象的数据都一样,意味着可以写死,就不需要传参。 如果对象的数据都不一样,意味着不能写死,那就需要传参。 Original: …

    Java 2023年6月13日
    059
  • 完了,我的小网站图片挂了!

    大家好,我是二哥! 很早之前,就有小伙伴给我反馈说《Java 程序员进阶之路》经常有图片不显示或者加载缓慢。 但由于白嫖(GitHub图床+jsdelivr CDN)的力量实在是太…

    Java 2023年6月9日
    067
  • 58.忘与记

    dsfds posted @2022-09-28 08:33 随遇而安== 阅读(3 ) 评论() 编辑 Original: https://www.cnblogs.com/55z…

    Java 2023年6月7日
    089
  • 2.门面Slf4j+Simple简单实现

    1.导入pom文件 org.slf4j slf4j-api 1.7.27 org.slf4j slf4j-simple 1.7.27 2.使用 import org.slf4j.L…

    Java 2023年6月13日
    078
  • IDEA超级好用的插件推荐

    IDEA超级好用的插件推荐 以下都是本人使用idea开发以来,所使用过的插件,强烈推荐,提升代码质量,事半功倍之首选!!! 先介绍下如何安装这些插件:(本人使用idea的版本是20…

    Java 2023年6月15日
    078
  • 不重启tomcat,清空catalina.out的几种方式

    相信小伙伴们使用tomcat容器部署项目时,都会遇到这个问题 尤其是刚上线日志级别一般启动为DEBUG级别时,catalina.out文件过一会就会特别特别大,特别占我们服务器上的…

    Java 2023年6月16日
    093
  • 戏说领域驱动设计(廿七)——Saga设计模式

    上一节我们讲解了常用的事务,也提及了Saga,这是在分布式环境下被经常使用的一种处理复杂业务和分布式事务的设计模式。本章我们的主要目标是编写一个简单版本的Saga处理器,不同于Se…

    Java 2023年6月7日
    070
  • Vue 利用后端的数据字典和Map对象实现表格列字段动态转义的处理方案

    1、前言 Vue中,使用el-table组件,经常遇到列字段转义的问题。常规处理方法有以下两种: 方法1:在模板中使用v-if,直接转义。如: N Y 方法2:使用formatte…

    Java 2023年6月14日
    0100
  • MyBatis: Invalid bound statement (not found)错误的可能原因

    其他原因导致此问题解决参考: 1.检查 xml 文件所在 package 名称是否和 Mapper interface 所在的包名一致 mapper 的 namespace 写的不…

    Java 2023年5月30日
    064
  • IDEA使用Gittee提交代码

    今天给大家分享一下如何从idea提交代码到gitee上面。1.先在gitee上创建仓库2.填写相应的信息3.下载git直接next一站式安装4.鼠标右键点击 Git Bash He…

    Java 2023年6月9日
    098
  • 【设计模式】Java设计模式-建造者模式

    【设计模式】Java设计模式 – 建造者模式 😄 不断学习才是王道🔥 继续踏上学习之路,学之分享笔记👊 总有一天我也能像各位大佬一样🏆 @一个有梦有戏的人 @怒放吧德德…

    Java 2023年6月16日
    079
  • Java 北京时间 转 UTC时间

    ” 北京时间” 转为” UTC时间“,根据需要转换格式不同,实现方式有所不同。 简单整理如下4种格式: 1、UTC格式:2021-1…

    Java 2023年5月29日
    060
  • Tomcat8下的Redis会话共享

    前言: 最近在做网站的升级,从 Tomcat7升级到 Tomcat8版本,因为没接触过,就以为升级下Tomcat的版本就万事大吉,可是天不如人愿,很顺利的将应用升级到了Tomcat…

    Java 2023年6月6日
    086
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球