Java IO流(五)Netty实战[TCP|Http|心跳检测|Websocket]

Netty入门代码示例(基于TCP服务)

Server端

package com.bierce.io.netty.simple;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
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.util.CharsetUtil;
public class NettyServer {
    public static void main(String[] args) throws InterruptedException {
        //创建BossGroup和WorkerGroup线程池组,均属于自旋状态
        EventLoopGroup bossGroup = new NioEventLoopGroup(); //负责连接请求处理
        EventLoopGroup workerGroup = new NioEventLoopGroup(); //进行业务处理
        try {
            //创建服务器端启动,通过链式编程配置相关参数
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class) //设置为服务端通道
                    .option(ChannelOption.SO_BACKLOG,128) //设置线程队列等待连接的个数
                    .childOption(ChannelOption.SO_KEEPALIVE, true) //设置保持活动连接状态
                    .childHandler(new ChannelInitializer<SocketChannel>() { //匿名创建通道初始对象
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new NettyServerHandler()); //为workerGroup下的NioEventLoop对应管道pipeline设置自定义处理器
                        }
                    });
            System.out.println("Server is start Successful !!!");
            ChannelFuture cf = bootstrap.bind(6668).sync(); //绑定指定端口并同步处理
            cf.channel().closeFuture().sync(); //监听关闭通道方法
        }finally { //关闭线程池资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
class NettyServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { //读取客户端发送的数据
        //ctx:上下文对象,包含管道pipeline,通道等信息
        //msg:即客户端发送的数据
        ByteBuf buf = (ByteBuf) msg; //ByteBuf是Netty提供的缓冲区,性能更高
        System.out.println("客户端发送过来的msg = " + buf.toString((CharsetUtil.UTF_8)));
        System.out.println("客户端地址 = " + ctx.channel().remoteAddress());
    }
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { //读取客户端信息完成后进行的业务处理
//        super.channelReadComplete(ctx);
        ctx.writeAndFlush(Unpooled.copiedBuffer("Hello, Client!",CharsetUtil.UTF_8)); //将数据写到缓存并刷新
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close(); //处理异常需要关闭通道
    }
}

 Client

package com.bierce.io.netty.simple;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.CharsetUtil;
public class NettyClient {
    public static void main(String[] args) throws InterruptedException {
        NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup(); //客户端事件循环组
        try {
            Bootstrap bootstrap = new Bootstrap(); //客户端启动对象
            bootstrap.group(eventLoopGroup)
                    .channel(NioSocketChannel.class) //客户端通道
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new NettyClientHandler()); //添加自定义处理器
                        }
                    });
            System.out.println("Client Start Successful!!!");
            ChannelFuture sync = bootstrap.connect("127.0.0.1", 6668).sync();
            sync.channel().closeFuture().sync(); //监听关闭通道
        }finally {
            eventLoopGroup.shutdownGracefully();
        }
    }
}
class NettyClientHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception { //通道就绪会触发该方法
        ctx.writeAndFlush(Unpooled.copiedBuffer("Hello, Server!", CharsetUtil.UTF_8)); //将数据写到缓存并刷新
    }
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { //读取服务端返回信息
        ByteBuf buf = (ByteBuf) msg; //ByteBuf是Netty提供的缓冲区,性能更高
        System.out.println("服务端发送过来的msg = " + buf.toString((CharsetUtil.UTF_8)));
        System.out.println("服务端地址 = " + ctx.channel().remoteAddress());
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close(); //处理异常需要关闭通道
    }
}

运行结果

Server
Client

Netty入门代码示例(基于HTTP服务)

package com.bierce.io.netty.http;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
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.handler.codec.http.*;
import io.netty.util.CharsetUtil;

import java.net.URI;

public class TestServer {
    public static void main(String[] args) throws InterruptedException {
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            //创建服务器端启动,通过链式编程配置相关参数
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class) //设置为服务端通道
                    .childHandler(new TestServerInitializer()); //设置为自定义的初始化
            System.out.println("Server is start Successful !!!");
            ChannelFuture cf = bootstrap.bind(9999).sync(); //绑定指定端口并同步处理
            cf.channel().closeFuture().sync(); //监听关闭通道方法
        }finally { //关闭线程池资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
class TestServerInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();
        //HttpServerCodec是Netty提供的处理Http的编-解码器 使用io.netty:netty-all:4.1.20Final版本,其他版本不支持会报错
        pipeline.addLast("MyHttpServerCodec", new HttpServerCodec());
        //增加自定义的handler
        pipeline.addLast("MyTestHttpServerHandler", new TestHttpServerHandler());
    }
}
class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {
    //读取客户端数据
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, HttpObject httpObject) throws Exception {
        if (httpObject instanceof HttpRequest){
            System.out.println("httpObject Type = " + httpObject.getClass());
            System.out.println("Client Address = " + channelHandlerContext.channel().remoteAddress());
            //对特定资源进行过滤
            HttpRequest httpRequest = (HttpRequest)httpObject;
            URI uri = new URI(httpRequest.getUri());
            if ("/favicon.ico".equals(uri.getPath())){
                System.out.println("favicon.ico资源不做响应");
                return;
            }
            //回复浏览器信息(http协议)
            ByteBuf content = Unpooled.copiedBuffer("Hello, I'm Server", CharsetUtil.UTF_8);
            FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
            response.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain");
            response.headers().set(HttpHeaderNames.CONTENT_LENGTH,content.readableBytes());
            channelHandlerContext.writeAndFlush(response);
        }
    }
}

运行结果

Netty心跳检测机制

package com.bierce.io.netty.heartbeat;

import io.netty.bootstrap.ServerBootstrap;
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.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;

import java.util.concurrent.TimeUnit;
public class MyServer {
    public static void main(String[] args) {
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            //IdleStateHandler:Netty提供的处理空闲状态的处理器
                            //readerIdleTime:多长时间没有读操作,会发送心跳检测包检测是否连接
                            //writerIdleTime:多长时间没有写操作,会发送心跳检测包检测是否连接
                            //allIdleTime:多长时间没有读写操作,会发送心跳检测包检测是否连接
                            pipeline.addLast(new IdleStateHandler(3,5,7, TimeUnit.SECONDS));
                            //IdleStateHandler触发后,将传递给下一个Handler的userEventTriggered方法去处理
                            //通过自定义的Handler对空闲状态进一步处理
                            pipeline.addLast(new MyServerHandler());
                        }
                    });
            ChannelFuture sync = bootstrap.bind(9999).sync().channel().closeFuture().sync();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
class MyServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent){
            IdleStateEvent IdleStateEvent = (IdleStateEvent) evt;
            String eventType = null;
            switch (IdleStateEvent.state()){
                case READER_IDLE:
                    eventType = "读空闲";
                    break;
                case WRITER_IDLE:
                    eventType = "写空闲";
                    break;
                case ALL_IDLE:
                    eventType = "读写空闲";
                    break;
            }
            System.out.println(ctx.channel().remoteAddress() + "-已超时,超时类型为: " + eventType );
            System.out.println("Server will deal with it instantly...");
            //发生空闲则关闭当前通道
            ctx.close();
        }
    }
}
class NettyClient {
    public static void main(String[] args) throws InterruptedException {
        NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup(); //客户端事件循环组
        try {
            Bootstrap bootstrap = new Bootstrap(); //客户端启动对象
            bootstrap.group(eventLoopGroup)
                    .channel(NioSocketChannel.class) //客户端通道
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new NettyClientHandler()); //添加自定义处理器
                        }
                    });
            System.out.println("Client Start Successful!!!");
            ChannelFuture sync = bootstrap.connect("127.0.0.1", 9999).sync();
            sync.channel().closeFuture().sync(); //监听关闭通道
        }finally {
            eventLoopGroup.shutdownGracefully();
        }
    }
}

 注意: 需要调整readerIdleTime|writerIdleTime|allIdleTime参数才会显示对应超时信息

Netty入门代码示例(基于WebSocket协议)

服务端

package com.bierce.websocket;

import io.netty.bootstrap.ServerBootstrap;
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.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.stream.ChunkedWriteHandler;

import java.time.LocalDateTime;

public class MyWebsocketServer {
    public static void main(String[] args) {
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            //基于Http协议,所以需要http解码编码
                            pipeline.addLast(new HttpServerCodec());
                            pipeline.addLast(new ChunkedWriteHandler()); //处理块方式的写操作
                            //http传输过程数据量非常大时会分段,而HttpObjectAggregator可以将多个分段聚合
                            pipeline.addLast(new HttpObjectAggregator(8192));
                            //webSocket采用帧方式传输数据
                            //WebSocketServerProtocolHandler作用是将http协议升级为ws协议,且保持长连接
                            pipeline.addLast(new WebSocketServerProtocolHandler("/hello"));
                            pipeline.addLast(new MyWebsocketServerHandler());
                        }
                    });
            ChannelFuture sync = bootstrap.bind(9999).sync().channel().closeFuture().sync();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
class MyWebsocketServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        System.out.println("Server receive the Info: " + msg.text());
        ctx.channel().writeAndFlush(new TextWebSocketFrame("Server time " + LocalDateTime.now() + " --- " + msg.text()));
    }
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println("有客户端连接成功 --" + ctx.channel().id().asLongText()); //asLongText唯一值
    }
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        System.out.println("有客户端已经离开 --" + ctx.channel().id().asLongText()); //asLongText唯一值
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("异常Info:" + cause.getMessage());
        ctx.close();
    }
}

客户端(浏览器)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Websocket</title>
</head>
<body>
<script>
    var socket;
    if (window.WebSocket) {
        socket = new WebSocket("ws://localhost:9999/hello");
        //相当于channelRead0,读取服务器端的消息
        socket.onmessage = function(ev){
            var rt = document.getElementById("responseText");
            rt.value = rt.value + "\n" + ev.data;
        }
        //开启连接
        socket.onopen = function(ev){
            var rt = document.getElementById("responseText");
            rt.value = "开启连接成功!";
        }
        //连接关闭
        socket.onclose = function(ev){
            var rt = document.getElementById("responseText");
            rt.value = rt.value + "\n" + "连接关闭成功!";
        }
    }
    //发送消息给服务器
    function send(msg){
        if(!window.socket){ //是否已创建socket
            return;
        }
        if(socket.readyState == WebSocket.OPEN){
            socket.send(msg);
        }else{
            alert("socket未连接");
        }
    }
</script>
    <form onsubmit="return false">
        <textarea name="message" style="height:300px;width:300px"></textarea>
        <input type="button" value="Send" onclick="send(this.form.message.value)">
        <textarea id="responseText" style="height:300px;width:300px"></textarea>
        <input type="button" value="Clear" onclick="document.getElementById('responseText').value=''">
    </form>
</body>
</html>

效果图

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

宝塔 杀死 java服务 netstat -tlnp | grep :7003 kill 2205698

7003 是端口 netstat -tlnp | grep :7003 kill 2205698

企业电子招投标采购系统源码之电子招投标的组成 tbms

​ 功能模块&#xff1a; 待办消息&#xff0c;招标公告&#xff0c;中标公告&#xff0c;信息发布 描述&#xff1a; 全过程数字化采购管理&#xff0c;打造从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。通供应商门户具备内外协同的能力&#xff0c;为…

七夕特辑(一)浪漫表白方式 用神经网络生成一首情诗

目录 一、准备工作二、用神经网络生成一首诗&#xff0c;代码说明 牛郎织女相会&#xff0c;七夕祝福要送来。祝福天下有情人&#xff0c;终成眷属永相伴。 七夕是中国传统的情人节&#xff0c;也是恋人们表达爱意的好时机。在这个特别的日子里&#xff0c;送上温馨的祝福&…

STM32电源名词解释

STM32电源架构 常用名词 VCC Ccircuit 表示电路&#xff0c;即接入电路的电压。 VDD Ddevice 表示器件&#xff0c; 即器件内部的工作电压。 VSS Sseries 表示公共连接&#xff0c;通常指电路公共接地端电压。 VDDA Aanalog 表示模拟&#xff0c;是模拟电路部分的电源。主要为…

Java“牵手”天猫商品列表数据,关键词搜索天猫商品数据接口,天猫API申请指南

天猫商城是一个网上购物平台&#xff0c;售卖各类商品&#xff0c;包括服装、鞋类、家居用品、美妆产品、电子产品等。要获取天猫商品列表和商品详情页面数据&#xff0c;您可以通过开放平台的接口或者直接访问天猫商城的网页来获取商品详情信息。以下是两种常用方法的介绍&…

21. 合并两个有序链表(简单系列)

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] 示例 2&#xff1a; 输入&#xff1a;l1 [], l2 [] 输出&#xff1a;[] 示例 3&#xff1a; …

jstat(JVM Statistics Monitoring Tool):虚拟机统计信息监视工具

jstat&#xff08;JVM Statistics Monitoring Tool&#xff09;&#xff1a;虚拟机统计信息监视工具 用于监视虚拟机各种运行状态信息的命令行工具。 它可以显示本地或者远程虚拟机进程中的类加载、内存、垃圾收集、即时编译等运行时数据&#xff0c;在没有GUI图形界面、只提…

C语言暑假刷题冲刺篇——day4

目录 一、选择题 二、编程题 &#x1f388;个人主页&#xff1a;库库的里昂 &#x1f390;CSDN新晋作者 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏✨收录专栏&#xff1a;C语言每日一练 ✨其他专栏&#xff1a;代码小游戏C语言初阶&#x1f91d;希望作者的文章能对你…

MySQL MVCC的详解之Read View

文章目录 概要一、基于UNDO LOG的版本链1.1、行记录结构1.2、了解UNDO LOG1.3、版本链 二、Read View2.1、判定机制 三、参考 概要 在上文中&#xff0c;我们提到了MVCC&#xff08;Multi-Version Concurrency Control)多版本并发控制&#xff0c;是通过undo log来实现的。那具…

如何在 Opera 中启用DNS over HTTPS

DNS over HTTPS&#xff08;基于HTTPS的DNS&#xff09;是一种更安全的浏览方式&#xff0c;但大多数 Web 浏览器默认情况下不启用它。了解如何在 Opera 浏览器中启用该功能。 您可能不知道这一点&#xff0c;但您的网络浏览器并不像您希望的那样私密或安全。您会看到&#xff…

Python Opencv实践 - 图像直方图均衡化

import cv2 as cv import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/pomeranian.png", cv.IMREAD_COLOR) print(img.shape)#图像直方图计算 #cv.calcHist(images, channels, mask, histSize, ranges, hist, accumulate) #images&…

数据结构(6)

2-3查找树 2-结点&#xff1a;含有一个键(及其对应的值)和两条链&#xff0c;左链接指向2-3树中的键都小于该结点&#xff0c;右链接指向的2-3树中的键都大于该结点。 3-结点&#xff1a;含有两个键(及其对应的值)和三条链&#xff0c;左链接指向的2-3树中的键都小于该结点&a…

FPGA原理与结构——FIFO IP核原理学习

一、FIFO概述 1、FIFO的定义 FIFO是英文First-In-First-Out的缩写&#xff0c;是一种先入先出的数据缓冲器&#xff0c;与一般的存储器的区别在于没有地址线&#xff0c; 使用起来简单&#xff0c;缺点是只能顺序读写数据&#xff0c;其数据地址由内部读写指针自动加1完成&…

vscode 无法跳转第三方安装包

vscode 无法跳转第三方安装包 场景&#xff1a;使用vscode写代码时&#xff0c; 第三方的安装包无法使用ctrl 左键&#xff0c;点击进入查看&#xff0c; 不方便源码查看 解决办法&#xff1a; 使用快捷键 Ctrl Shift P&#xff0c; 进入命令搜索框搜索 setting.json 编辑…

【数据结构】实现栈和队列

目录 一、栈1.栈的概念及结构&#xff08;1&#xff09;栈的概念&#xff08;2&#xff09;栈的结构 2.栈的实现&#xff08;1&#xff09;类型和函数的声明&#xff08;2&#xff09;初始化栈&#xff08;3&#xff09;销毁&#xff08;4&#xff09;入栈&#xff08;5&#x…

高忆管理:药店零售概念回落,开开实业走低,此前7日大涨超80%

药店零售概念18日盘中大幅下挫&#xff0c;到发稿&#xff0c;华人健康跌逾11%&#xff0c;漱玉布衣、塞力医疗跌超9%&#xff0c;重药控股、浙江震元、榜首医药等跌超7%&#xff0c;药易购跌超6%&#xff0c;开开实业跌超3%。 值得注意的是&#xff0c;开开实业此前7个交易日斩…

【应用层】网络基础 -- HTTP协议

再谈协议HTTP协议认识URLurlencode和urldecodeHTTP协议格式HTTP的方法HTTP的状态码HTTP常见HeaderHTTP周边会话保持 再谈协议 协议是一种 “约定”. socket api的接口&#xff0c;在读写数据时&#xff0c;都是按 “字符串” 的方式来发送接收的(tcp是以字节流的方式发送的&am…

vue项目配置git提交规范

vue项目配置git提交规范 一、背景介绍二、husky、lint-staged、commitlint/cli1.husky2.lint-staged3.commitlint/cli 三、具体使用1.安装依赖2.运行初始化脚本3.在package.json中配置lint-staged4.根目录新增 commitlint.config.js 4.提交测试1.提示信息格式错误时2.eslint校验…

sql递归查询

一、postgresql 递归sql with recursive p as(select t1.* from t_org_test t1 where t1.id2union allselect t2.*from t_org_test t2 join p on t2.parent_idp.id) select id,name,parent_id from p; sql中with xxxx as () 是对一个查询子句做别名&#xff0c;同时数据库会对…

c++ day2

#include <iostream>using namespace std; /*void row(int &p,int &q)//引用 {int t;tp;pq;qt; }*/ /*struct ab {string name;// int &age; }; void add(int a,int b) {cout << ab<< endl; } void add(float a,float b) {cout << ab <…