【技术应用】springboot内置tomcat配置unix域套接字属性

一、前言

近期总结了不少**UNIX域套接字协议(unix domain socket)**在工作中的应用,但是有一个工作中使用最多的组件支持使用UNIX DOMAIN SOCKET协议的应用示例一直没有总结,那就是:tomcat支持UNIX域套接字协议(unix domain socket)的应用示例,今天总结一下;

往期UNIX域套接字协议在组件中的应用示例:

1、【技术应用】java基于UNIX域套接字(unix domain socket)连接redis

2、【技术应用】java基于UNIX域套接字(unix domain socket)连接postgresql数据库

3、【技术应用】java基于UNIX域套接字(unix domain socket)连接mysql数据库

二、springboot内置tomcat

1、springboot支持tomcat内置功能,项目直接可以编译成jar包运行,tomcat可以编译到jar内部,简化了应用,这也是springboot一大特点;

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

2、springboot内置tomcat的配置项属性通常是在application.yml配置文件中设置,如下:

server:
  tomcat:
    accept-count: 80 #挂起的请求队列最大连接数,默认100
    max-connections: 2000  #最大连接数,默认10000,tomcat内tcp连接池的大小
    max-threads: 200 #最大线程数,默认200,超过加入等待队列,默认是100,当等待队列达到100后,直接拒绝此次请求返回connection refused。连接超时时间默认为20秒
    min-spare-threads: 5  #最小工作线程数
    connection-timeout: 60000  #server端的socket超时间,默认60s
    accesslog:
      enabled: true #启动tomcat访问日志

但是,通过分析所有的配置项,并没有发现unixdomainsocket相关的配置信息,所以不能通过apllication.yml配置文件实现unixdomainsocket功能;

3、springboot中通过代码配置内置tocmat属性;

1)配置Servlet容器的端口为9000

import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import
org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;
@Component
public class MyWebServerFactoryCustomizer implements
WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
  @Override
  public void customize(ConfigurableServletWebServerFactory server) {
  server.setPort(9000);
  }
}

2)使用ConfigurableServletWebServerFactory的子类(比如TomcatServletWebServerFactory)来配置Servlet容器

import java.time.Duration;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;
@Component
public class MyTomcatWebServerFactoryCustomizer implements
WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
  @Override
  public void customize(TomcatServletWebServerFactory server) {
  server.addConnectorCustomizers((connector) ->
connector.setAsyncTimeout(Duration.ofSeconds(20).toMillis()));
  }
}

三、tomcat对unix域套接字协议支持分析

1、tomcat的组成可以分为两部分:连接器容器
连接器(connector):专门用于处理与网络连接相关的问题,比如在web开发中的Socket链接,request封装,连接线程池等等。
容器(servlet):用来存放我们编写的网站程序。tomcat一共有四个容器:Engine, Host, Context和Wrapper。一个Wrapper对应一个Servlet, 一个Context对应一个应用(比如,默认情况下webapps/ROOT中存放的为主应用,对应一个站点的根目录),一个Host对应一个站点(例如不同的域名),Engine是引擎。

2、connector里面负责处理请求的协议主要有基于bio,nio,nio2,apr 几种不同的http协议,其中支持NIO协议的属性设置内是支持unixdomainsocket属性配置的;

pollerThreadPriority:(int)The priority of the poller threads
selectorTimeout:(int)The time in milliseconds to timeout on a select() for the poller
useSendfile:(bool)Use this attribute to enable or disable sendfile capability
socket.directBuffer:(bool)Boolean value, whether to use direct ByteBuffers or java mapped ByteBuffers
 
socket.directSslBuffer:(bool)Boolean value, whether to use direct ByteBuffers or java mapped ByteBuffers for the SSL buffers
socket.appReadBufSize:(int)Each connection that is opened up in Tomcat get associated with a read ByteBuffer
socket.appWriteBufSize:(int)Each connection that is opened up in Tomcat get associated with a write ByteBuffer
socket.bufferPool:(int)The NIOx connector uses a class called NioXChannel that holds elements linked to a socket
socket.bufferPoolSize:(int)The NioXChannel pool can also be size based, not used object based
 
socket.processorCache:(int)Tomcat will cache SocketProcessor objects to reduce garbage collection
socket.eventCache:(int)Tomcat will cache PollerEvent objects to reduce garbage collection
 
unixDomainSocketPath:Where supported, the path to a Unix Domain Socket that this Connector will create and await incoming connections
unixDomainSocketPathPermissions:Where supported, the posix permissions that will be applied to the to the Unix Domain Socket specified with unixDomainSocketPath above
useInheritedChannel:(bool)Defines if this connector should inherit an inetd/systemd network socket

所以,我们只需要设置unixDomainSocketPath属性就可以了;

四、springboot内置tomcat使用unixdomainsocket协议示例

1、jdk版本
在使用springboot的内置tomcat使用,依赖的jdk最低版本是jdk16,因为从jdk16开始,才支持Unix套接字;
在2019 Windows Server和Windows 10提供了对Unix套接字的支持,Unix套接字常用语本地进程之间通信,相比于TCP协议,本地进程使用Unix套接字可以更高效安全的通信。JDK16新增了一个适配Unix套接字的新接口java.net.UnixDomainSocketAddress用于支持这一特性

如果jdk版本低于16,则会报错:

Caused by: java.lang.UnsupportedOperationException: Java Runtime does not support Unix domain sockets. You must use Java 16 to use this feature.
	at org.apache.tomcat.util.compat.JreCompat.openUnixDomainServerSocketChannel(JreCompat.java:337) ~[tomcat-embed-core-9.0.70.jar:9.0.70]
	at org.apache.tomcat.util.net.NioEndpoint.initServerSocket(NioEndpoint.java:252) ~[tomcat-embed-core-9.0.70.jar:9.0.70]
	at org.apache.tomcat.util.net.NioEndpoint.bind(NioEndpoint.java:230) ~[tomcat-embed-core-9.0.70.jar:9.0.70]
	at org.apache.tomcat.util.net.AbstractEndpoint.bindWithCleanup(AbstractEndpoint.java:1227) ~[tomcat-embed-core-9.0.70.jar:9.0.70]
	at org.apache.tomcat.util.net.AbstractEndpoint.init(AbstractEndpoint.java:1240) ~[tomcat-embed-core-9.0.70.jar:9.0.70]
	at org.apache.coyote.AbstractProtocol.init(AbstractProtocol.java:604) ~[tomcat-embed-core-9.0.70.jar:9.0.70]
	at org.apache.coyote.http11.AbstractHttp11Protocol.init(AbstractHttp11Protocol.java:76) ~[tomcat-embed-core-9.0.70.jar:9.0.70]
	at org.apache.catalina.connector.Connector.initInternal(Connector.java:1047) ~[tomcat-embed-core-9.0.70.jar:9.0.70]
	... 21 common frames omitted

所以我们使用jdk19

<properties>
        <java.version>19</java.version>
    </properties>

2、springboot内置tomcat属性配置

springboot内置tomcat配置unix域套接字协议很简单,只需要设置connector.setProperty("unixDomainSocketPath","D:\\http.sock")

package com.example.tomcat19.config;

import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Configuration;


@Configuration
public class MyTomcatWebServerFactoryCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

    @Override
    public void customize(TomcatServletWebServerFactory server) {
        server.addConnectorCustomizers((connector) -> {
                    //connector.setAsyncTimeout(Duration.ofSeconds(20).toMillis());
                    //connector.setProperty("protocol","org.apache.coyote.http11.Http11NioProtocol");
                    connector.setProperty("unixDomainSocketPath","D:\\http.sock");
                }
        );
    }
}

3、服务端测试接口

package com.example.tomcat19.action;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
public class TestAction {

    @GetMapping("/test")
    public String test(){
        log.info("收到请求id:1");
        return "哈哈哈";
    }

}

服务端项目结构:
在这里插入图片描述

4、请求客户端

为了方便验证,我们这里使用socket基于http.sock向服务端发送http请求报文

package com.sk.init;

import java.net.StandardProtocolFamily;
import java.net.UnixDomainSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class Test {

    public static void main(String[] args) throws Exception{
        // 建立 Unix Socket 连接
        //File sockFile = new File("D:\\http.sock");
        SocketChannel socketChannel = SocketChannel.open(StandardProtocolFamily.UNIX);

        UnixDomainSocketAddress of = UnixDomainSocketAddress.of("D:\\http.sock");
        //UnixDomainSocketAddress of = UnixDomainSocketAddress.of("D:\\test.sock");
        boolean connect = socketChannel.connect(of);
        System.out.println(connect);

        String newData = "this is domain socket..." + System.currentTimeMillis();
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("GET /test HTTP/1.1").append("\n");
       
        stringBuffer.append("Host: 127.0.0.1").append("\r\n");
        stringBuffer.append("Connection: keep-alive").append("\r\n");
        stringBuffer.append("Cache-Control: max-age=0").append("\r\n");
        stringBuffer.append("sec-ch-ua: \" Not A;Brand\";v=\"99\", \"Chromium\";v=\"100\", \"Google Chrome\";v=\"100\"").append("\r\n");
        stringBuffer.append("sec-ch-ua-mobile: ?0").append("\r\n");
        stringBuffer.append("sec-ch-ua-platform: \"Windows\"").append("\r\n");
        stringBuffer.append("Upgrade-Insecure-Requests: 1").append("\r\n");
        stringBuffer.append("User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36").append("\r\n");
        stringBuffer.append("Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9").append("\r\n");
        stringBuffer.append("Sec-Fetch-Site: none").append("\r\n");
        stringBuffer.append("Sec-Fetch-Mode: navigate").append("\r\n");
        stringBuffer.append("Sec-Fetch-User: ?1").append("\r\n");
        stringBuffer.append("Sec-Fetch-Dest: document").append("\r\n");
        stringBuffer.append("Accept-Encoding: gzip, deflate, br").append("\r\n");
        stringBuffer.append("Accept-Language: zh-CN,zh;q=0.9").append("\r\n");
        stringBuffer.append("\r\n");

        //stringBuffer.append("Accept: */*").append("\r\n");
        ByteBuffer buf = ByteBuffer.allocate(2048);
        buf.clear();
        buf.put(stringBuffer.toString().getBytes());
        //buf.put(newData.getBytes());
        buf.flip();
        while (buf.hasRemaining()) {
            socketChannel.write(buf);
        }
        socketChannel.close();
    }

}

5、请求结果

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::       (v3.0.2-SNAPSHOT)

23:06:54.720 [main] INFO  org.springframework.boot.StartupInfoLogger.logStarting(StartupInfoLogger.java:51) - Starting Tomcat19Application using Java 19.0.1 with PID 6248 (G:\work3\UDS redis\tomcat19\target\classes started by Administrator in G:\work3\UDS redis\tomcat19)
23:06:54.962 [main] INFO  org.springframework.boot.SpringApplication.logStartupProfileInfo(SpringApplication.java:630) - No active profile set, falling back to 1 default profile: "default"
23:06:55.895 [main] INFO  org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:108) - Tomcat initialized with port(s): 8086 (http)
23:06:55.905 [main] INFO  org.apache.juli.logging.DirectJDKLog.log(DirectJDKLog.java:173) - Initializing ProtocolHandler ["http-nio-D:\\http.sock"]
23:06:55.906 [main] INFO  org.apache.juli.logging.DirectJDKLog.log(DirectJDKLog.java:173) - Starting service [Tomcat]
23:06:55.906 [main] INFO  org.apache.juli.logging.DirectJDKLog.log(DirectJDKLog.java:173) - Starting Servlet engine: [Apache Tomcat/10.1.4]
23:06:55.996 [main] INFO  org.apache.juli.logging.DirectJDKLog.log(DirectJDKLog.java:173) - Initializing Spring embedded WebApplicationContext
23:06:55.996 [main] INFO  org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.prepareWebApplicationContext(ServletWebServerApplicationContext.java:291) - Root WebApplicationContext: initialization completed in 991 ms
23:06:56.344 [main] INFO  org.apache.juli.logging.DirectJDKLog.log(DirectJDKLog.java:173) - Starting ProtocolHandler ["http-nio-D:\\http.sock"]
23:06:56.364 [main] INFO  org.springframework.boot.web.embedded.tomcat.TomcatWebServer.start(TomcatWebServer.java:220) - Tomcat started on port(s): -1 (http) with context path ''
23:06:56.374 [main] INFO  org.springframework.boot.StartupInfoLogger.logStarted(StartupInfoLogger.java:57) - Started Tomcat19Application in 2.287 seconds (process running for 4.057)
23:07:12.012 [http-nio-D:\\http.sock-exec-1] INFO  org.apache.juli.logging.DirectJDKLog.log(DirectJDKLog.java:173) - Initializing Spring DispatcherServlet 'dispatcherServlet'
23:07:12.032 [http-nio-D:\\http.sock-exec-1] INFO  org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:532) - Initializing Servlet 'dispatcherServlet'
23:07:12.033 [http-nio-D:\\http.sock-exec-1] INFO  org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:554) - Completed initialization in 1 ms
23:07:12.062 [http-nio-D:\\http.sock-exec-1] INFO  com.example.tomcat19.action.TestAction.test(TestAction.java:13) - 收到请求id:1

五、springboot外置tomcat配置unixdomainsocket

<Service name="CatalinaLocal">
        <Connector
            protocol="org.apache.coyote.http11.Http11AprProtocol"
            unixDomainSocketPath="/opt/app-name/http.sock" />
        <Engine
            defaultHost="localhost"
            name="Catalina">
            <Host
                name="localhost"
                appBase="webapps"
                unpackWARs="true"
                autoDeploy="false"
                deployIgnore="(?!.*hawtio).*">
                <Valve
                    className="org.apache.catalina.valves.RemoteIpValve" />
            </Host>
        </Engine>
    </Service>

注:这里的配置没有做验证,有兴趣的可以实测一下

六、总结

unix域套接字协议还是非常有用,尤其是涉及性能优化时,有很多大厂的微服务架构都支持unix域套接字协议;

=如果文章对你有帮助,请点赞收藏=====================

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dylan~~~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值