Netty入门(一)-3.x User Guide

一、简单的DiscardServer

1.当然,首先要引入Netty 3.x的jar包

2.最简单的官方用户指引文档的例子:DiscardServer

  • DiscardServerHandler.java
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;

public class DiscardServerHandler extends SimpleChannelHandler {
	/**
	 * DiscardServerHandler extends SimpleChannelHandler, which is an
	 * implementation of ChannelHandler. SimpleChannelHandler provides various
	 * event handler methods that you can override. For now, it is just enough
	 * to extend SimpleChannelHandler rather than to implement the handler
	 * interfaces by yourself.
	 * 
	 */

	@Override
	public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
			throws Exception {
		/**
		 * 
		 We override the messageReceived event handler method here. This
		 * method is called with a MessageEvent, which contains the received
		 * data, whenever new data is received from a client. In this example,
		 * we ignore the received data by doing nothing to implement the DISCARD
		 * protocol.
		 */

		/**
		 * 
		 It is safe to assume the message type in socket transports is always
		 * ChannelBuffer. ChannelBuffer is a fundamental data structure which
		 * stores a sequence of bytes in Netty. It's similar to NIO ByteBuffer,
		 * but it is easier to use and more flexible. For example, Netty allows
		 * you to create a composite ChannelBuffer which combines multiple
		 * ChannelBuffers reducing the number of unnecessary memory copy.
		 */

		ChannelBuffer buf = (ChannelBuffer) e.getMessage();
		while (buf.readable()) {
			System.out.println((char) buf.readByte());
			System.out.flush();
		}

	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
			throws Exception {
		/**
		 * 
		 exceptionCaught event handler method is called with an ExceptionEvent
		 * when an exception was raised by Netty due to I/O error or by a
		 * handler implementation due to the exception thrown while processing
		 * events. In most cases, the caught exception should be logged and its
		 * associated channel should be closed here, although the implementation
		 * of this method can be different depending on what you want to do to
		 * deal with an exceptional situation. For example, you might want to
		 * send a response message with an error code before closing the
		 * connection.
		 */
		Channel ch = e.getChannel();
		ch.close();

	}

}
  • DiscardServer.java
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;

import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;

public class DiscardServer {
	public static void main(String[] args) throws Exception {
		/**
		 * 
		 * ChannelFactory is a factory which creates and manages Channels and
		 * its related resources. It processes all I/O requests and performs I/O
		 * to generate ChannelEvents. Netty provides various ChannelFactory
		 * implementations. We are implementing a server-side application in
		 * this example, and therefore NioServerSocketChannelFactory was used.
		 * Another thing to note is that it does not create I/O threads by
		 * itself. It is supposed to acquire threads from the thread pool you
		 * specified in the constructor, and it gives you more control over how
		 * threads should be managed in the environment where your application
		 * runs, such as an application server with a security manager.
		 */
		ChannelFactory factory = new NioServerSocketChannelFactory(
				Executors.newCachedThreadPool(),
				Executors.newCachedThreadPool());
		/**
		 * ServerBootstrap is a helper class that sets up a server. You can set
		 * up the server using a Channel directly. However, please note that
		 * this is a tedious process and you do not need to do that in most
		 * cases.
		 */
		ServerBootstrap bootstrap = new ServerBootstrap(factory);
		/**
		 * Here, we configure the ChannelPipelineFactory. Whenever a new
		 * connection is accepted by the server, a new ChannelPipeline will be
		 * created by the specified ChannelPipelineFactory. The new pipeline
		 * contains the DiscardServerHandler. As the application gets
		 * complicated, it is likely that you will add more handlers to the
		 * pipeline and extract this anonymous class into a top level class
		 * eventually.
		 */
		bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
			public ChannelPipeline getPipeline() {
				return Channels.pipeline(new DiscardServerHandler());
			}
		});
		/**
		 * 
		 You can also set the parameters which are specific to the Channel
		 * implementation. We are writing a TCP/IP server, so we are allowed to
		 * set the socket options such as tcpNoDelay and keepAlive. Please note
		 * that the "child." prefix was added to all options. It means the
		 * options will be applied to the accepted Channels instead of the
		 * options of the ServerSocketChannel. You could do the following to set
		 * the options of the ServerSocketChannel:
		 * 
		 * bootstrap.setOption("reuseAddress", true);
		 */
		bootstrap.setOption("child.tcpNoDelay", true);
		bootstrap.setOption("child.keepAlive", true);

		bootstrap.bind(new InetSocketAddress(8080));
	}

}

3.测试:telnet localhost 8080


二、Echo Server(将请求的message回显)

1.修改DiscardServerHandler类中的messageReceived方法:

@Override
	public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
			throws Exception {
		/**
		 * 
		 We override the messageReceived event handler method here. This
		 * method is called with a MessageEvent, which contains the received
		 * data, whenever new data is received from a client. 
		 */

		/**
		 * A ChannelEvent object has a reference to its associated Channel.
		 * Here, the returned Channel represents the connection which received
		 * the MessageEvent. We can get the Channel and call the write method to
		 * write something back to the remote peer.
		 */
		Channel ch = e.getChannel();
		ch.write(e.getMessage());

	}

2.测试:telnet localhost 8080

io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2Exception: Unexpected HTTP/1.x request: POST /user/login at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2Exception.connectionError(Http2Exception.java:109) ~[grpc-netty-shaded-1.54.0.jar:1.54.0] at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler$PrefaceDecoder.readClientPrefaceString(Http2ConnectionHandler.java:317) ~[grpc-netty-shaded-1.54.0.jar:1.54.0] at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler$PrefaceDecoder.decode(Http2ConnectionHandler.java:247) ~[grpc-netty-shaded-1.54.0.jar:1.54.0] at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler.decode(Http2ConnectionHandler.java:453) [grpc-netty-shaded-1.54.0.jar:1.54.0] at io.grpc.netty.shaded.io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:529) [grpc-netty-shaded-1.54.0.jar:1.54.0] at io.grpc.netty.shaded.io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:468) [grpc-netty-shaded-1.54.0.jar:1.54.0] at io.grpc.netty.shaded.io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:290) [grpc-netty-shaded-1.54.0.jar:1.54.0] at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) [grpc-netty-shaded-1.54.0.jar:1.54.0] at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) [grpc-netty-shaded-1.54.0.jar:1.54.0] at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) [grpc-netty-shaded-1.54.0.jar:1.54.0] at io.grpc.netty.shaded.io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) [grpc-netty-shaded-1.54.0.jar:1.54.0] at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440) [grpc-netty-shaded-1.54.0.jar:1.54.0] at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) [grpc-netty-shaded-1.54.0.jar:1.54.0] at io.grpc.netty.shaded.io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) [grpc-netty-shaded-1.54.0.jar:1.54.0] at io.grpc.netty.shaded.io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166) [grpc-netty-shaded-1.54.0.jar:1.54.0] at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788) [grpc-netty-shaded-1.54.0.jar:1.54.0] at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724) [grpc-netty-shaded-1.54.0.jar:1.54.0] at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650) [grpc-netty-shaded-1.54.0.jar:1.54.0] at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562) [grpc-netty-shaded-1.54.0.jar:1.54.0] at io.grpc.netty.shaded.io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) [grpc-netty-shaded-1.54.0.jar:1.54.0] at io.grpc.netty.shaded.io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) [grpc-netty-shaded-1.54.0.jar:1.54.0] at io.grpc.netty.shaded.io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) [grpc-netty-shaded-1.54.0.jar:1.54.0] at java.lang.Thread.run(Thread.java:748) [na:1.8.0_221]
最新发布
08-26
<think>好的,我现在需要解决用户遇到的gRPC错误:“io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2Exception: Unexpected HTTP/1.x request: POST /user/login”。首先,我需要理解这个错误的原因。 首先看错误信息,核心是“Unexpected HTTP/1.x request”,这说明gRPC服务器期望接收HTTP/2请求,但实际收到了HTTP/1.x的请求。gRPC默认使用HTTP/2协议,而用户可能用HTTP/1.x的客户端(比如普通的REST客户端)访问了gRPC服务器的端口,导致协议不匹配。 接下来,用户的问题可能是在同个端口上同时运行了gRPC服务和REST服务,或者客户端错误地用HTTP/1.x的方式调用了gRPC接口。例如,用户可能尝试用POST请求访问gRPC服务器的某个路径(如/user/login),而gRPC服务器只处理HTTP/2的gRPC请求,不识别HTTP/1.x的请求,因此抛出异常。 可能的原因包括: 1. 客户端错误地使用HTTP/1.x协议(如curl、Postman的普通请求)调用了gRPC服务端的端口,而gRPC服务端只监听HTTP/2请求。 2. 服务端配置错误,比如在同个端口同时启用了HTTP/1.x(如REST)和HTTP/2(如gRPC),但未正确配置协议协商(如ALPN)。 3. 如果使用Netty作为传输层,可能未正确配置HTTP/2的支持,导致无法处理混合协议。 解决方法需要根据具体场景来调整: **情况1:客户端误用HTTP/1.x调用gRPC服务** gRPC客户端需要使用gRPC库(如gRPC的Java客户端、gRPC的Python客户端等),这些客户端会自动使用HTTP/2协议。如果用户用普通的HTTP客户端(如Postman发送POST到/user/login),会触发此错误。此时需要确认客户端是否使用正确的gRPC客户端库,而不是REST客户端。 **情况2:服务端同端口混合HTTP/1.x和HTTP/2** 如果需要在同个端口同时提供REST(HTTP/1.x)和gRPC(HTTP/2)服务,需要配置协议协商(ALPN,Application-Layer Protocol Negotiation)。Netty支持通过`Http2FrameCodec`和`HttpServerCodec`结合`ApplicationProtocolNegotiationHandler`来处理不同协议的请求。例如,对于HTTP/1.x请求路由到REST控制器,HTTP/2请求路由到gRPC服务。 **情况3:服务端未正确配置HTTP/2** 检查gRPC服务器的启动配置,确保使用了支持HTTP/2的传输层。例如,在Netty中,gRPC默认应该使用HTTP/2,但如果有其他拦截器或处理器干扰,可能导致协议协商失败。需要确认服务器管道(ChannelPipeline)中是否正确添加了HTTP/2相关的处理器。 **示例解决步骤(以Spring Boot集成gRPC为例):** 如果用户希望在同个端口同时支持REST和gRPC,需要配置ALPN。例如,使用`gRPC Spring Boot Starter`时,可能需要额外配置,或者手动设置Netty的ChannelPipeline。但更常见的做法是分开端口:gRPC使用个端口(如50051),REST使用另个端口(如8080),避免协议冲突。 **验证方法:** 使用gRPC客户端(如bloomrpc、grpcurl)调用服务,确认是否正常。如果正常,说明问题出在客户端使用了错误的协议;如果仍然报错,需要检查服务端配置。 总结,该错误的根本原因是HTTP版本不匹配,解决关键是确保客户端使用HTTP/2协议访问gRPC服务,或服务端正确配置协议协商以支持混合协议。 </think>这个错误的核心原因是 **gRPC 服务器期望接收 HTTP/2 请求,但实际收到了 HTTP/1.x 请求**。gRPC 基于 HTTP/2 协议设计,而客户端可能误将 HTTP/1.x(如 REST 风格的 POST 请求)发送到了 gRPC 服务的端口,导致协议不兼容。 ### **具体原因分析** gRPC 服务器(如基于 Netty 的实现)默认监听 HTTP/2 协议。当客户端使用 HTTP/1.x 协议(例如用 Postman 发送普通 POST 请求到 `/user/login`)访问时,服务器会检测到协议不匹配,并抛出 `Unexpected HTTP/1.x request` 异常。 ### **解决方法** 根据具体场景选择以下方案: #### **方案 1:确保客户端使用 gRPC 协议调用** gRPC 必须通过 **gRPC 客户端库**(如 Java 的 `io.grpc`、Python 的 `grpcio` 等)调用,这些库会自动使用 HTTP/2 协议。 - **错误示例**:用 `curl` 或 Postman 发送 `POST /user/login`(HTTP/1.x)。 - **正确示例**:使用 gRPC 客户端(如 `grpcurl` 工具或自定义 gRPC 客户端)调用服务。 #### **方案 2:同端口混合支持 HTTP/1.x(REST)和 HTTP/2(gRPC)** 若需在同端口同时提供 REST(HTTP/1.x)和 gRPC(HTTP/2)服务,需通过 **ALPN(应用层协议协商)** 配置协议兼容。具体步骤如下(以 Netty 为例): 1. **添加 ALPN 依赖**(针对 Java 8 及以下,Java 9+ 内置支持): ```xml <!-- 仅 Java 8 需要,用于 ALPN 支持 --> <dependency> <groupId>io.netty</groupId> <artifactId>netty-tcnative-boringssl-static</artifactId> <version>2.0.54.Final</version> </dependency> ``` 2. **配置 Netty 服务器支持协议协商**: 在启动 gRPC 服务器时,通过 `NettyServerBuilder` 自定义管道,添加 `ApplicationProtocolNegotiationHandler` 处理 HTTP/1.x 和 HTTP/2 的协商: ```java Server server = NettyServerBuilder.forPort(8080) .addService(new UserGrpcService()) .channelType(NioServerSocketChannel.class) .sslContext(/* 可选:SSL 上下文(若启用 HTTPS) */) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) { ChannelPipeline p = ch.pipeline(); // 支持 HTTP/1.x 和 HTTP/2 协商 p.addLast(new ApplicationProtocolNegotiationHandler("h2") { @Override protected void configurePipeline(ChannelHandlerContext ctx, String protocol) { if ("h2".equals(protocol)) { // HTTP/2 协议:添加 gRPC 处理器 p.addLast(GrpcSslContexts.selectApplicationProtocolHandler(ctx.channel())); } else if ("http/1.1".equals(protocol)) { // HTTP/1.x 协议:添加 REST 处理器(如 Spring MVC) p.addLast(new HttpServerCodec()); p.addLast(new HttpObjectAggregator(1024 * 1024)); p.addLast(new RestRequestHandler()); // 自定义 REST 处理逻辑 } else { throw new IllegalStateException("Unsupported protocol: " + protocol); } } }); } }) .build(); server.start(); ``` #### **方案 3:分离 REST 和 gRPC 端口** 更简单的方式是为 REST 和 gRPC 分配不同端口(如 REST 用 8080,gRPC 用 50051),避免协议冲突。这是最常见的生产环境配置方式。 ### **验证方法** - 使用 `grpcurl` 工具调用 gRPC 服务(验证 HTTP/2 正常): ```bash grpcurl -plaintext localhost:50051 com.example.user.UserService/GetUser ``` - 使用 Postman 发送 HTTP/1.x 请求到 REST 端口(验证 REST 正常)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值