• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

关于java:四种方法实现http服务

java 搞代码 4年前 (2022-02-19) 31次浏览 已收录 0个评论
文章目录[隐藏]

前言:对于微服务来说,如果咱们要实现一个web服务,
大部分人可能间接用springboot的spring-boot-starter-web了。
咱们晓得spring-boot-starter-web默认实现是tomcat,当然你也能够抉择其余服务器类型,比方Jetty、Undertow等。
然而如果是一个非springboot我的项目,该如何实现呢?

这里介绍了下四种实现形式,基于Tomcat、Jetty、JdkHttp、Netty实现内嵌web容器。

Tomcat

依赖的maven坐标:

<code class="xml">        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <exclusions>
                <!--避免注解抵触,排除tomcat自带注解,springboot也是这样设置的-->
                <exclusion>
                    <artifactId>tomcat-annotations-api</artifactId>
                    <groupId>org.apache.tomcat</groupId>
                </exclusion>
            </exclusions>
        </dependency>

首先看下初始化启动的代码:

<code class="java">Tomcat tomcatServer = new Tomcat();
//静默形式启动
tomcatServer.setSilent(true);

tomcatServer.setPort(8080);
//是否设置主动部署
tomcatServer.getHost().setAutoDeploy(false);
//创立上下文,拿到上下文后就能够设置整个拜访地址了
StandardContext standardContext = new StandardContext();
standardContext.setPath(CONTEX_PATH);
//监听上下文
standardContext.addLifecycleListener(new Tomcat.FixContextListener());
// tomcat容器增加standardContext 增加整个context
tomcatServer.getHost().addChild(standardContext);
// 创立servlet   servlet的名字叫IndexServlet
tomcatServer.addServlet(CONTEX_PATH, SERVLET_NAME, new JdkSimpleDispatchServlet());
// 增加servleturl映射
standardContext.addServletMappingDecoded("/*", SERVLET_NAME);
try {
    tomcatServer.start();
    server = tomcatServer;

} catch (Exception e) {
}

下面tomcat在注册servlet的时候,自定义了一个Servlet,而后映射了/*的申请。能够查看下JdkSimpleDispatchServlet这个类,代码如下:

<code class="java">@Slf4j
public class JdkSimpleDispatchServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        log.info("path:{}, clientIp:{}", req.getRequestURL(), req.getRemoteHost());
        PrintWriter writer = resp.getWriter();
        writer.print("this is index");
        writer.close();

    }
}

Jetty

Jetty和tomcat十分相似,也是调用start办法启动。

maven依赖如下:

<code class="xml">        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-server</artifactId>
        </dependency>
        <!--增加servlet模块-->
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-servlet</artifactId>
        </dependency>

jetty服务初始化代码更简略,如下:

<code class="java">Server server = new Server(NetworkUtil.getHealthServerPort());

// ServletHandler是一种简略的创立上下文处理程序的简略办法,该上下文处理程序由Servlet实例反对。而后,须要将该处理程序注册到Server对象。
ServletHandler handler = new ServletHandler();
server.setHandler(handler);

// 这是原始Servlet,而不是已配置的Servlet,JdkSimpleDispatchServlet和tomcat的相似
handler.addServletWithMapping(JdkSimpleDispatchServlet.class, "/*");
try {
    // Start things up!
    server.start();
    jetty = server;

} catch (Exception e) {
}

Netty

说了Tomcat和Jetty,咱们再看下Netty,之前所在的一家公司就是基于Netty封装了Web服务,Netty对Web反对也比较完善,默认基于NIO的多路复用IO模型反对单机上万的吞肚量。

看下pom依赖:

<code class="xml">       <dependency>
           <groupId>io.netty</groupId>
           <artifactId>netty-all</artifactId>
       </dependency>

启动形式:

<code class="java">public class NettyHttpServerHealthCheckServer implements IHealthCheckServer {
    private static ExecutorService Pool = Executors.newSingleThreadExecutor(
            new NamedThreadFactory("Netty-HealthCheck-Pool", true));
    ServerBootstrap bootstrap = new ServerBootstrap();
    // boss线程,只需一个
    EventLoopGroup boss = new NioEventLoopGroup();
    // work线程
    EventLoopGroup work = new NioEventLoopGroup();

    @Override
    public void start() {
        try {
            // 因为监听服务是阻塞的,须要线程池异步监听
            Pool.execute(() -> {
                try {
                    // 配置channel、handle
                    bootstrap.group(boss, work)
//                            .handler(new LoggingHandler(LogLevel.INFO))
                            .channel(NioServerSocketChannel.class)
                            // HttpServerInitializer即http编码解码和解决配置器
                            .childHandler(new HttpServerInitializer());
                    ChannelFuture f = bootstrap.bind(new InetSocketAddress(NetworkUtil.getHealthServerPort())).sync();
                    // 阻塞监听
                    f.channel().closeFuture().sync();

                } catch (Exception e) {
                }
            });
        } catch (Exception e) {
            return;
        }
    }


    @Override
    public void stop() {
        boss.shutdownGracefully();
        work.shutdownGracefully();
    }

    class HttpServerInitializer extends ChannelInitializer<SocketChannel> {

        @Override
        protected void initChannel(SocketChannel channel) throws Exception {
            ChannelPipeline pipeline = channel.pipeline();
            pipeline.addLast(new HttpServerCodec());// http 编解码
            pipeline.addLast("httpAggregator", new HttpObjectAggregator(512 * 1024)); // http 音讯聚合器                                                                     512*1024为接管的最大contentlength
            pipeline.addLast(new HttpRequestHandler());// 申请处理器
        }
    }

    class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

        @Override
        public void channelReadComplete(ChannelHandlerContext ctx) {
            ctx.flush();
        }

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
            /**
             * 100 Continue
             * 是这样的一种状况:HTTP客户端程序有一个实体的主体局部要发送给服务器,但心愿在发送之前查看下服务器是否会
             * 承受这个实体,所以在发送实体之前先发送了一个携带100
             * Continue的Expect申请首部的申请。服务器在收到这样的申请后,应该用 100 Continue或一条错误码来进行响应。
             */
            if (is100ContinueExpected(req)) {
                ctx.write(new DefaultFullHttpResponse(
                        HttpVersion.HTTP_1_1,
                        HttpResponseStatus.CONTINUE));
            }
            // 获取申请的uri
            String path = req.uri();
    
            // 响应申请
            fireResponse(ctx, HttpResponseStatus.OK,
                    "hello", "text/html; charset=utf-8");

        }

        private void fireResponse(ChannelHandlerContext ctx, HttpResponseStatus httpResponseStatus, String resp, String contentType) {
            FullHttpResponse responseDown = new DefaultFullHttpResponse(
                    HttpVersion.HTTP_1_1,
                    httpResponseStatus,
                    Unpooled.copiedBuffer(resp, Charset.defaultCharset()));
            responseDown.headers().set(HttpHeaderNames.CONTENT_TYPE, contentType);
            ctx.writeAndFlush(responseDown).addListener(ChannelFutureListener.CLOSE);
        }
    }

}

从下面代码能够得悉,Netty默认就提供了http编解码和协定的实现,十分不便。

JdkHttp

最好介绍下不依赖第三方实现,应用JDK8内置的Http Server实现。

HttpServer类

外围类HttpServer,HttpServer是属于rt包的类,须要下载rt包的源码,配置到IDEA。或者间接应用openjdk,也能够查看到源码。

rt包能够下载OpenJDK的源码,https://download.java.net/openjdk/jdk8/promoted/b132/openjdk-8-src-b132-03_mar_2014.zip

HttpServer源码:

<code class="java">package com.sun.net.httpserver;
@Exported
public abstract class HttpServer {
    protected HttpServer() {
    }
    public static HttpServer create() throws IOException {
        return create((InetSocketAddress)null, 0);
    }
    public static HttpServer create(InetSocketAddress var0, int var1) throws IOException {
        HttpServerProvider var2 = HttpServerProvider.provider();
        return var2.createHttpServer(var0, var1);
    }
    public abstract void bind(InetSocketAddress var1, int var2) throws IOException;
    public abstract void start();
    public abstract void setExecutor(Executor var1);
    public abstract Executor getExecutor();
    public abstract void stop(int var1);
    public abstract HttpContext createContext(String var1, HttpHandler var2);
    public abstract HttpContext createContext(String var1);
    public abstract void removeContext(String var1) throws IllegalArgu<a style="color:transparent">来源gao($daima.com搞@代@#码网</a>mentException;
    public abstract void removeContext(HttpContext var1);
    public abstract InetSocketAddress getAddress();
}

初始化Http服务:

<code class="java">public class JDKHttpServerHealthCheckServer implements IHealthCheckServer {
    HttpServer server;

    @Override
    public void start() {
        try {
            // 初始化监听
            server = HttpServer.create(new InetSocketAddress(8080), 100);
            // 注册http申请解决类
            server.createContext("/", new JdkHttpHandler());
            // 启动服务
            server.start();
        } catch (Exception e) {
            return;
        }
    }

    @Override
    public void stop() {
        if (server != null) {
            server.stop(0);
        }
    }

    static class JdkHttpHandler implements HttpHandler {
        @Override
        public void handle(HttpExchange httpExchange) throws IOException {
            String path = httpExchange.getRequestURI() == null ? "/" : httpExchange.getRequestURI().getPath();

            try {
                CharArrayWriter charArrayWriter = new CharArrayWriter();
                charArrayWriter.write("hello");
                httpExchange.getResponseHeaders().add("Content-Type", "text/plain; charset=utf-8");
                // 这里必须指定字节大小,因为默认是固定大小的编码解码实现
                httpExchange.sendResponseHeaders(200, charArrayWriter.size());
                outputStream.write(charArrayWriter.toString().getBytes());
                 
            } catch (Exception e) {
                httpExchange.sendResponseHeaders(500, 0);
            }
        }
    }
}

从下面四个实现来看,对http servlet标准反对比较完善的有Jetty、Tomcat。性能高的是Netty,实现最简略的是JDK默认HttpServer。

本文由猿必过 YBG 公布


搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:关于java:四种方法实现http服务

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址