比较NodeJS和Java

时间:2019-07-01 08:32:59

标签: java node.js

这是单个程序文件中的节点js http服务器,重要的是,我不要在2个单独的进程中的2个服务器下方运行。 它们正在同一进程中运行。

var today = require('./today');
var http = require('http');

var server1 = http.createServer(function(request, response) {
    var now = Date.now();
    var body = "The day of the week is " + today() + " " + now;
    response.writeHead(200, {
        'Content-Length': body.length,
        'Content-Type': 'text/plain'
    });
    response.end(body);
});

server1.listen(3000);


var server2 = http.createServer(function(request, response) {
    var body = "The day of the week is " + today();
    var count = 0;
    while (true) {
        count++;
        if(count == 10000){
            console.log('server2');
            count = 0;
        }
    }   
    response.writeHead(200, {
        'Content-Length': body.length,
        'Content-Type': 'text/plain'
    });
    response.end(body);
});

server2.listen(3002);

运行此节点js程序后,我向server1发送了请求,该请求正常运行,即响应良好。 (200)

因此,然后我发送了对server2的请求,该请求进入了无限循环,第二个请求显然挂起了。

第二个请求挂起时,我也将第三个请求发送到服务器1的端口3000上挂起的server1上,这意味着 Node.js不支持并发请求处理,即当回调处理程序处理特定请求时没有其他请求 即使我的机器是intel i7多核也可以处理... server2中的无限循环应仅阻塞其中一个核, 其余的核心应该已经在server1上处理了后续请求。

因此下面是在glassfish中部署的Java Web服务,我将其开发为无状态EJB,以便ejb容器可以创建多个实例 EJB并同时在此Web服务上处理多个请求

@WebService(serviceName = "NewWebService")
@Stateless()
public class NewWebService {

    @WebMethod(operationName = "hello")
    public String hello(@WebParam(name = "name") String txt) {
        if(txt.equals("infinite")){
            int i = 0;
            while(true){
                i++;
                if(i>10000){
                    try{
                        Thread.sleep(2000);
                        i=0;
                    }catch(Exception e){

                    }
                }
            }
        }
        return "Hello " + txt + " !" + (new Date());
    }
}

当我在glassfish上的Web服务上方部署并以“无限”作为输入发送请求时,该请求从另一个浏览器窗口中挂起,但我以“ Aziz”作为输入将另一个请求发送至相同的Web服务,它同时返回了良好的结果当带有“无限”输入的窗口挂起时...但是当我评论Thread.sleep(2000)时;上面的两个请求均如此挂起,因此JAVA并不支持同时发出的请求。

结论:至少对于我创建的Web服务器而言,java和node js的行为方式相同,而无视nodejs具有回调的概念,而回调的概念很神奇……所以我的问题是为什么我们特别调用nodejs作为单线程,在此特定问题中,java的行为方式相同。

注意:我不是在谈论您可以使用诸如crypto.pbkdf2sync crypto.pbkdf2之类的异步功能进行的nodejs异步调用。####################### ########

在@minus的首次更新后更新: 非常感谢@minus,您说: “如果您有99%的响应时间是由于IO造成的,那么nodejs将可以正常工作,甚至比Java的多线程环境还要好。”

因此,我更改了节点代码,更改了无限调用以读取主要基于IO操作的文件,但server2仍然阻止了server1。如果我们将节点的行为与java的行为进行比较,那么java将同时处理节点在长时间运行的回调中被阻塞的情况……该回调是否主要执行IO无关紧要。下面是我更改的节点代码:

var http = require('http');

var server1 = http.createServer(function(request, response) {
    var now = Date.now();
    var body = "Time " + now;
    response.writeHead(200, {
        'Content-Length': body.length,
        'Content-Type': 'text/plain'
    });
    response.end(body);
});

server1.listen(3000);

var fs = require('fs');

var server2 = http.createServer(function(request, response) {
    while (true) {
        fs.readFile('stuff.txt', 'utf8', function(err, contents) {
            if(err){
               console.log(err);
            }else{
               console.log(contents);
            }
        }); 

        var contents = fs.readFileSync('stuff.txt', 'utf8');
        console.log(contents);
    }

    var now = Date.now();
    var body = "Time " + now;

    response.writeHead(200, {
        'Content-Length': body.length,
        'Content-Type': 'text/plain'
    });
    response.end(body);
});

server2.listen(3002);

1 个答案:

答案 0 :(得分:0)

我无法复制您的确切测试,但是我可以使用Java进行最少的配置来进行类似的测试。

我使用maven来管理依赖项。

这是运行示例的最小Pom。

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>it.minus</groupId>
    <artifactId>thread.demo.ws</artifactId>
    <version>0.0.1</version>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <jersey.version>2.29</jersey.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-grizzly2-http</artifactId>
            <version>${jersey.version}</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.inject</groupId>
            <artifactId>jersey-hk2</artifactId>
            <version>${jersey.version}</version>
        </dependency>
    </dependencies>
</project>

这是运行服务器的类:

package thread.demo.ws;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.server.ResourceConfig;

public class Run {

    public static void main(String[] args) throws URISyntaxException {
        String BASE_URI = "http://0.0.0.0:3000/ws/";
        final ResourceConfig rc = new ResourceConfig().packages("thread.demo.ws");
        HttpServer server = GrizzlyHttpServerFactory.createHttpServer(new URI(BASE_URI), rc, false);
        try {
            server.start();
            System.in.read();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            server.shutdownNow();
            System.exit(0);
        }
    }
}

这是一个资源:

package thread.demo.ws;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;

@Path("echo")
public class Echo {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String get(@QueryParam("msg") String echoMsg) {
        if("LOCK".equalsIgnoreCase(echoMsg)) {
            while(true) {
            }
        }
        String reply = echoMsg == null ? "PING" : echoMsg;
        return " - " + reply + " - ";
    }
}

将触发服务的请求是:

http://localhost:3000/ws/echo?msg=hello

将永远锁定胎面的请求是:

http://localhost:3000/ws/echo?msg=LOCK

您可以进行测试,并且即使一个(或多个)响应请求的线程陷入无限循环,Java也会继续处理请求。

最终,您将锁定太多线程,系统将不再响应。

Java是多线程的,因此您 可以同时处理多个请求。

测试未提供正确结果的原因可能取决于许多原因,最合理的原因是,通过在线程中造成无限循环,您可能会阻止正常关闭应用程序服务器,也可能会阻止正确的运行重新部署您的应用程序。

Java和节点有两种处理并发请求的方法,节点选择使用多个进程,每个进程管理无阻塞IO处理,而java(主要)则使用运行多个线程的单个进程,具体取决于许多不同的因素中,一个因素可能比另一个因素表现更好。

还有许多用Java编写的应用程序服务器,它们使用NIO(非阻塞IO)来减少使用的线程数,因此在Java中,您也可以使用相同的Node范式。

评论后更新

您通过阻塞server1来滥用nodejs。

这是一个有用的实验,它可以了解阻塞代码的任何部分有多糟糕,但实际上您绝对不要阻塞js。

您的服务器将或多或少地像这样实现:

function(request, response) {
    request.json()
        .then(data=>callService(data))
        .catch(err=> sendError(err,response))
        .then(someEntity => buildResponseWithEntity(someEntity,response ) )
        .catch(e => logError(e));
}

函数主体将立即返回,而过程的每个步骤(.then和.catch)将最终被处理,每个步骤也永远不会阻塞以等待任何IO。

如果您99%的响应时间是由于IO造成的,那么nodejs将可以正常工作,甚至比Java多线程环境还要好。

要详细解释这一点,将需要太多时间。