跳至主要内容

博文

目前显示的是 2021的博文

使用CoreDNS加快XBOX在中国大陆的下载速度

Welcome file 简介 最近弄了个xbox series主机,在主机上下载游戏的时候,发现下载速度的随机性很大,有时候能有100Mbps左右,大部分时候在10-20Mbps之间。经过搜索,发现其实大陆是有xbox的服务器的,只是默认分配不到。也看了些方案,说是用nginx之类的web服务器拦截http请求,然后返回301或302状态,让xbox跳回带cn后缀的服务器。看了上述原理,我感觉在DNS上完成应该就行,于是在家里服务器上搭建了CoreDNS。选用CoreDNS是因为之前在k8s上用过CoreDNS,对相关配置比较熟悉,性能也不错。 安装和配置CoreDNS CoreDNS的安装十分简单,直接下载对应平台[1]的二进制包就行,也可以用Docker安装,看个人喜好。 配置只需要在安装包的当前目录下建立一个名字为 Corefile 的文件就行,最初我的配置如下: . { rewrite name substring xboxlive.com xboxlive.cn forward . /etc/resolv.conf log errors cache } 保存之后直接启动 ./coredns 。 启动之后可以使用dig命令查看是否生效(可以修改@后的地址为服务器地址): dig @127.0.0.1 www.xboxlive.com 通过查看应答,发现跟之前的不一样,而跟xboxlive.cn的返回一样,表示已部署成功并生效。 在xbox中设置dns 打开xbox的网络设置,然后在高级设置中配置对应地址就行。 测试xbox下载速度 马上去试了下载游戏,发现在浏览游戏界面时的速度变快了,但是下载速度还是在10-20Mbps之间。觉得有点奇怪,再次搜索相关的文章,发现可能是下载的游戏不是国区游戏的关系,最好是找到亚洲其他国家cdn的ip地址替换[2]。 经过测试几个cdn地址,最终的 Corefile 配置如下: . { hosts { 219.76.10.33 assets1.xboxlive.com 219.76.10.184 assets1.xboxlive.com 219.76.10.3

让Quartz Scheduler在小写表名下运行

Welcome file 简介 在使用Quartz框架时,框架内的sql语句都是使用大写的表名来操作,但是在公司内统一小写并且开启了大小写敏感的情况下,Quartz就会报错。Quartz官方也不打算提供可配置的大小写选项,只是说开启数据库大小写不敏感就可以解决[1]。在很多情况下,公司内的规范都不是个人能轻易改变的,所以比较靠谱的方法就是改Quartz框架的代码。还好框架提供了足够的扩展点,让使用者可以解决这个问题。 修改sql执行类的配置 与sql执行相关的配置有tablePrefix、driverDelegateClass和lockHandler.class,其中后两项是使用新的实现替换框架内置的实现。新的实现可以直接使用Quartz框架的,只需要把类中对应的表名大写改成小写即可,可参考基于Spring Boot的示例工程[2]。不过需要注意的是目前我只使用到了基于Cron的调度功能,所以如果有使用其他实现,需要再对示例工程做一些相应的修改。 基于Spring Boot的application配置示例: spring.datasource.url = jdbc:mysql://127.0.0.1:3306/quartz_demo spring.datasource.username = root spring.datasource.password = root spring.quartz.job-store-type = jdbc spring.quartz.properties.org.quartz.jobStore.tablePrefix = qrtz_ spring.quartz.properties.org.quartz.jobStore.driverDelegateClass = com.nereusyi.demos.quartz.StdJDBCDelegate spring.quartz.properties.org.quartz.jobStore.lockHandler.class = com.nereusyi.demos.quartz.StdRowLockSemaphore 参考资料 [1]quartz-scheduler github issues: https://g

加快Maven的构建速度

Welcome file 简介 对于CI/CD服务器,需要频繁对maven项目进行构建。我们可以基于这样的场景,使用一些技术加快maven构建速度。 使用所有cpu进行构建 默认情况下,maven只使用一个线程进行构建,但对于有多核cpu的CI/CD服务器来说,这样就有些浪费cpu资源了。这时可以通过Maven 3的并行构建来充分利用多核的性能,加速maven的构建。 构建命令: mvn -T 1C clean install 设置 -T 1C 表示每个CPU一个线程。 这对于有多个模块并且没有依赖关系的大型项目来说,能极大的加快构建速度。更详细的资料可见参考资料1。 后台运行Maven maven每次启动构建,JVM都需要重新加载相关的类,初始化相关的环境。对于CI/CD服务器来说,频繁构建就导致频繁的JVM初始化,会消耗大量时间。基于这样的场景,社区开发了maven daemon项目。 该项目的目的是让maven一直运行在后台,这样就可以免去不少maven构建时的初始化步骤。还有该项目使用了GraalVM,也就是说maven daemon是直接以本地二进制代码执行的,执行效率和内存占用相对传统JVM来说更有优势。 maven daemon使用起来也很简单,跟普通的mvn命令一样,只需要把mvn改成mvnd就可以(不同环境的安装方法,可见参考资料2的官方文档): mvnd clean install maven daemon默认就是以多线程的方式运行,所以不需要再额外设置参数。 参考资料 [1]Parallel builds in Maven 3: https://cwiki.apache.org/confluence/display/MAVEN/Parallel+builds+in+Maven+3 [2]mvn daemon: https://github.com/mvndaemon/mvnd

增强Java8和11的空指针异常提示

Welcome file 简介 在Java14之前,如果出现 NullPointerException ,只会知道有对象为null导致空指针,如果在多个对象上调用了方法,那么就很难区分到底哪个对象为null。在 Java14中,JEP 358(详情见参考资料1)的出现改善了这样的情况,丰富了 NullPointerException 的输出,但目前常用的版本还是Java8或11等长期支持的版本,所以下面介绍一种方法来让8和11也能实现更加丰富的 NullPointerException 提示。 增加空指针异常提示 示例代码如下: public static void main ( String [ ] args ) { Map < String , String > map = null ; demo ( map ) ; } public static void demo ( Map < String , String > map ) { System . out . println ( map . get ( "foo" ) . toLowerCase ( ) ) ; } 8和11在增强之前的输出如下: Exception in thread "main" java . lang . NullPointerException at com . nereusyi . npe . NpeDemo . demo ( NpeDemo . java : 18 ) at com . nereusyi . npe . NpeDemo . main ( NpeDemo . java : 14 ) 并没有什么有用的信息,根据这段异常也没法判断到底是哪个对象为null。 下面就可以开始动手增强异常提示了,我们可以在https://github.com/odnoklassniki/jvmti-tools/tree/master/richNPE中找到增强异常提示的代码,具体使用方法也在首页中有详细说明。 下载该文件之后,需要手动编译该文件,首页中少了Mac的编译命令,我在这里也补上: # Lin

Java NIO File API 使用总结 2

Welcome file 简介 在之前的文章中总结了NIO的Path接口和Files工具类的使用,本文继续总结一些其他的新功能和概念 NIO的基础概念和基本使用 不同于传统的Java File I/O的Stream流的概念,NIO基于Buffer和Channel重新设计了一套API。Buffer的API相对来说比较难用,不过具有零拷贝的功能,可以大大提高文件传输速度。 创建和写入文件 public static void createAndWriteFile ( ) { ByteBuffer byteBuffer = ByteBuffer . allocate ( 1024 ) ; byteBuffer . put ( "HelloWorld" . getBytes ( StandardCharsets . UTF_8 ) ) ; byteBuffer . flip ( ) ; try ( FileChannel fileChannel = FileChannel . open ( Path . of ( "/tmp/test.txt" ) , StandardOpenOption . CREATE , StandardOpenOption . WRITE ) ) { fileChannel . write ( byteBuffer ) ; } catch ( IOException e ) { e . printStackTrace ( ) ; } System . out . println ( "write success." ) ; } 可以看到,NIO写入文件前,需要先创建ByteBuffer对象,之后再往里面写入数据。写入之后必须调用 flip 方法重新设置position和limit,这也是Buffer中的概念。 之后再创建一个Channel,往Channel中写入数据。 读取文件 public static void readFile ( ) { Ch

Spring的Transaction事务管理

Welcome file 简介 本文的介绍主要从JDBC事务API的使用,到常用的SpringBoot中的事务管理,而SpringBoot其实是简化了很多Spring事务管理的配置。 JDBC的事务管理 基本的开启、提交和回滚事务 先复习一下JDBC的事务管理: public void jdbcSaveOrder ( ) throws SQLException { // 1. 拿到JDBC连接 Connection connection = dataSource . getConnection ( ) ; try ( connection ) { // 2. 设置自己控制事务 connection . setAutoCommit ( false ) ; . . . . // 3. 提交事务 connection . commit ( ) ; } catch ( Exception e ) { log . error ( "ds save order error:" , e ) ; // 4. 回滚事务 connection . rollback ( ) ; } } JDBC控制事务的API总体比较直观。 Spring的事务管理 可以从上面的JDBC示例中看到,事务管理最主要的几个操作就是:开启事务、提交事务、回滚事务。那么Spring是如何管理的呢?首先可以从不太常见的Spring编程式事务管理入手。 Spring编程式事务管理 我们可以通过 PlatformTransactionManager 来编程式开启事务,不过Spring提供了一个更加简便的 TransactionTemplate : @Service public class OrderService { @Autowired private Transacti

jvm的jit参数设置

Welcome file 简介 在工作中,经常会见到在启动Java程序的命令行里加一大把启动参数,有些是过时的,有些是不适合每个程序的。本文就简单说说经常见到的 -server 参数。 -server参数的作用 在早期jvm中, -server 参数表示Java程序使用Server VM运行。设置server参数可以让jit尽最大努力,用较多的启动时间,把较多的代码预先编译成native代码,增加运行效率。 但在现代JVM(如 Java 8)中,该参数并没有什么作用,因为现代JVM都是Server VM(详细见参考资料1)。 控制JIT编译的参数 现代JVM的JIT默认开启了分层编译,共分为5级: level 0 - interpreter level 1 - C1 with full optimization (no profiling) level 2 - C1 with invocation and backedge counters level 3 - C1 with full profiling (level 2 + MDO) level 4 - C2 所以如果想要JIT尽可能的优化,可以设置JIT使用C2编译,如: -XX:TieredStopAtLevel=4 另外,JIT对执行超过一定次数的代码,会编译成native代码,控制该次数的参数是: -XX:CompileThreshold=10000 。编译的native代码也会有大小限制,控制该大小的参数是: -XX:ReservedCodeCacheSize=240m 参考资料 [1]Oracle文档: https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html [2]JVM源码: http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/2b2511bd3cc8/src/share/vm/runtime/advancedThresholdPolicy.hpp#l34

Spring Boot Bean Validation 示例

Welcome file 简介 在应用的各个环节,最好对输入的参数进行校验,以保持应用的健壮性。Java的Bean Validation API对此提供了较好的支持,下面展示在Spring Boot中使用Bean Validation的示例。 使用环境:Java 11 和 Spring Boot 2.4.3.RELEASE 集成Bean Validation 在Spring Boot中集成Bean Validation与集成其他的框架类似,在 pom.xml 里引入相关的starter就可以: < dependency > < groupId > org.springframework.boot </ groupId > < artifactId > spring-boot-starter-web </ artifactId > </ dependency > < dependency > < groupId > org.springframework.boot </ groupId > < artifactId > spring-boot-starter-validation </ artifactId > </ dependency > 这里用springmvc做示例。 在Spring Boot老版本中,validation包不用单独引入。在新版本中,validation被拆分,需要单独引入。引入的实现是hibernate validator。 对Controller的入参校验 通常来说,一个Controller有下面几种方式接收入参: 通过request body,如解析json请求 用url path作为参数,如 order/{id} get请求的url形式的 ?key=value 传参,和post的类似key value传参等 Java Bean参数 通过request body和get、post的传参都可以通过一个java bean接收。我们可以在java bean中的字段上,加上校验的注解[1]: @Da

Spring Boot Actuator 2 示例

Welcome file 简介 Spring Boot Actuator为应用程序提供了各种开箱即用的运维特性,可以与应用方便的交互和监控。 使用环境:Java 11 和 Spring Boot 2.4.3.RELEASE 集成Spring Boot Actuator 在Spring Boot中集成Spring Boot Actuator与集成其他的框架类似,在 pom.xml 里引入相关的starter就可以: < dependency > < groupId > org.springframework.boot </ groupId > < artifactId > spring-boot-starter-actuator </ artifactId > </ dependency > < dependency > < groupId > org.springframework.boot </ groupId > < artifactId > spring-boot-starter-web </ artifactId > </ dependency > 由于大部分的使用场景还是web,所以这里也用Spring MVC做示例。 配置好 pom.xml 后,默认actuator仅暴露一些基本功能,实际使用中,根据需求暴露对应功能。为了简便测试,这里在 application.yml 中配置暴露全部功能: management : endpoints : web : exposure : include : "*" endpoint : health : enabled : true show-details : always probes : enabled : true shutdown : enabled : true metr