【问题标题】:Comparing NodeJS and Java比较 NodeJS 和 Java
【发布时间】:2019-07-01 08:32:59
【问题描述】:

所以这是我在一个程序文件中的节点 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 的请求,它进入了无限循环,第二个请求显然挂了。

当第二个请求挂起时,我在端口 3000 向 server1 发送第三个请求,server1 也挂起,所以这意味着 nodejs 不支持并发请求处理,即当回调处理程序正在处理特定请求时没有其他请求 即使我的机器是 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 服务并以“infinite”作为输入发送请求时,该请求从另一个浏览器窗口挂起,但我向同一个 Web 服务发送了另一个请求,并以“Aziz”作为输入,它同时返回了良好的结果当带有“无限”输入的窗口挂起时......但是当我评论 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);

【问题讨论】:

    标签: java node.js


    【解决方案1】:

    我无法复制您的确切测试,但我可以在 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 和节点有 2 种不同的方法来处理并发请求,节点选择使用多个 进程,每个进程管理非阻塞 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 的多线程环境更好。

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

    【讨论】:

    • 嗨@minus,感谢您的回答,所以您的回答解决了一个问题,但引发了另一个问题。我看到由于第一次部署时的无限循环,我使用 Thead.sleep(2000);有点搞砸了我的 glassfish,当我第二次部署评论 Thread.sleep(2000);部署确实搞砸了。我可以看到,java 在第一次请求后使用“infinite”参数响应后续请求,所以好的 java 服务器是多线程的,nodejs 不是多线程的,这就是解决了一个问题。但是出现的问题是,如果 nodejs server2 在我的节点代码中阻塞了 server1,那么 nodejs 有什么好处?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-11
    • 2017-11-26
    • 1970-01-01
    • 2014-06-02
    • 1970-01-01
    • 2019-05-17
    • 1970-01-01
    相关资源
    最近更新 更多