最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

netty對(duì)proxy protocol代理協(xié)議的支持詳解

 更新時(shí)間:2022年07月21日 09:29:18   作者:程序那些事  
這篇文章主要為大家介紹了netty對(duì)proxy protoco代理協(xié)議的支持詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

簡(jiǎn)介

我們知道proxy protocol是haproxy提出的一個(gè)代理協(xié)議,通過(guò)這個(gè)協(xié)議,所有實(shí)現(xiàn)這個(gè)協(xié)議的proxy或者LBS,都可以附帶真實(shí)客戶端的IP地址和端口號(hào),這使得proxy protocol在實(shí)際應(yīng)用中非常有用。

這么優(yōu)秀的協(xié)議,沒(méi)有理由netty不支持。本文將會(huì)談一下netty中對(duì)proxy protoco代理協(xié)議的支持。

netty對(duì)proxy protocol協(xié)議的支持

proxy protocol協(xié)議其實(shí)很簡(jiǎn)單,就是在請(qǐng)求前面帶了proxy header信息。

在netty中這個(gè)header信息叫做HAProxyMessage:

public final class HAProxyMessage extends AbstractReferenceCounted {

HAProxyMessage是一個(gè)ReferenceCounted,這一點(diǎn)和ByteBuf很類似,說(shuō)明HAProxyMessage保留著和ByteBuf很類似的特性。

根據(jù)proxy protocol協(xié)議,該協(xié)議可以分為兩個(gè)版本,分別是v1和v2,其中v1版本是文本協(xié)議,而v2版本支持二進(jìn)制的格式。

顯然從代碼編寫和調(diào)試的角度來(lái)看v1更加友好,但是從程序的角度來(lái)看,v2可能性能更高。

HAProxyMessage中有個(gè)專門的HAProxyProtocolVersion類,來(lái)表示proxy protocol的版本信息:

public enum HAProxyProtocolVersion {
    V1(VERSION_ONE_BYTE),
    V2(VERSION_TWO_BYTE);

HAProxyProtocolVersion是一個(gè)枚舉類,在它里面定義了和proxy協(xié)議相對(duì)應(yīng)的兩個(gè)版本號(hào)。

在版本號(hào)之后是command,在netty中用HAProxyCommand來(lái)表示:

public enum HAProxyCommand {
    LOCAL(HAProxyConstants.COMMAND_LOCAL_BYTE),
    PROXY(HAProxyConstants.COMMAND_PROXY_BYTE);

HAProxyCommand也是一個(gè)枚舉類,里面定義了兩個(gè)command的值,分別是local和proxy。

其中l(wèi)ocal表示該請(qǐng)求是代理服務(wù)器主動(dòng)發(fā)起的,而不是客戶端發(fā)起的,比如監(jiān)控檢測(cè)等請(qǐng)求。

proxy表示該請(qǐng)求是一個(gè)代理請(qǐng)求。

接下來(lái)是AddressFamily和TransportProtocol,這兩個(gè)字段用同一個(gè)byte來(lái)表示,所以這兩個(gè)類都是HAProxyProxiedProtocol的內(nèi)部類。

先看下AddressFamily的定義:

    public enum AddressFamily {
        AF_UNSPEC(AF_UNSPEC_BYTE),
        AF_IPv4(AF_IPV4_BYTE),
        AF_IPv6(AF_IPV6_BYTE),
        AF_UNIX(AF_UNIX_BYTE);

AddressFamily中定義了4個(gè)address family類型,分別是unspec,ipv4,ipv6和unix。分別對(duì)應(yīng)未知family,ipv4,ipv6和unix domain socket。

再看下TransportProtocol的定義:

    public enum TransportProtocol {
        UNSPEC(TRANSPORT_UNSPEC_BYTE),
        STREAM(TRANSPORT_STREAM_BYTE),
        DGRAM(TRANSPORT_DGRAM_BYTE);

TransportProtocol有3個(gè)值,分別是unspec,stream和dgram。分別對(duì)應(yīng)未知協(xié)議,http/https協(xié)議,udp/tcp協(xié)議。

因?yàn)锳ddressFamily和TransportProtocol實(shí)際上是同一個(gè)byte,所以經(jīng)過(guò)組合之后可以得到下面的幾個(gè)枚舉值:

    UNKNOWN(TPAF_UNKNOWN_BYTE, AddressFamily.AF_UNSPEC, TransportProtocol.UNSPEC),
    TCP4(TPAF_TCP4_BYTE, AddressFamily.AF_IPv4, TransportProtocol.STREAM),
    TCP6(TPAF_TCP6_BYTE, AddressFamily.AF_IPv6, TransportProtocol.STREAM),
    UDP4(TPAF_UDP4_BYTE, AddressFamily.AF_IPv4, TransportProtocol.DGRAM),
    UDP6(TPAF_UDP6_BYTE, AddressFamily.AF_IPv6, TransportProtocol.DGRAM),
    UNIX_STREAM(TPAF_UNIX_STREAM_BYTE, AddressFamily.AF_UNIX, TransportProtocol.STREAM),
    UNIX_DGRAM(TPAF_UNIX_DGRAM_BYTE, AddressFamily.AF_UNIX, TransportProtocol.DGRAM);

以上的枚舉值也是HAProxyProxiedProtocol中定義的值。

接下就是源ip地址,目標(biāo)地ip地址,源端口和目標(biāo)端口這幾個(gè)值,定義為屬性表示如下:

    private final String sourceAddress;
    private final String destinationAddress;
    private final int sourcePort;
    private final int destinationPort;

最后,proxy protocol中還可以包含額外的字段tlv,tlv在netty中也是一種byteBuf,使用HAProxyTLV表示:

public class HAProxyTLV extends DefaultByteBufHolder 

因?yàn)閠lv是key value結(jié)構(gòu),所以看下HAProxyTLV的構(gòu)造函數(shù):

    public HAProxyTLV(Type type, ByteBuf content) {
        this(type, Type.byteValueForType(type), content);
    }

HAProxyTLV接受一個(gè)type和byteBuf的value。

Type是一個(gè)枚舉類,在netty中可以支持下面的值:

    public enum Type {
        PP2_TYPE_ALPN,
        PP2_TYPE_AUTHORITY,
        PP2_TYPE_SSL,
        PP2_TYPE_SSL_VERSION,
        PP2_TYPE_SSL_CN,
        PP2_TYPE_NETNS,
        OTHER;

在HAProxyMessage中,tlv是一個(gè)list來(lái)保存的:

private final List<HAProxyTLV> tlvs;

到此,所有HAProxyMessage所需要的參數(shù)都齊了,我們看下HAProxyMessage的構(gòu)造函數(shù):

    public HAProxyMessage(
            HAProxyProtocolVersion protocolVersion, HAProxyCommand command, HAProxyProxiedProtocol proxiedProtocol,
            String sourceAddress, String destinationAddress, int sourcePort, int destinationPort,
            List<? extends HAProxyTLV> tlvs)

HAProxyMessage會(huì)將所有的參數(shù)都存儲(chǔ)到本地的變量中,供后續(xù)使用。

因?yàn)閜roxy protocol有兩個(gè)版本,v1和v2,所以HAProxyMessage中提供了兩個(gè)將header編碼為AProxyMessage對(duì)象的方法,分別是:

static HAProxyMessage decodeHeader(ByteBuf header) 

和:

static HAProxyMessage decodeHeader(String header)

有了proxy protocol的java表示之后,我們?cè)賮?lái)看一下HAProxyMessage的編碼解碼器。

HAProxyMessage的編碼解碼器

netty對(duì)HAProxyMessage對(duì)象的支持表現(xiàn)在兩個(gè)地方,netty提供了兩個(gè)類分別對(duì)HAProxyMessage進(jìn)行編碼和解碼,這兩個(gè)類是HAProxyMessageEncoder和HAProxyMessageDecoder。

先看一下HAProxyMessageEncoder:

public final class HAProxyMessageEncoder extends MessageToByteEncoder<HAProxyMessage> 

HAProxyMessageEncoder繼承自MessageToByteEncoder,傳入的泛型是HAProxyMessage,表示是將HAProxyMessage編碼成為ByteBuf。

它的encode方法很簡(jiǎn)單,根據(jù)HAProxyMessage傳入的message版本信息,分別進(jìn)行編碼:

    protected void encode(ChannelHandlerContext ctx, HAProxyMessage msg, ByteBuf out) throws Exception {
        switch (msg.protocolVersion()) {
            case V1:
                encodeV1(msg, out);
                break;
            case V2:
                encodeV2(msg, out);
                break;
            default:
                throw new HAProxyProtocolException("Unsupported version: " + msg.protocolVersion());
        }
    }

HAProxyMessageDecoder是跟HAProxyMessageEncoder相反的動(dòng)作,是將接收到的ByteBuf解析成為HAProxyMessage:

public class HAProxyMessageDecoder extends ByteToMessageDecoder 

因?yàn)镠AProxyMessage有兩個(gè)版本,那么怎么判斷接收到的ByeBuf是哪個(gè)版本呢?

其實(shí)很簡(jiǎn)單,因?yàn)関1版本和v2版本的開(kāi)始字符是不一樣的,v1版本的開(kāi)頭是一個(gè)text:"PROXY", v2版本的開(kāi)頭是一個(gè)固定的二進(jìn)制串,如下所示:

    static final byte[] BINARY_PREFIX = {
            (byte) 0x0D,
            (byte) 0x0A,
            (byte) 0x0D,
            (byte) 0x0A,
            (byte) 0x00,
            (byte) 0x0D,
            (byte) 0x0A,
            (byte) 0x51,
            (byte) 0x55,
            (byte) 0x49,
            (byte) 0x54,
            (byte) 0x0A
    };
    static final byte[] TEXT_PREFIX = {
            (byte) 'P',
            (byte) 'R',
            (byte) 'O',
            (byte) 'X',
            (byte) 'Y',
    };

看下它的decode方法實(shí)現(xiàn):

    protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        if (version == -1) {
            if ((version = findVersion(in)) == -1) {
                return;
            }
        }
        ByteBuf decoded;
        if (version == 1) {
            decoded = decodeLine(ctx, in);
        } else {
            decoded = decodeStruct(ctx, in);
        }
        if (decoded != null) {
            finished = true;
            try {
                if (version == 1) {
                    out.add(HAProxyMessage.decodeHeader(decoded.toString(CharsetUtil.US_ASCII)));
                } else {
                    out.add(HAProxyMessage.decodeHeader(decoded));
                }
            } catch (HAProxyProtocolException e) {
                fail(ctx, null, e);
            }
        }
    }

上面代碼的邏輯是先從ByteBuf中根據(jù)版本號(hào)decode出header信息放到ByteBuf中。

然后再根據(jù)版本號(hào)的不同,分別調(diào)用HAProxyMessage的兩個(gè)不同版本的decodeHeader方法進(jìn)行解碼。最終得到HAProxyMessage。

netty中proxy protocol的代碼示例

有了netty對(duì)proxy protocol的支持,那么在netty中搭建支持proxy protocol的服務(wù)器和客戶端就很容易了。

先看一下如何搭建支持proxy protocol的服務(wù)器:

    private static void startServer(int port) throws InterruptedException {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class)
             .handler(new LoggingHandler(LogLevel.INFO))
             .childHandler(new ServerInitializer());
            b.bind(port).sync().channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

代碼和常規(guī)的netty server一樣,這里使用了NioEventLoopGroup和NioServerSocketChannel,搭建了一個(gè)支持TCP協(xié)議的netty服務(wù)器。

ServerInitializer中包含了netty自帶的HAProxy編碼器和自定義的消息處理器:

class ServerInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    public void initChannel(SocketChannel ch) throws Exception {
        ch.pipeline().addLast(
                new LoggingHandler(LogLevel.DEBUG),
                new HAProxyMessageDecoder(),
                new SimpleChannelInboundHandler() {
                    @Override
                    protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
                        if (msg instanceof HAProxyMessage) {
                            log.info("proxy message is : {}", msg);
                        } else if (msg instanceof ByteBuf) {
                            log.info("bytebuf message is : {}", ByteBufUtil.prettyHexDump((ByteBuf) msg));
                        }
                    }
                });
    }
}

這里使用netty自帶的HAProxyMessageDecoder,用來(lái)將ByteBuf消息解碼為HAProxyMessage,然后在自定義的SimpleChannelInboundHandler中對(duì)HAProxyMessage進(jìn)行處理。

這里的服務(wù)器可以處理兩種消息,一種是HAProxyMessage,一種是原始的ByteBuf。處理的結(jié)果就是將消息打印出來(lái)。

然后看下客戶端的定義:

EventLoopGroup group = new NioEventLoopGroup();
            Bootstrap b = new Bootstrap();
            b.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ClientHander());
            Channel ch = b.connect(host, port).sync().channel();

客戶端使用的是EventLoopGroup和NioSocketChannel,是基于TCP協(xié)議的請(qǐng)求。

這里添加了自定義的handler:ClientHander,ClientHander繼承自ChannelOutboundHandlerAdapter用來(lái)對(duì)client發(fā)出的消息進(jìn)行處理。

這里看一下它的handlerAdded方法:

    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        ctx.pipeline().addBefore(ctx.name(), null, HAProxyMessageEncoder.INSTANCE);
        super.handlerAdded(ctx);
    }

可以看到handlerAdded方法向channelPipeline中添加了HAProxyMessageEncoder,用于編碼HAProxyMessage。

因?yàn)閷?duì)于一個(gè)connection來(lái)說(shuō),HAProxyMessage只需要用到一次,后續(xù)的正常消息就不需要這個(gè)編碼器了,所以我們需要在write方法中監(jiān)聽(tīng)HAProxyMessage的狀態(tài),如果寫入成功之后,就從pipeline中移出HAProxyMessageEncoder和ClientHander。

    public void write(final ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        ChannelFuture future1 = ctx.write(msg, promise);
        if (msg instanceof HAProxyMessage) {
            future1.addListener((ChannelFutureListener) future2 -> {
                if (future2.isSuccess()) {
                    ctx.pipeline().remove(HAProxyMessageEncoder.INSTANCE);
                    ctx.pipeline().remove(ClientHander.this);
                } else {
                    ctx.close();
                }
            });
        }
    }

最后我們構(gòu)建了一個(gè)虛擬的HAProxyMessage,然后通過(guò)netty客戶端進(jìn)行發(fā)送:

HAProxyMessage message = new HAProxyMessage(
                    HAProxyProtocolVersion.V2, HAProxyCommand.PROXY, HAProxyProxiedProtocol.TCP4,
                    "127.0.0.1", "127.0.0.2", 8000, 9000);
            ch.writeAndFlush(message).sync();
            ch.writeAndFlush(Unpooled.copiedBuffer("this is a proxy protocol message!", CharsetUtil.UTF_8)).sync();
            ch.close().sync();

總結(jié)

上面的代碼只是一個(gè)簡(jiǎn)單的模擬proxy protocol在netty中的使用情況,并不代表上面的代碼就可以在實(shí)際的項(xiàng)目中應(yīng)用了。如果你想使用的話,可以在下面的代碼上面繼續(xù)豐富和完善。

本文的代碼,大家可以參考:

learn-netty4

以上就是netty對(duì)proxy protocol代理協(xié)議的支持詳解的詳細(xì)內(nèi)容,更多關(guān)于netty支持proxy protocol代理協(xié)議的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java利用for循環(huán)輸出空心菱形的實(shí)例代碼

    Java利用for循環(huán)輸出空心菱形的實(shí)例代碼

    這篇文章主要介紹了Java利用for循環(huán)輸出空心菱形的實(shí)例代碼,需要的朋友可以參考下
    2014-02-02
  • java springboot郵箱找回密碼功能的實(shí)現(xiàn)講解

    java springboot郵箱找回密碼功能的實(shí)現(xiàn)講解

    這篇文章主要介紹了java springboot郵箱找回密碼功能的實(shí)現(xiàn)講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • eclipse怎么引入spring boot項(xiàng)目插件的方法

    eclipse怎么引入spring boot項(xiàng)目插件的方法

    這篇文章主要介紹了eclipse怎么引入spring boot項(xiàng)目插件的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • Java實(shí)時(shí)監(jiān)控日志文件并輸出的方法詳解

    Java實(shí)時(shí)監(jiān)控日志文件并輸出的方法詳解

    這篇文章主要給大家介紹了關(guān)于Java實(shí)時(shí)監(jiān)控日志文件并輸出的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編一起來(lái)學(xué)習(xí)學(xué)習(xí)吧。
    2017-06-06
  • SpringMvc使用GoogleKaptcha生成驗(yàn)證碼

    SpringMvc使用GoogleKaptcha生成驗(yàn)證碼

    這篇文章主要為大家詳細(xì)介紹了SpringMvc項(xiàng)目中使用GoogleKaptcha 生成驗(yàn)證碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-09-09
  • 解決MyEclipse中的Building workspace問(wèn)題的三個(gè)方法

    解決MyEclipse中的Building workspace問(wèn)題的三個(gè)方法

    這篇文章主要介紹了解決MyEclipse中的Building workspace問(wèn)題的三個(gè)方法,需要的朋友可以參考下
    2015-11-11
  • Spring Security十分鐘入門教程

    Spring Security十分鐘入門教程

    這篇文章主要介紹了Spring Security入門教程,Spring Security是一個(gè)能夠?yàn)榛赟pring的企業(yè)應(yīng)用系統(tǒng)提供聲明式的安全訪問(wèn)控制解決方案的安全框架
    2022-09-09
  • C# TreeNode案例詳解

    C# TreeNode案例詳解

    這篇文章主要介紹了C# TreeNode案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • java中String,數(shù)組,ArrayList三者之間的轉(zhuǎn)換

    java中String,數(shù)組,ArrayList三者之間的轉(zhuǎn)換

    這篇文章主要介紹了java中String,數(shù)組,ArrayList三者之間的轉(zhuǎn)換,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • 關(guān)于pom.xml中maven無(wú)法下載springcloud包問(wèn)題

    關(guān)于pom.xml中maven無(wú)法下載springcloud包問(wèn)題

    小編遇到這樣一個(gè)問(wèn)題spring-cloud-starter-feign,spring-cloud-starter-eureka 一直無(wú)法下載,maven倉(cāng)庫(kù)中包路徑顯示為unknown,怎么解決呢?下面小編給大家?guī)?lái)了pom.xml中maven無(wú)法下載springcloud包問(wèn)題,需要的朋友可以參考下
    2022-08-08

最新評(píng)論

家居| 缙云县| 普安县| 乐东| 沧源| 苏尼特左旗| 固安县| 青田县| 河西区| 许昌县| 内江市| 赤壁市| 高阳县| 个旧市| 个旧市| 兴仁县| 蓝田县| 龙岩市| 焦作市| 开封县| 洛宁县| 汝南县| 莎车县| 黄冈市| 丹寨县| 正蓝旗| 谢通门县| 济南市| 临西县| 广饶县| 隆尧县| 临高县| 新津县| 云霄县| 易门县| 安顺市| 凤翔县| 当涂县| 视频| 莆田市| 桂阳县|