netty http3功能从零开始

1、windows安装jdk和mvn、gradle、gloovy

 配置环境变量JAVA_HOME  CLASSPATH   MVN_HOME GRADLE_HOME GLOOVY_HOME
 mvn和gradle都是用来管理和编译java项目的,mvn比较老,现在一般用gradle

2、vscode环境

 vscode安装extension:Extension Pack for java
 设置->extension->java 修改setting.json
{
    "java.jdt.ls.java.home": "D:\\Program Files\\Java\\jdk-20",
    "java.semanticHighlighting.enabled": true,
    "editor.suggestSelection": "first",
    "vsintellicode.modify.editor.suggestSelection": "automaticallyOverrodeDefaultValue",
    "java.configuration.checkProjectSettingsExclusions": false,
    "git.ignoreWindowsGit27Warning": true,
    "java.requirements.JDK11Warning": false,
    "java.eclipse.downloadSources": true,
    "java.maven.downloadSources": true,
    "java.configuration.maven.globalSettings": "D:\\Program Files\\apache-maven-3.9.2\\conf\\settings.xml",
    "java.debug.settings.exceptionBreakpoint.skipClasses": [

    ],
}

修改环境变量(JAVA_home):
在这里插入图片描述

3、创建新项目

idea可以直接创建,但我用的vscode,不知道怎么创建
在cmd内使用gradle命令创建(gradle init --type java-application):
在这里插入图片描述
执行完毕后可以看到生成了一些文件:
在这里插入图片描述
修改build.gradle文件:


plugins {
   id 'java'
   //配合shadowJar将依赖的jar打在一个包里
   id 'com.github.johnrengelman.shadow' version '2.0.4'
}

group 'hyper.http.server'
version '1.0-SNAPSHOT'

//很多镜像被墙或者速度很慢,需要设置代理
repositories {
    maven {
      url 'https://maven.aliyun.com/repository/central/'
    }
    maven {
      url 'https://maven.aliyun.com/repository/public/'
    }
    maven {
      url 'https://maven.aliyun.com/repository/spring/'
    }    
    maven {
      url 'https://maven.aliyun.com/repository/google/'
    }
    maven {
      url 'https://maven.aliyun.com/repository/gradle-plugin/'
    }
    maven {
      url 'https://maven.aliyun.com/repository/spring-plugin/'
    }
	maven { url 'https://www.jitpack.io' }
    mavenLocal()
    mavenCentral()
}

dependencies {
    implementation "io.netty:netty-all:4.1.78.Final"
    implementation "io.netty:netty-codec-http:4.1.78.Final"
	implementation "io.netty.incubator:netty-incubator-codec-http3:0.0.12.Final"
    implementation 'io.netty.incubator:netty-incubator-codec-quic:0.0.12.Final'
    implementation 'io.netty.incubator:netty-incubator-codec-native-quic:0.0.28.Final'
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

shadowJar {
    baseName = 'hyperhttp'
    version = '1.0-SNAPSHOT'
    classifier = null
    manifest {
        //attributes 'Main-Class': 'com.example.Main'
    }
}

4、netty环境

4.1 代码下载

首先根据jdk环境和netty分支,选择jdk版本对应的分支,jdk换版本会有很多莫名其妙的问题。
源码路径:https://github.com/netty/netty
netty的pom.xml总指定了jdk版本:
在这里插入图片描述
还要下载netty-incubator-codec-quic用来编译netty-quiche-xxx.so,libquiche.so等
(建议用linux环境,windows用dll但更麻烦一些,工具安装麻烦,问题也多)
https://github.com/netty/netty-incubator-codec-quic

可以使用IntellJ的右键Dependency analazy选择正确的版本,到没有版本冲突为止

4.2 编译netty-incubator-codec-quic

需要安装c、c++、rust和go相关的软件,包括rust(curl --proto ‘=https’ --tlsv1.2 -sSf https://sh.rustup.rs | sh)、c++(sudo apt install build-essential)、cmake(https://cmake.org/download/,apt install cmake)、Ninja(https://github.com/ninja-build/ninja/releases,apt install ninja-build)、go(apt install golang)、nasm(https://www.nasm.us/pub/nasm/releasebuilds/),autoreconf(sudo apt install -y autoconf automake libtool),还有其他工具,按照错误提示安装。
先执行(不然编译过程中下载代码会失败):

git config --global url."https://github.com/google/boringssl.git".insteadOf "https://boringssl.googlesource.com/boringssl"

在代码目录下执行:mvn compile -X(X是为了看到错误细节)
执行完会生成libquiche.so
在这里插入图片描述

继续执行mvn install -Dmaven.test.skip=true
执行完会生成libnetty_quiche_xxx.so,install过程中会自动将生成的库设置好动态链接:

[INFO] libtool: install: /usr/bin/install -c .libs/libnetty_quiche_linux_x86_64-0.0.46.Final.so /home/http3/netty-incubator-codec-quic-46/codec-native-quic/target/native-build/target/lib/libnetty_quiche_linux_x86_64-0.0.46.Final.so
[INFO] libtool: install: (cd /home/http3/netty-incubator-codec-quic-46/codec-native-quic/target/native-build/target/lib && { ln -s -f libnetty_quiche_linux_x86_64-0.0.46.Final.so libnetty_quiche_linux_x86_64.so || { rm -f libnetty_quiche_linux_x86_64.so && ln -s libnetty_quiche_linux_x86_64-0.0.46.Final.so libnetty_quiche_linux_x86_64.so; }; })
[INFO] libtool: install: /usr/bin/install -c .libs/libnetty_quiche_linux_x86_64.lai /home/http3/netty-incubator-codec-quic-46/codec-native-quic/target/native-build/target/lib/libnetty_quiche_linux_x86_64.la
[INFO] libtool: finish: PATH="/root/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/http3/jdk-20.0.1/bin:/sbin" ldconfig -n /home/http3/netty-incubator-codec-quic-46/codec-native-quic/target/native-build/target/lib
[INFO] ----------------------------------------------------------------------
[INFO] Libraries have been installed in:
[INFO]    /home/http3/netty-incubator-codec-quic-46/codec-native-quic/target/native-build/target/lib

也可以将这两个so拷贝到/usr/lib/下面
(用老版本很可能编不过,因为老版本需要每个git先下载再切分支,很慢很慢,很多次成功一次,很可能切分支失败你不知道,最后就版本不对应,你也不知道,github的访问有多愁人你懂的。
很老的版本,比如我用的12,commit点都找不到了,根本不可能编的过)

在/etc/profile中添加:

export LD_LIBRARY_PATH=/usr/lib

然后source /etc/profile

4.3 编译运行自己的项目

./gradlew build后打包,可以看到打包的jar包:

./build/libs/hyper.jar

将hyperhttp.jar和依赖的jar都拷贝到build/libs/运行:

/mnt/d/codeC/hyperhttp# cp -f build/libs/hyperhttp.jar bin/test/hyperhttp/
//-cp后面是依赖的jar包
/mnt/d/codeC/hyperhttp/bin/test#  java -verbose:class -cp "hyperhttp/*:" hyperhttp.AppServerExample

4.4 将所有依赖包打包到一个jar

按照上文中的配置,并执行./gradlew shadowJar可以生成build/libs/hyperhttp-1.0-SNAPSHOT.jar
但是需要注意在build.gradle中配置正确的依赖包打包文件:

dependencies {
    implementation "io.netty:netty-all:4.1.94.Final"
    implementation "io.netty:netty-codec-http:4.1.94.Final"
    implementation "io.netty.incubator:netty-incubator-codec-http3:0.0.19.Final"
    implementation 'io.netty.incubator:netty-incubator-codec-classes-quic:0.0.46.Final'
    //implementation 'io.netty.incubator:netty-incubator-codec-quic:0.0.19.Final'
    implementation 'io.netty.incubator:netty-incubator-codec-native-quic:0.0.46.Final'
    implementation 'org.bouncycastle:bcpkix-jdk15on:1.69'
    implementation 'org.bouncycastle:bcprov-jdk15on:1.69'
    implementation 'org.bouncycastle:bctls-jdk15on:1.69'
    implementation 'org.bouncycastle:bcutil-jdk15on:1.69'
    // Use JUnit test framework.
    //testImplementation 'junit:junit:4.13.2'
}

不然可能会缺失jar或者jar中class冲突。

5、问题总结

运行main后提示java.lang.NoClassDefFoundError:org/bouncycastle/jce/provider/BouncyCastleProvider

解决方法:
在pom.xml增加:

    <dependency>
        <groupId>org.bouncycastle</groupId>
        <artifactId>bcpkix-jdk15on</artifactId>
        <version>1.69</version>
        <scope>compile</scope>
        <optional>true</optional>
    </dependency>

mvn compile提示new line什么space

使用mvn compile -Dcheckstyle.skip=true 编译

下载gradle管理的代码,使用./gradlew build编译提示:Could not open settings generic class cache for settings file ‘D:\codeC\s2\UserAgent\user-agent-app\settings.gradle’ (C:\Users\xxx.gradle\caches\7.5.1\scripts\e4b9g30f2zgl5hoyt6gigkukl).

在这里插入图片描述
解决方法:修改D:\codeC\s2\UserAgent\user-agent-app\gradle\wrapper\gradle-wrapper.Properties中的distributionUrl为当前安装的版本

编译提示错误:An exception occurred applying plugin request [id: ‘com.github.johnrengelman.shadow’, version: ‘2.0.4’]

在这里插入图片描述
解决方法:将build.gradle中id ‘com.github.johnrengelman.shadow’ version '2.0.4’删了,用不着

编译的时候依赖下载慢

依赖库在setting.gradle中配置,执行gradle build(或者./gradlew build)后依赖库会下载到目录:
C:\Users(用户名).gradle\caches\modules-2\files-2.1
如果使用默认配置下载会很慢,有的依赖库下不来,需要配置国内镜像,比如阿里云:

repositories {    
    maven { url 'https://maven.aliyun.com/repository/public/' }    
    maven { url 'https://maven.aliyun.com/repository/spring/' }
    mavenLocal()
    mavenCentral() 
}

java降级后gradle build提示找不到java.exe(java home是卸载版本的)

 删除C:\Users\xxx\.gradle文件夹后重新执行

编译时test报错

在这里插入图片描述
解决方法:跳过test–gradlew.bat build -x test

生成pom.xml(依赖包本地编译、查找的时候要)

D:\tool\quickio-main>gradlew.bat publishToMavenLocal
生成结果在D:\tool\quickio-main\build\publications\maven

依赖库找不到

首先要明确找不到的库应该在哪里,是否在阿里云镜像上(在https://maven.aliyun.com/上查找)
阿里云镜像没有的话在不在别的位置,也要加入setting
比如quickio在https://www.jitpack.io(很多github的项目可能都在这个网站上发布包),就要把这个加入:
在这里插入图片描述
也可以本地编译后放入C盘下的缓存中

vscode提示System.out cannot be resolved to a type

在Setting->Extension找到java相关的setting.json,里边配置了maven配置文件的路径,
将对应路径下的setting中jdk设置上。
在java的setting.json中增加"java.import.generatesMetadataFilesAtProjectRoot": true,
在这里插入图片描述
在这里插入图片描述

找不到netty quiche dll

Caused by: java.lang.LinkageError: Possible multiple incompatible native libraries on the classpath for ‘C:\Users\llll\AppData\Local\Temp\netty_quiche_windows_x86_643357633935921649459.dll’?
Caused by: java.lang.NoSuchMethodError: Method io.netty.incubator.codec.quic.QuicheNativeStaticallyReferencedJniMethods.quicheRecvInfoOffsetofTo()I not found
在这里插入图片描述
这实际上是找不到quiche的JNI层,该层实现在netty-incubator-codec-quic的codec-native-quic中。
gradle build只会下载jar包(下载到C:\Users\lll.gradle\caches\modules-2\files-2.1),并不会下载其中JNI层的dll文件(在linux上是so文件),
可能需要手动编译。
下载对应版本后执行mvn build,注意将被墙的git路径替换掉,执行:

git config --global url."https://github.com/google/boringssl.git".insteadOf "https://boringssl.googlesource.com/boringssl"

或者手动修改pom.xml

注意准备好rust环境、c++环境、cmake(https://cmake.org/download/)、Ninja(https://github.com/ninja-build/ninja/releases)、go、nasm(https://www.nasm.us/pub/nasm/releasebuilds/)

代码会下载到netty-incubator-codec-quic/codec-native-quic/target下面

引用库冲突

引用库的版本间可能有冲突,可以先通过工具解决(以下例子通过IntelliJ)
在文件上右键Analyze Dependence
在这里插入图片描述

The C compiler identification is unknown

这是mvn compile -X才看得到的错误
linux上使用g++,windows上使用VisualStudio下的cl.exe等,如果已经安装了c++的VisualStudio,把对应exe目录加到path里。
在这里插入图片描述

Compiling the C compiler identification source file “CMakeCCompilerId.c” failed.

执行
“C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat” x86

【linux】java.lang.IllegalStateException: Unable to load cache item

这是由于java和maven版本不匹配,maven用的3.6.3,jdk是17,而maven3.6.x不支持jdk17
更换版本3.8.8,但只能更改path和maven_home到新的连接,如果覆盖原maven文件则出错:
mvn -v
Error: Could not find or load main class org.codehaus.plexus.classwords.launcher.Launcher
这可能是由于文件系统的问题,我把文件放在了windows D盘,linux使用可能有问题。

【linux】运行失败 Exception in thread “main” java.lang.UnsatisfiedLinkError: failed to load the required native library

root@DESKTOP-JJS9ODB:/mnt/d/codeC/hyperhttp/bin/test# java -cp "hyperhttp/*:" hyperhttp.AppServerExample
Exception in thread "main" java.lang.UnsatisfiedLinkError: failed to load the required native library
        at io.netty.incubator.codec.quic.Quic.ensureAvailability(Quic.java:78)
        at io.netty.incubator.codec.quic.QuicCodecBuilder.<init>(QuicCodecBuilder.java:59)
        at io.netty.incubator.codec.quic.QuicServerCodecBuilder.<init>(QuicServerCodecBuilder.java:48)
        at io.netty.incubator.codec.http3.Http3.newQuicServerCodecBuilder(Http3.java:148)
        at hyper.http.server.hyperHttpServer.<init>(hyperHttpServer.java:29)
        at hyperhttp.AppServerExample.main(AppServerExample.java:17)
Caused by: java.lang.LinkageError: Possible multiple incompatible native libraries on the classpath for '/tmp/libnetty_quiche_linux_x86_649259403363156468475.so'?
        at io.netty.util.internal.NativeLibraryLoader.rethrowWithMoreDetailsIfPossible(NativeLibraryLoader.java:414)
        at io.netty.util.internal.NativeLibraryLoader.loadLibrary(NativeLibraryLoader.java:402)
        at io.netty.util.internal.NativeLibraryLoader.load(NativeLibraryLoader.java:218)
        at io.netty.incubator.codec.quic.Quiche.loadNativeLibrary(Quiche.java:77)
        at io.netty.incubator.codec.quic.Quiche.<clinit>(Quiche.java:57)
        at io.netty.incubator.codec.quic.Quic.<clinit>(Quic.java:43)
        ... 5 more
Caused by: java.lang.NoSuchMethodError: Method io.netty.incubator.codec.quic.QuicheNativeStaticallyReferencedJniMethods.quicheRecvInfoOffsetofTo()I not found
        at java.base/jdk.internal.loader.NativeLibraries.load(Native Method)
        at java.base/jdk.internal.loader.NativeLibraries$NativeLibraryImpl.open(NativeLibraries.java:331)
        at java.base/jdk.internal.loader.NativeLibraries.loadLibrary(NativeLibraries.java:197)
        at java.base/jdk.internal.loader.NativeLibraries.loadLibrary(NativeLibraries.java:139)
        at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2404)
        at java

linux运行java: java -verbose:class -cp “hyperhttp/*:” hyperhttp.AppClientExample
windows也是这里失败,实际上不是在找so,而是在找class相关的jar
需要编译netty-incubator-codec-quic(maven compile -X)成功生成的codec-classes-quic/target/netty-incubator-codec-classes-quic-0.0.46.Final.jar

但有的情况下这个jar没问题也会出现这个提示,可以查看过程是不是引用错了jar,正确的应该是:

[class,load] io.netty.incubator.codec.quic.QuicheNativeStaticallyReferencedJniMethods source: file:/mnt/d/codeC/hyperhttp/bin/test/hyperhttp/netty-incubator-codec-classes-quic-0.0.46.Final.jar

另外一个jar也包含了这个class,就是netty-incubator-codec-quic-0.0.19.Final.jar,java可能会优先加载错误的jar包,删除这个jar就好了。
jar包的内容使用命令:

jar tf xxx.jar

java.lang.UnsupportedOperationException: OpenJdkSelfSignedCertGenerator not supported on the used JDK version

在这里插入图片描述
netty这个版本依赖于jdk20(pom.xml)

fatal: unable to access ‘https://github.com/google/boringssl.git/’: Failed to connect to github.com port 443: Connection refused

还有fatal: unable to connect to github.com:
github.com[0: 20.205.243.166]: errno=Invalid argument

这很可能不是错误,就是github太难连上了,在不同的时间多试几次吧。。

java.security.cert.CertificateException: No provider succeeded to generate a self-signed certificate.

java.security.cert.CertificateException: No provider succeeded to generate a self-signed certificate.
java.lang.UnsupportedOperationException: OpenJdkSelfSignedCertGenerator not supported on the used JDK version
在这里插入图片描述
虽然提示了jdk版本不对(不知道为什么提示,但版本确实是对的,我特意换了指定版本)
但实际上,看代码:
在这里插入图片描述
如果支持了BouncyCastle,就用不着走后面的流程了,
所以依赖包位置上根据对应版本的netty pom.xml拷贝几个jar包
windows位置:C:\Users\lll.m2\repository\org\bouncycastle\bcutil-jdk15on\1.69
linux位置:/root/.m2/repository/org\bouncycastle\bcutil-jdk15on\1.69
netty的pom.xml指定的版本:
在这里插入图片描述
pom里边指定了三个jar包,实际上拷贝完这三个jar包并不管用,还有一个bcutil-jdk15on-1.69才可以:
在这里插入图片描述

Could not set unknown property ‘baseName’ for task ‘:shadowJar’ of type com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar

FAILURE: Build failed with an exception.

* Where:
Build file '/mnt/d/codeC/hyperhttp/build.gradle' line: 69

* What went wrong:
A problem occurred evaluating root project 'hyperhttp'.
> Could not set unknown property 'baseName' for task ':shadowJar' of type com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.

这可能是由于shadow比较新,
旧的ShadowJar版本使用的是baseName,但新的版本更换了方式:

plugins {
   id 'java'
   id 'com.github.johnrengelman.shadow' version '8.1.1'
}
    shadowJar {
        archiveBaseName.set('hyperhttp')
        archiveClassifier.set('1.0-SNAPSHOT')
        archiveVersion.set('')
    }

General error during conversion: Unsupported class file major version 64

root@DESKTOP-JJS9ODB:/mnt/d/codeC/hyperhttp# ./gradlew build -X

FAILURE: Build failed with an exception.

* Where:
Build file '/mnt/d/codeC/hyperhttp/build.gradle'

* What went wrong:
Could not compile build file '/mnt/d/codeC/hyperhttp/build.gradle'.
> startup failed:
  General error during conversion: Unsupported class file major version 64

  java.lang.IllegalArgumentException: Unsupported class file major version 64
        at groovyjarjarasm.asm.ClassReader.<init>(ClassReader.java:199)
        at groovyjarjarasm.asm.ClassReader.<init>(ClassReader.java:180)
        at groovyjarjarasm.asm.ClassReader.<init>(ClassReader.java:166)
        at groovyjarjarasm.asm.ClassReader.<init>(ClassReader.java:287)
        at org.codehaus.groovy.ast.decompiled.AsmDecompiler.parseClass(AsmDecompiler.java:81)
        at org.codehaus.groovy.control.ClassNodeResolver.findDecompiled(ClassNodeResolver.java:251)
        at org.codehaus.groovy.control.ClassNodeResolver.tryAsLoaderClassOrScript(ClassNodeResolver.java:189)
        at org.codehaus.groovy.control.ClassNodeResolver.findClassNode(ClassNodeResolver.java:169)
        at org.codehaus.groovy.control.ClassNodeResolver.resolveName(ClassNodeResolver.java:125)
        at org.codehaus.groovy.ast.decompiled.AsmReferenceResolver.resolveClassNullable(AsmReferenceResolver.java:57)
        at org.codehaus.groovy.ast.decompiled.AsmReferenceResolver.resolveClass(AsmReferenceResolver.java:44)

这是由于jdk版本与gradle版本不配套,可以修改gradle\wrapper下的配置文件到相应的版本,
build时就会自动下载响应的gradle版本(我是jdk20, gradle8.2, shadow8.1.1):

在这里插入图片描述
这个路径可能会下载失败,提示Exception in thread “main” java.io.IOException: Downloading from https://services.gradle.org/distributions/gradle-7.5.1-bin.zip failed: timeout
可以换成distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-7.5.1-bin.zip

You can’t map a property that does not exist: propertyName=classifier

FAILURE: Build failed with an exception.

* Where:
Build file '/mnt/d/codeC/hyperhttp/build.gradle' line: 12

* What went wrong:
An exception occurred applying plugin request [id: 'com.github.johnrengelman.shadow', version: '2.0.4']
> Failed to apply plugin class 'com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin'.
   > You can't map a property that does not exist: propertyName=classifier

可能是由于shadow版本和gradle不配套,如果gradle用8.x.x,那么shadow相应的一般也是8.x.x

vscode所有类都提示Implicit super constructor XXXX() is undefined for default constructor

一般是vscode中JAVA_HOME配置有问题,首先确认环境变量中java home配置是否正确,然后确认vscode中setting->java->setting.json中"java.jdt.ls.java.home"是否正确。
如果都没有问题,那么看下项目里边是否指定了jdk版本,比如build.gradle中:

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值