技术难点:TCP粘包拆包问题,解决思路:客户端在往服务端发送消息时(编码),先要将消息的长度(一个int)写入与channel关联的buffer中。客户端在接受消息时,先判断消息的长度是不是够一个int,如果不够,等待下一次事件触发,如果够,判断可读的消息的长度是不是等于客户端传过来的长度,如果不够,继续等待下一次事件触发。自定义编解码器代码如下:
自定义编码器:
package codec;
import com.alibaba.fastjson.JSON;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import java.util.Objects;
public class RpcEncoder extends MessageToByteEncoder {
private Class> clazz;
public RpcEncoder(Class> clazz) {
this.clazz = clazz;
}
/**
* 自定义编码方法实现 java--》byte
* @param channelHandlerContext
* @param o
* @param byteBuf
* @throws Exception
*/
@Override
protected void encode(ChannelHandlerContext channelHandlerContext, Object o, ByteBuf byteBuf) throws Exception {
if(Objects.nonNull(clazz)&&clazz.isInstance(o)){
System.out.println("编码方法被调用..encode");
byte[] bytes = JSON.toJSONBytes(o);
int length = bytes.length;
byteBuf.writeInt(length);
byteBuf.writeBytes(bytes);
}
}
}
自定义解码器:
package codec;
import com.alibaba.fastjson.JSON;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import java.util.List;
public class RpcDecoder extends ByteToMessageDecoder {
private Class> clazz;
public RpcDecoder(Class> clazz) {
this.clazz = clazz;
}
/**
* 自定义解码器解码方法实现
* @param channelHandlerContext
* @param byteBuf
* @param list
* @throws Exception
*/
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List
客户端代码(包括获取动态代理对象与初始化客户端):
package rpc.consumer;
import codec.RpcDecoder;
import codec.RpcEncoder;
import rpc.handler.ClientHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import protocol.RpcRequest;
import protocol.RpcResponse;
import java.lang.reflect.Proxy;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class NettyClient {
public static ExecutorService executor = Executors.newFixedThreadPool(4);
public static boolean clientOk = false;
private static ClientHandler clientHandler;
public static Object getBean(final Class> serviceClass){
/**
* 该方法返回目标对象的代理对象
*/
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class>[]{serviceClass}, (proxy, method, args) -> {
System.out.println("客户端invoke方法执行..hello");
if(null==clientHandler){
initClient();
}
RpcRequest rpcRequest = new RpcRequest();
rpcRequest.setRequestId(UUID.randomUUID().toString());
rpcRequest.setInterfaceName(method.getDeclaringClass().getName());
rpcRequest.setMethodName(method.getName());
rpcRequest.setParamTypes(method.getParameterTypes());
rpcRequest.setPatams(args);
clientHandler.setRpcRequest(rpcRequest);
return executor.submit(clientHandler).get();
});
}
/**
* 该方法初始化netty客户端
*/
public static void initClient(){
clientHandler= new ClientHandler();
NioEventLoopGroup executors = new NioEventLoopGroup();
try{
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(executors)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY,true)
.handler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new RpcEncoder(RpcRequest.class));
pipeline.addLast(new RpcDecoder(RpcResponse.class));
pipeline.addLast(clientHandler);
}
});
System.out.println("客户端启动完毕");
clientOk=true;
bootstrap.connect("127.0.0.1", 9999).sync();
}catch (Exception e){
e.printStackTrace();
}
}
}
客户端handler处理器:
package rpc.handler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import protocol.RpcRequest;
import protocol.RpcResponse;
import java.util.concurrent.Callable;
public class ClientHandler extends SimpleChannelInboundHandler implements Callable {
private ChannelHandlerContext ctx;
private RpcResponse rpcResponse;
private RpcRequest rpcRequest;
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("客户端与服务端建立连接..");
this.ctx=ctx;
}
@Override
protected synchronized void channelRead0(ChannelHandlerContext channelHandlerContext, RpcResponse rpcResponse) throws Exception {
System.out.println("服务器返回数据..."+rpcResponse);
this.rpcResponse=rpcResponse;
notify();
}
//被代理对象调用,发送数据给服务器,等待被唤醒wait
@Override
public synchronized Object call() throws Exception {
System.out.println("call 方法执行..");
ctx.writeAndFlush(rpcRequest);
wait();
return rpcResponse.getData();
}
public void setRpcRequest(RpcRequest rpcRequest) {
this.rpcRequest = rpcRequest;
}
}
客户端controller(注意此处拿到的service是代理对象)
package rpc.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import rpc.consumer.NettyClient;
import rpcinterface.HelloService;
import rpcinterface.HiService;
import javax.annotation.PostConstruct;
@RestController
public class HelloController {
private HelloService helloService;
private HiService hiService;
@PostConstruct
private void init(){
helloService = (HelloService) NettyClient.getBean(HelloService.class);
hiService = (HiService)NettyClient.getBean(HiService.class);
}
@RequestMapping("/hello")
public String hello(String name){
return helloService.hello(name);
}
@RequestMapping("/hi")
public String hi(String name){
return hiService.hi(name);
}
}
自定义请求协议:
package protocol;
/**
* 请求协议
*/
public class RpcRequest {
private String requestId;
private String interfaceName;
private String methodName;
private Class>[] paramTypes;
private Object[] patams;
public String getRequestId() {
return requestId;
}
public void setRequestId(String requestId) {
this.requestId = requestId;
}
public String getInterfaceName() {
return interfaceName;
}
public void setInterfaceName(String interfaceName) {
this.interfaceName = interfaceName;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public Class>[] getParamTypes() {
return paramTypes;
}
public void setParamTypes(Class>[] paramTypes) {
this.paramTypes = paramTypes;
}
public Object[] getPatams() {
return patams;
}
public void setPatams(Object[] patams) {
this.patams = patams;
}
}
自定义响应协议:
package protocol;
/**
* 响应协议
*/
public class RpcResponse {
private String requestId;
private Object data;
public String getRequestId() {
return requestId;
}
public void setRequestId(String requestId) {
this.requestId = requestId;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
服务端初始化代码:
package provider;
import codec.RpcDecoder;
import codec.RpcEncoder;
import handler.NettyServerHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import protocol.RpcRequest;
import protocol.RpcResponse;
public class NettyServer {
//nettyServer的初始化
public static void startServer0(String hostName,int port){
NioEventLoopGroup boss = new NioEventLoopGroup(1);
NioEventLoopGroup worker = new NioEventLoopGroup(8);
try{
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(boss,worker)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new RpcDecoder(RpcRequest.class));//解码器
pipeline.addLast(new RpcEncoder(RpcResponse.class));//编码器
pipeline.addLast(new NettyServerHandler());//业务处理器
}
});
ChannelFuture channelFuture = serverBootstrap.bind(hostName, port).addListener(future -> {
if(future.isSuccess()){
System.out.println("服务端启动成功,监听端口:"+port);
}
});
channelFuture.channel().closeFuture().sync();
}catch (Exception e){
e.printStackTrace();
}finally {
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
}
服务端启动类:
package provider;
/**
* 启动一个服务提供者,nettyServer
*/
public class ServerBootApplication {
public static void main(String[] args) {
NettyServer.startServer0("127.0.0.1",9999
);
}
}
服务端业务处理器Handler:
package handler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import protocol.RpcRequest;
import protocol.RpcResponse;
import util.InterfaceMapUtils;
import java.lang.reflect.Method;
import java.util.List;
public class NettyServerHandler extends SimpleChannelInboundHandler {
@Override
public void channelRead0(ChannelHandlerContext ctx, RpcRequest request) throws Exception {
System.out.println("服务端收到请求");
String requestId = request.getRequestId();
String interfaceName = request.getInterfaceName();
String methodName = request.getMethodName();
Class>[] paramTypes = request.getParamTypes();
Object[] prams = request.getPatams();
Class> clazz = InterfaceMapUtils.interfaceMap.get(interfaceName);
Object instance = clazz.newInstance();
Method method = clazz.getMethod(methodName, paramTypes);
Object invoke = method.invoke(instance, prams);
RpcResponse response = new RpcResponse();
response.setRequestId(requestId);
response.setData(invoke);
ctx.writeAndFlush(response);
System.out.println("服务端处理请求完毕");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
服务端本地注册的容器
package util;
import rpcinterface.HelloService;
import rpcinterface.HiService;
import rpcinterface.impl.HelloServiceImpl;
import rpcinterface.impl.HiServiceImpl;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 服务端本地注册的容器
*/
public class InterfaceMapUtils {
public static Map> interfaceMap = new ConcurrentHashMap<>();
static{
Class helloServiceClass = HelloService.class;
String hello = helloServiceClass.getName();
Class hiServiceClass = HiService.class;
String hi = hiServiceClass.getName();
interfaceMap.put(hello, HelloServiceImpl.class);
interfaceMap.put(hi, HiServiceImpl.class);
}
}
服务端接口:
package rpcinterface;
public interface HelloService {
String hello(String msg);
}
服务端实现类:
package rpcinterface.impl;
import rpcinterface.HelloService;
public class HelloServiceImpl implements HelloService {
@Override
public String hello(String msg) {
System.out.println("收到客户端消息:"+msg);
return "你好客户端,我已经收到你的消息:["+msg+"]";
}
}