Riy*_*lla 6 java nio nonblocking socketchannel
我目前正在使用非阻塞SocketChannel(Java 1.6)充当Redis服务器的客户端.Redis直接在套接字上接受纯文本命令,由CRLF终止并以类似方式响应,这是一个简单的例子:
发送:'PING\r \n'
RECV:'+ PONG\r \n'
Redis还可以返回大量回复(取决于您要求的内容),其中许多\ r \n终止数据部分都作为单个响应的一部分.
我使用标准while(socket.read()> 0){// append bytes}循环从套接字读取字节并将它们重新组装到客户端进行回复.
注意:我没有使用Selector,只是连接到服务器的多个客户端SocketChannel,等待服务发送/接收命令.
我感到困惑的是合同中的SocketChannel.read()在非阻塞模式的方法,特别是如何知道什么时候该服务器进行发送,我有整个邮件.
我有一些方法可以防止返回太快并让服务器有机会回复,但我坚持的一件事是:
基本上,如果我收到至少1个字节并且最终read()返回0然后我知道我已经完成了,或者可能服务器只是忙碌而且可能会丢弃一些如果我等待并继续尝试更多的字节?
如果它仍然可以继续发送字节,即使read()返回0字节(之前成功读取后),那么我不知道如何告诉服务器何时完成与我交谈,事实上我很困惑如何java.io.*样式通信甚至可以知道服务器何时"完成".
你们知道read永远不会返回-1,除非连接已经死了,这些都是标准的长期数据库连接,所以我不会在每次请求时关闭并打开它们.
我知道一个受欢迎的反应(至少对于这些NIO问题)一直在看Grizzly,MINA或Netty - 如果可能的话,我真的想在采用第三方依赖之前了解这一切是如何工作的.
谢谢.
奖金问题:
我原本以为阻塞SocketChannel就是这样的方式,因为我不想让调用者做任何事情,直到我处理他们的命令然后给他们回复.
如果这最终成为一种更好的方法,只要没有足够的字节来填充给定的缓冲区,我就会看到SocketChannel.read()阻塞,我有点困惑...没有逐字节读取所有内容我无法弄清楚这个默认行为是如何实际使用的......我从来不知道从服务器返回的回复的确切大小,所以我对SocketChannel.read()的调用总是阻塞直到超时(此时我终于看到内容位于缓冲区中了.
我不清楚使用阻塞方法的正确方法,因为它总是挂起来读取.
如果即使在 read() 返回 0 字节之后(在之前成功读取之后)它仍然可以继续发送字节,那么我不知道如何判断服务器何时完成与我的对话,事实上我很困惑 java.io.*风格的通信甚至会知道服务器何时“完成”。
阅读并遵循协议:
http://redis.io/topics/protocol
该规范描述了可能的回复类型以及如何识别它们。有些是行终止的,而多行响应包括前缀计数。
回复
Redis 会以不同类型的回复来回复命令。可以从服务器发送的第一个字节开始检查回复的类型:
- 对于单行回复,回复的第一个字节将为“+”
- 对于错误消息,回复的第一个字节将为“-”
- 对于整数,回复的第一个字节将为“:”
- 对于批量回复,回复的第一个字节将为“$”
- 对于多批量回复,回复的第一个字节将为“*”
单行回复
单行回复采用单行字符串的形式,以“+”开头,以“\r\n”结尾。...
...
批量回复
像 LRANGE 这样的命令需要返回多个值(列表中的每个元素都是一个值,而 LRANGE 需要返回多个单个元素)。这是通过使用多个批量写入来完成的,前缀是一个初始行,指示接下来将有多少批量写入。
read() 是否有可能返回字节,然后在后续调用中不返回任何字节,但在另一个后续调用中再次返回一些字节?基本上,如果我收到至少 1 个字节并且最终 read() 返回 0,我可以相信服务器已经完成对我的响应,那么我知道我已经完成了,或者服务器可能只是忙,可能会回传一些信息如果我等待并继续尝试更多字节?
是的,这是可能的。这不仅仅是由于服务器繁忙,网络拥塞和路由中断也会导致数据“暂停”。数据是一个流,可以在流中的任何位置“暂停”,而与应用程序协议无关。
继续将流读入缓冲区。查看第一个字符以确定预期的响应类型。每次成功读取后检查缓冲区,直到缓冲区包含符合规范的完整消息。
我最初认为阻塞 SocketChannel 是解决这个问题的方法,因为我真的不希望调用者做任何事情,直到我处理他们的命令并给他们回复。
我想你是正确的。根据我对规范的快速浏览,阻止读取对该协议不起作用。由于它看起来是基于行的,BufferedReader可能会有所帮助,但您仍然需要知道如何识别响应何时完成。