目的

java 实战真实的例子,而不是侃侃而谈的理论。

netty 入门例子

maven

  [xml]
1
2
3
4
5
<dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.108.Final</version> </dependency>

服务端

  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package com.github.houbb.netty.learn; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import java.nio.charset.StandardCharsets; public class EchoServer { public static void main(String[] args) throws Exception { // 1. 创建线程组(Boss 负责连接,Worker 负责 I/O) EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { // 2. 配置服务端引导类 ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) { ch.pipeline().addLast(new EchoServerHandler()); } }); // 3. 绑定端口并启动服务 ChannelFuture f = b.bind(8080).sync(); f.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } } // 自定义处理器 class EchoServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { ByteBuf in = (ByteBuf) msg; System.out.println("收到客户端消息: " + in.toString(StandardCharsets.UTF_8)); ctx.writeAndFlush(Unpooled.copiedBuffer("Hi Client!", StandardCharsets.UTF_8)); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); ctx.close(); } }

客户端

  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package com.github.houbb.netty.learn; import io.netty.bootstrap.Bootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import java.nio.charset.StandardCharsets; public class EchoClient { public static void main(String[] args) throws Exception { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) { ch.pipeline().addLast(new EchoClientHandler()); } }); // 连接服务端 ChannelFuture f = b.connect("127.0.0.1", 8080).sync(); f.channel().closeFuture().sync(); } finally { group.shutdownGracefully(); } } } // 自定义处理器 class EchoClientHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) { ctx.writeAndFlush(Unpooled.copiedBuffer("Hello Server!", StandardCharsets.UTF_8)); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { ByteBuf in = (ByteBuf) msg; System.out.println("收到服务端响应: " + in.toString(StandardCharsets.UTF_8)); ctx.close(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); ctx.close(); } }

测试

首先启动 EchoServer、然后启动 EchoClient

预期结果:

服务端输出:收到客户端消息: Hello Server!

客户端输出:收到服务端响应: Hi Client!

sofatbolt 例子

maven

  [xml]
1
2
3
4
5
<dependency> <groupId>com.alipay.sofa</groupId> <artifactId>bolt</artifactId> <version>1.6.11</version> </dependency>

服务端

  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.github.houbb.sofabolt.learn; import com.alipay.remoting.BizContext; import com.alipay.remoting.rpc.RpcServer; import com.alipay.remoting.rpc.protocol.SyncUserProcessor; public class SofaBoltServer { public static void main(String[] args) { // 1. 创建 RPC 服务端(自动管理线程池) RpcServer rpcServer = new RpcServer(12200); // 2. 注册用户处理器(处理字符串消息) rpcServer.registerUserProcessor(new SyncUserProcessor<String>() { @Override public Object handleRequest(BizContext bizContext, String s) throws Exception { System.out.println("收到客户端消息: " + s); return "Hi Client!"; // 直接返回响应 } @Override public String interest() { return String.class.getName(); // 声明处理的消息类型 } }); // 3. 启动服务 rpcServer.start(); System.out.println("Server started on port 12200"); } }

客户端

  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.github.houbb.sofabolt.learn; import com.alipay.remoting.rpc.RpcClient; public class SofaBoltClient { public static void main(String[] args) { // 1. 创建 RPC 客户端(自动管理连接池) RpcClient rpcClient = new RpcClient(); rpcClient.init(); try { // 2. 同步调用(指定地址、请求体、超时时间) String response = (String) rpcClient.invokeSync( "127.0.0.1:12200", "Hello Server!", 3000 // 超时 3 秒 ); System.out.println("收到服务端响应: " + response); } catch (Exception e) { e.printStackTrace(); } finally { rpcClient.shutdown(); } } }

测试验证

服务端启动,客户端启动。

服务端日志:

  [plaintext]
1
2
3
4
5
6
7
8
9
10
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. Sofa-Middleware-Log:WARN [com.alipay.remoting] No log util is usable, Default app logger will be used. Server started on port 12200 Sofa-Middleware-Log:WARN [sofa-common-tools] No log util is usable, Default app logger will be used. 三月 26, 2025 8:09:25 下午 com.caucho.hessian.io.AbstractStringBuilderDeserializer <clinit> 警告: coder field not found or not accessible, will skip coder check, error is coder Sofa-Middleware-Log:WARN [com.alipay.sofa.hessian] No log util is usable, Default app logger will be used. 收到客户端消息: Hello Server!

客户端日志

  [plaintext]
1
2
3
4
5
6
7
8
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. Sofa-Middleware-Log:WARN [com.alipay.remoting] No log util is usable, Default app logger will be used. 三月 26, 2025 8:09:24 下午 com.caucho.hessian.io.AbstractStringBuilderDeserializer <clinit> 警告: coder field not found or not accessible, will skip coder check, error is coder Sofa-Middleware-Log:WARN [com.alipay.sofa.hessian] No log util is usable, Default app logger will be used. 收到服务端响应: Hi Client!

参考资料

https://www.sofastack.tech/projects/sofa-bolt/sofa-bolt-handbook/