这是单个程序文件中的节点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);
答案 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多线程环境还要好。
要详细解释这一点,将需要太多时间。