Java的IO分为BIO、NIO、AIO(NIO.2), 其中它们分别含义是:
Java BIO : 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
Java NIO : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
Java AIO(NIO.2) : 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。
其中NIO常用框架netty,dubbo作为远程调用RPC也是调用此框架。下面我就以netty写一个远程调用例子。
项目应用结构:

整个项目分为客户端(wyjm-netty-client)、服务器端(wyjm-netty-server)和公共模块(wyjm-netty-common)。
客户端(wyjm-netty-client):
wyjm-netty-client-bean,客户端消费参数的处理

ConsumerConfigure.java,消费者实体数据信息。此参数是在consumer.xml配置。
package com.jd.wyjm.netty.client.bean.model;
import lombok.Data;
@Data
public class ConsumerConfigure {
/**
* IP地址
*/
private String ip;
/**
* 端口
*/
private Integer port;
/**
* 接口类别
*/
private String interfaceClass;
/**
* 重试次数
*/
private Integer retries;
}
ConsumerBeanDefinitionParser.java,解析消费者实体数据信息,数据来源在consumer.xml配置。
package com.jd.wyjm.netty.client.bean.schema;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;
public class ConsumerBeanDefinitionParser implements BeanDefinitionParser {
private final Class<?> beanClass;
public ConsumerBeanDefinitionParser(Class<?> beanClass) {
this.beanClass = beanClass;
}
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClass(beanClass);
beanDefinition.setLazyInit(false);
beanDefinition.getPropertyValues().add("ip", element.getAttribute("ip"));
beanDefinition.getPropertyValues().add("port", element.getAttribute("port"));
beanDefinition.getPropertyValues().add("interfaceClass", element.getAttribute("interfaceClass"));
beanDefinition.getPropertyValues().add("retries", element.getAttribute("retries"));
BeanDefinitionRegistry beanDefinitionRegistry = parserContext.getRegistry();
beanDefinitionRegistry.registerBeanDefinition(element.getAttribute("id"),beanDefinition);
//注册bean到BeanDefinitionRegistry中
return beanDefinition;
}
}
ConsumerNamespaceHandler.java,注册Bean到Spring容器。
package com.jd.wyjm.netty.client.bean.schema;
import com.jd.wyjm.netty.client.bean.model.ConsumerConfigure;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
public class ConsumerNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("consumer", new ConsumerBeanDefinitionParser(ConsumerConfigure.class));
}
}
netty-client.xsd,定义xml配置文件节点。
<xsd:schema xmlns="http://wyjm.netty.com/schema"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://wyjm.netty.com/schema">
<xsd:complexType name="complexType">
<xsd:attribute name="id" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[ The complexType id. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="ip" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[ The complexType ip. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="port" type="xsd:int">
<xsd:annotation>
<xsd:documentation><![CDATA[ The complexType port. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="interfaceClass" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[ The complexType interfaceClass. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="retries" type="xsd:int">
<xsd:annotation>
<xsd:documentation><![CDATA[ The complexType retries. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:element name="consumer" type="complexType">
<xsd:annotation>
<xsd:documentation><![CDATA[ consumer 的文档 ]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:schema>
spring.handlers,定义ConsumerNamespaceHandler的加载。
http\://wyjm.netty.com/schema=com.jd.wyjm.netty.client.bean.schema.ConsumerNamespaceHandler
spring.schemas,定义consumer.xml
http\://wyjm.netty.com/schema/netty-client.xsd=META-INF/netty-client.xsd
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>wyjm-netty</artifactId>
<groupId>com.jd.wyjm.netty</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>wyjm-netty-client-bean</artifactId>
<dependencies>
<dependency>
<groupId>com.jd.wyjm.netty</groupId>
<artifactId>wyjm-netty-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
</dependencies>
</project>
wyjm-netty-client-core,客户端消费核心

ClientChannelHandlerAdapter.java,客户端渠道句柄适配器
package com.jd.wyjm.netty.client.core.adapter;
import com.jd.wyjm.netty.common.entity.MethodInvokeMeta;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class ClientChannelHandlerAdapter extends ChannelHandlerAdapter {
/**
* 方法元信息
*/
private MethodInvokeMeta methodInvokeMeta;
/**
* 客户端渠道
*/
private ClientChannelInitializer customChannel;
public ClientChannelHandlerAdapter(MethodInvokeMeta methodInvokeMeta, ClientChannelInitializer customChannel){
this.methodInvokeMeta=methodInvokeMeta;
this.customChannel=customChannel;
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
log.info("客户端出异常了,异常信息:{}", cause.getMessage());
cause.printStackTrace();
ctx.close();
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
if (methodInvokeMeta.getMethodName().endsWith("toString") && !"class java.lang.String".equals(methodInvokeMeta.getReturnType().toString()))
log.info("客户端发送信息参数:{},信息返回值类型:{}", methodInvokeMeta.getArgs(), methodInvokeMeta.getReturnType());
ctx.writeAndFlush(methodInvokeMeta);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
customChannel.setResponse(msg);
}
}
ClientChannelInitializer.java,客户端渠道管道初始化.
package com.jd.wyjm.netty.client.core.adapter;
import com.jd.wyjm.netty.common.entity.MethodInvokeMeta;
import com.jd.wyjm.netty.common.entity.NullWritable;
import com.jd.wyjm.netty.common.entity.ObjectCodec;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
public class ClientChannelInitializer extends ChannelInitializer {
private MethodInvokeMeta methodInvokeMeta;
private Object response;
public ClientChannelInitializer(MethodInvokeMeta methodInvokeMeta){
this.methodInvokeMeta=methodInvokeMeta;
}
@Override
protected void initChannel(Channel channel) throws Exception {
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(new LengthFieldPrepender(2));
pipeline.addLast(new LengthFieldBasedFrameDecoder(1024 * 1024, 0, 2, 0, 2));
pipeline.addLast(new ObjectCodec());
pipeline.addLast(new ClientChannelHandlerAdapter(methodInvokeMeta, this));
}
public Object getResponse() {
if (response instanceof NullWritable) {
return null;
}
return response;
}
public void setResponse(Object response) {
this.response = response;
}
}
NettyProxyFactoryBean.java,服务接口动态代理
package com.jd.wyjm.netty.client.core.proxy;
import com.jd.wyjm.netty.client.core.NettyClient;
import com.jd.wyjm.netty.common.entity.MethodInvokeMeta;
import com.jd.wyjm.netty.common.utils.WrapMethodUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
@Slf4j
public class NettyProxyFactoryBean extends AbstractFactoryBean implements InvocationHandler {
/**
* IP地址
*/
private String ip;
/**
* 端口号
*/
private int port;
/**
* 重试次数
*/
private int retries;
/**
* 接口类
*/
private Class interfaceClass;
/**
* 远程客户端
*/
private NettyClient nettyClient;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
final MethodInvokeMeta methodInvokeMeta = WrapMethodUtils.readMethod(interfaceClass, method, args);
if (!methodInvokeMeta.getMethodName().equals("toString")) {
log.info("[invoke] 调用接口{},调用方法名:{},入参:{},参数类型:{},返回值类型{}",
methodInvokeMeta.getInterfaceClass(), methodInvokeMeta.getMethodName()
, methodInvokeMeta.getArgs(), methodInvokeMeta.getParameterTypes(), methodInvokeMeta.getReturnType());
}
return nettyClient.remoteCall(methodInvokeMeta, ip,0,port,retries);
}
@Override
public Class<?> getObjectType() {
return this.interfaceClass;
}
@Override
protected Object createInstance() throws Exception {
log.info("[代理工厂] 初始化代理Bean : {}", this.interfaceClass);
return Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, this);
}
public void setInterfaceClass(Class interfaceClass) {
this.interfaceClass = interfaceClass;
}
public void setNettyClient(NettyClient nettyClient) {
this.nettyClient = nettyClient;
}
public void setIp(String ip) {
this.ip = ip;
}
public void setPort(int port) {
this.port = port;
}
public void setRetries(int retries) {
this.retries = retries;
}
}
NettyProxyFactoryRegisterProcessor.java,将对应的接口注册为动态代理。
package com.jd.wyjm.netty.client.core.proxy;
import com.jd.wyjm.netty.client.bean.model.ConsumerConfigure;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.stereotype.Component;
@Component
public class NettyProxyFactoryRegisterProcessor implements BeanFactoryPostProcessor {
private DefaultListableBeanFactory beanFactory;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
this.beanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;
String[] beanNames = configurableListableBeanFactory.getBeanNamesForType(ConsumerConfigure.class);
for (String beanName : beanNames) {
ConsumerConfigure consumer = (ConsumerConfigure) configurableListableBeanFactory.getBean(beanName);
BeanDefinitionBuilder beanDefinitionBuilder=BeanDefinitionBuilder.genericBeanDefinition(NettyProxyFactoryBean.class);
beanDefinitionBuilder.addPropertyValue("interfaceClass",consumer.getInterfaceClass());
beanDefinitionBuilder.addPropertyValue("ip",consumer.getIp());
beanDefinitionBuilder.addPropertyValue("port",consumer.getPort());
beanDefinitionBuilder.addPropertyValue("retries",consumer.getRetries());
beanDefinitionBuilder.addPropertyReference("nettyClient","nettyClient");
this.beanFactory.registerBeanDefinition(consumer.getInterfaceClass(),beanDefinitionBuilder.getRawBeanDefinition());
}
}
}
NettyClient.java,远程调用客户端
package com.jd.wyjm.netty.client.core;
import com.jd.wyjm.netty.client.core.adapter.ClientChannelInitializer;
import com.jd.wyjm.netty.common.entity.MethodInvokeMeta;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
@Slf4j
@Component
public class NettyClient implements InitializingBean {
/**
* 客户端Bootstrap
*/
private Bootstrap bootstrap;
/**
* 客户端WORKER
*/
private EventLoopGroup worker;
@PreDestroy
public void close(){
log.info("关闭资源");
worker.shutdownGracefully();
}
/**
* 远程调用
* @param cmd
* @param ip
* @param retry
* @param port
* @param retries
* @return
*/
public Object remoteCall(final MethodInvokeMeta cmd, String ip,int retry,int port,int retries){
try{
ClientChannelInitializer customChannel=new ClientChannelInitializer(cmd);
bootstrap.handler(customChannel);
ChannelFuture channelFuture=bootstrap.connect(ip,port).sync();
channelFuture.channel().closeFuture().sync();
return customChannel.getResponse();
}catch (InterruptedException e){
retry++;
if (retry > retries) {
throw new RuntimeException("调用Wrong");
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
log.info("第{}次尝试....失败", retry);
return remoteCall(cmd, ip,retry,port,retries);
}
}
}
@Override
public void afterPropertiesSet() throws Exception {
bootstrap = new Bootstrap();
worker = new NioEventLoopGroup();
bootstrap.group(worker);
bootstrap.channel(NioSocketChannel.class);
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>wyjm-netty</artifactId>
<groupId>com.jd.wyjm.netty</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>wyjm-netty-client-core</artifactId>
<dependencies>
<dependency>
<groupId>com.jd.wyjm.netty</groupId>
<artifactId>wyjm-netty-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.jd.wyjm.netty</groupId>
<artifactId>wyjm-netty-client-bean</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
wyjm-netty-client-web,客户端启动程序

WebApplication.java,启动程序类
package com.jd.wyjm.netty.client.web.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ImportResource;
@SpringBootApplication
@ComponentScan(basePackages = {
"com.jd.wyjm.netty.common.*",
"com.jd.wyjm.netty.client.core",
"com.jd.wyjm.netty.client.core.*",
"com.jd.wyjm.netty.client.web.*"
})
@ImportResource(value = {"classpath:config/consumer.xml"})
public class WebApplication extends SpringBootServletInitializer {
public static void main(String[] args){
SpringApplication.run(WebApplication.class,args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(WebApplication.class);
}
}
consumer.xml,RPC接口配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cli="http://wyjm.netty.com/schema"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://wyjm.netty.com/schema http://wyjm.netty.com/schema/netty-client.xsd">
<cli:consumer id="userService" ip="127.0.0.1" port="2020" interfaceClass="com.jd.wyjm.netty.server.service.UserRpcService" retries="2"></cli:consumer>
</beans>
UserServiceImplTest.java,单元测试类.
package com.jd.wyjm.netty.client.web.test;
import com.jd.wyjm.netty.client.web.app.WebApplication;
import com.jd.wyjm.netty.server.service.UserRpcService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = WebApplication.class)
public class UserServiceImplTest {
@Autowired
private UserRpcService userRpcService;
@Test
public void insertUser(){
System.out.println(userRpcService.inserUser("单纯的心"));
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>wyjm-netty</artifactId>
<groupId>com.jd.wyjm.netty</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>wyjm-netty-client-web</artifactId>
<dependencies>
<dependency>
<groupId>com.jd.wyjm.netty</groupId>
<artifactId>wyjm-netty-client-core</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.jd.wyjm.netty</groupId>
<artifactId>wyjm-netty-server-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
<version>4.12</version>
</dependency>
</dependencies>
</project>
pom.xml,父节点的pom文件
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>wyjm-netty</artifactId>
<groupId>com.jd.wyjm.netty</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>wyjm-netty-client</artifactId>
<packaging>pom</packaging>
<modules>
<module>wyjm-netty-client-bean</module>
<module>wyjm-netty-client-core</module>
<module>wyjm-netty-client-web</module>
</modules>
</project>
至此客户端的RPC调用代码编写完成
服务器端(wyjm-netty-server):
wyjm-netty-server-api,对外提供接口API。此模块设计思想,不依赖外部任何一个模块。

UserRpcService.java,用户服务接口类
package com.jd.wyjm.netty.server.service;
public interface UserRpcService {
/**
* 插入用户数据
* @param userName
*/
Long inserUser(String userName);
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>wyjm-netty</artifactId>
<groupId>com.jd.wyjm.netty</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>wyjm-netty-server-api</artifactId>
</project>
wyjm-netty-server-core,核心模块,可以封装为jar。

ServerChannelHandlerAdapter.java,服务端渠道句柄适配器
package com.jd.wyjm.netty.server.core.adapter;
import com.jd.wyjm.netty.common.entity.MethodInvokeMeta;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
@Sharable
@Slf4j
public class ServerChannelHandlerAdapter extends ChannelHandlerAdapter {
/**
* 分派器
*/
@Autowired
private ServerRequestDispatcher dispatcher;
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
MethodInvokeMeta invokeMeta = (MethodInvokeMeta) msg;
// 屏蔽toString()方法
if (invokeMeta.getMethodName().endsWith("toString()")
&& !"class java.lang.String".equals(invokeMeta.getReturnType().toString()))
log.info("客户端传入参数 :{},返回值:{}",
invokeMeta.getArgs(), invokeMeta.getReturnType());
dispatcher.dispatcher(ctx, invokeMeta);
}
}
ServerRequestDispatcher.java,请求分派器
package com.jd.wyjm.netty.server.core.adapter;
import com.jd.wyjm.netty.common.core.CommonResult;
import com.jd.wyjm.netty.common.entity.MethodInvokeMeta;
import com.jd.wyjm.netty.common.entity.NullWritable;
import com.jd.wyjm.netty.common.enums.CodeEnum;
import com.jd.wyjm.netty.server.core.config.NettyProperties;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
*
* 请求分派器
*
* @author zhangqiang35
*/
@Component
public class ServerRequestDispatcher implements ApplicationContextAware, InitializingBean {
/**
* 线程池
*/
private ExecutorService executor= null;
/**
* 上下文
*/
private ApplicationContext applicationContext;
/**
* 配置
*/
@Autowired
private NettyProperties nettyProperties;
/**
* 消息发送
*/
public void dispatcher(final ChannelHandlerContext ctx,final MethodInvokeMeta invokeMeta){
/**
* 提交消息发送
*/
executor.submit(()->{
ChannelFuture channelFuture=null;
try{
//取得到接口名称
Class<?> interfaceClass=invokeMeta.getInterfaceClass();
//取得到方法名称
String methodName=invokeMeta.getMethodName();
//取得参数数组
Object[] args=invokeMeta.getArgs();
//取得参数类型
Class<?>[] parameterTypes=invokeMeta.getParameterTypes();
//目标对象
Object targetObject=applicationContext.getBean(interfaceClass);
//目标对象方法
Method method = targetObject.getClass().getMethod(methodName, parameterTypes);
//调用对应方法
Object obj=method.invoke(targetObject,args);
if(obj == null){
channelFuture=ctx.writeAndFlush(NullWritable.nullWritable());
}else{
channelFuture=ctx.writeAndFlush(obj);
}
channelFuture.addListener(ChannelFutureListener.CLOSE);
}catch (Exception ex){
CommonResult commonResult = new CommonResult(CodeEnum.UNKOWN_ERROR);
channelFuture= ctx.writeAndFlush(commonResult);
channelFuture.addListener(ChannelFutureListener.CLOSE);
}
});
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
}
@Override
public void afterPropertiesSet() throws Exception {
executor= Executors.newFixedThreadPool(nettyProperties.getMaxThreads());
}
}
NettyProperties.java,属性配置
package com.jd.wyjm.netty.server.core.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "netty.server")
@Data
public class NettyProperties {
/**
* 端口
*/
private Integer port;
/**
* 最大线程数
*/
private Integer maxThreads;
/**
* 数据包最大长度
*/
private Integer maxLength;
}
ServerNettyRunner.java,开启接口服务
package com.jd.wyjm.netty.server.core;
import com.jd.wyjm.netty.common.entity.ObjectCodec;
import com.jd.wyjm.netty.server.core.adapter.ServerChannelHandlerAdapter;
import com.jd.wyjm.netty.server.core.config.NettyProperties;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
@Slf4j
@Component
public class ServerNettyRunner implements CommandLineRunner {
/**
* 创建bootstrap
*/
private final ServerBootstrap serverBootstrap=new ServerBootstrap();
/**
* 创建BOSS
*/
private final EventLoopGroup bossLoopGroup=new NioEventLoopGroup();
/**
* 创建WORKER
*/
private final EventLoopGroup workerLoopGroup=new NioEventLoopGroup();
/**
* 渠道适配器
*/
@Resource
private ServerChannelHandlerAdapter serverChannelHandlerAdapter;
/**
* 配置
*/
@Autowired
private NettyProperties nettyProperties;
@PreDestroy
public void close(){
log.info("关闭服务器.....");
//退出BOSS
bossLoopGroup.shutdownGracefully();
//退出WORKER
workerLoopGroup.shutdownGracefully();
}
/**
* 开启线程服务
* @param strings
* @throws Exception
*/
@Override
public void run(String... strings) throws Exception {
serverBootstrap.group(bossLoopGroup,workerLoopGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG,100)
.handler(new LoggingHandler(LogLevel.INFO));
try{
serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline channelPipeline=socketChannel.pipeline();
channelPipeline.addLast(new LengthFieldBasedFrameDecoder(nettyProperties.getMaxLength(), 0, 2, 0, 2));
channelPipeline.addLast(new LengthFieldPrepender(2));
channelPipeline.addLast(new ObjectCodec());
channelPipeline.addLast(serverChannelHandlerAdapter);
}
});
log.info("netty服务器在[{}]端口启动监听",nettyProperties.getPort());
ChannelFuture channelFuture=serverBootstrap.bind(nettyProperties.getPort()).sync();
channelFuture.channel().closeFuture().sync();
}catch (Exception e){
log.info("[出现异常] 释放资源");
bossLoopGroup.shutdownGracefully();
workerLoopGroup.shutdownGracefully();
}
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>wyjm-netty</artifactId>
<groupId>com.jd.wyjm.netty</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>wyjm-netty-server-core</artifactId>
<dependencies>
<dependency>
<groupId>com.jd.wyjm.netty</groupId>
<artifactId>wyjm-netty-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
wyjm-netty-server-provider,代理模块,对外输出的RPC接口统一封装。

UserRpcServiceImpl.java,RPC接口的实现。
package com.jd.wyjm.netty.server.provider.service;
import com.jd.wyjm.netty.server.service.UserRpcService;
import com.jd.wyjm.netty.server.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserRpcServiceImpl implements UserRpcService {
@Autowired
private UserService userService;
@Override
public Long inserUser(String userName) {
return userService.insertUser(userName);
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>wyjm-netty</artifactId>
<groupId>com.jd.wyjm.netty</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>wyjm-netty-server-provider</artifactId>
<dependencies>
<dependency>
<groupId>com.jd.wyjm.netty</groupId>
<artifactId>wyjm-netty-server-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.jd.wyjm.netty</groupId>
<artifactId>wyjm-netty-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.jd.wyjm.netty</groupId>
<artifactId>wyjm-netty-server-service</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
wyjm-netty-server-service,服务层模块。

UserServiceImpl.java,用户服务实现类。
package com.jd.wyjm.netty.server.service.impl;
import com.jd.wyjm.netty.server.service.UserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Override
public Long insertUser(String userName) {
System.out.println("插入用户数据信息");
return System.currentTimeMillis();
}
}
UserService.java,用户接口类
package com.jd.wyjm.netty.server.service;
public interface UserService {
/**
* 插入用户数据
* @param userName
*/
Long insertUser(String userName);
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>wyjm-netty</artifactId>
<groupId>com.jd.wyjm.netty</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>wyjm-netty-server-service</artifactId>
<dependencies>
<dependency>
<groupId>com.jd.wyjm.netty</groupId>
<artifactId>wyjm-netty-server-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.jd.wyjm.netty</groupId>
<artifactId>wyjm-netty-server-core</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
wyjm-netty-server-web,应用启动模块。

WebApplication.java,应用启动类
package com.jd.wyjm.netty.server.web.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan(basePackages = {
"com.jd.wyjm.netty.common.*",
"com.jd.wyjm.netty.server.core",
"com.jd.wyjm.netty.server.core.*",
"com.jd.wyjm.netty.server.web.*",
"com.jd.wyjm.netty.server.service.*",
"com.jd.wyjm.netty.server.provider.service"
})
public class WebApplication {
public static void main(String[] args){
SpringApplication.run(WebApplication.class,args);
}
}
application.yml
debug: true
spring:
profiles:
active: test
application-test.yml
debug: false
server:
port: 9999
context-path: /
netty:
server:
port: 2020
maxLength: 65535
maxThreads: 100
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>wyjm-netty</artifactId>
<groupId>com.jd.wyjm.netty</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>wyjm-netty-server-web</artifactId>
<dependencies>
<dependency>
<groupId>com.jd.wyjm.netty</groupId>
<artifactId>wyjm-netty-server-core</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.jd.wyjm.netty</groupId>
<artifactId>wyjm-netty-server-provider</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
pom.xml,server的pom
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>wyjm-netty</artifactId>
<groupId>com.jd.wyjm.netty</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>wyjm-netty-server</artifactId>
<modules>
<module>wyjm-netty-server-api</module>
<module>wyjm-netty-server-core</module>
<module>wyjm-netty-server-provider</module>
<module>wyjm-netty-server-service</module>
<module>wyjm-netty-server-web</module>
</modules>
</project>
pom.xml,wyjm-netty的pom文件
<?xml version="1.0" encoding="UTF-8"?>
<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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.jd.wyjm.netty</groupId>
<artifactId>wyjm-netty</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>wyjm-netty-common</module>
<module>wyjm-netty-client</module>
<module>wyjm-netty-server</module>
</modules>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
公共模块(wyjm-netty-common):

CommonResult.java,接口统一响应结果,此处暂时没有用到。
package com.jd.wyjm.netty.common.core;
import com.jd.wyjm.netty.common.enums.CodeEnum;
import lombok.Data;
import lombok.ToString;
/**
* 响应结果
*
* @create 2018-8-22
* @author zhangqiang200<https://blog.youkuaiyun.com/zhangqiang_accp>
*/
@Data
@ToString
public class CommonResult<T> {
/**
* 应答码
*
* @see 默认值为操作成功
*/
private int code = CodeEnum.SUCCESS.getCode();
/**
* 应答码描述
*
* @see 默认值为操作成功
*/
private String message = CodeEnum.SUCCESS.getMessage();
/**
* 应答数据体
*
* @see 可以为空
*/
private T data;
public CommonResult() {
this(CodeEnum.SUCCESS.getCode(), CodeEnum.SUCCESS.getMessage(), null);
}
public CommonResult(CodeEnum codeEnum) {
this(codeEnum.getCode(), codeEnum.getMessage(), null);
}
/**
* 默认返回的code=CodeEnum.SUCCESS.getCode()
*/
public CommonResult(T data) {
this(CodeEnum.SUCCESS.getCode(), CodeEnum.SUCCESS.getMessage(), data);
}
/**
* 默认返回的data=null
*/
public CommonResult(int code, String message) {
this(code, message, null);
}
public CommonResult(int code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
/** 判断返回结果是否成功 */
public boolean success() {
return code == CodeEnum.SUCCESS.getCode();
}
}
MethodInvokeMeta.java,调用方法元数据类.
package com.jd.wyjm.netty.common.entity;
import lombok.Data;
import java.io.Serializable;
/**
* 调用的方法元信息
*
* @author 单纯的心
*/
@Data
public class MethodInvokeMeta implements Serializable {
/**
*接口
*/
private Class<?> interfaceClass;
/**
* 方法名
*/
private String methodName;
/**
* 参数
*/
private Object[] args;
/**
* 返回值类型
*/
private Class<?> returnType;
/**
* 参数类型
*/
private Class<?>[] parameterTypes;
}
NullWritable.java,返回空处理.
package com.jd.wyjm.netty.common.entity;
import java.io.Serializable;
/**
* 返回为空处理
*/
public class NullWritable implements Serializable {
private static NullWritable instance = new NullWritable();
private NullWritable() {
}
public static NullWritable nullWritable() {
return instance;
}
}
ObjectCodec.java,消息解析
package com.jd.wyjm.netty.common.entity;
import com.jd.wyjm.netty.common.utils.ObjectSerializerUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import java.util.List;
/**
* 消息解析
* @author zhangqiang35
*/
public class ObjectCodec extends MessageToMessageCodec<ByteBuf, Object> {
@Override
protected void encode(ChannelHandlerContext channelHandlerContext, Object msg, List<Object> out) throws Exception {
byte[] data = ObjectSerializerUtils.serilizer(msg);
ByteBuf buf = Unpooled.buffer();
buf.writeBytes(data);
out.add(buf);
}
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf msg, List<Object> out) throws Exception {
byte[] bytes = new byte[msg.readableBytes()];
msg.readBytes(bytes);
Object deSerilizer = ObjectSerializerUtils.deSerilizer(bytes);
out.add(deSerilizer);
}
}
CodeEnum.java, 响应结果枚举
package com.jd.wyjm.netty.common.enums;
/**
* 响应码
* @author zhangqiang35
*/
public enum CodeEnum {
/**
* 处理成功
*/
SUCCESS(200, "处理成功"),
/**
* 未知异常
*/
UNKOWN_ERROR(999,"未知异常");
/**
* 响应代码
*/
private final int code;
/**
* 响应消息
*/
private final String message;
CodeEnum(int _code, String _message) {
this.code = _code;
this.message = _message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}
ObjectSerializerUtils.java,对象序列号工具
package com.jd.wyjm.netty.common.utils;
import java.io.*;
public class ObjectSerializerUtils {
/**
* 反序列化
*
* @param data
* @return
*/
public static Object deSerilizer(byte[] data) {
if (data != null && data.length > 0) {
try {
ByteArrayInputStream bis = new ByteArrayInputStream(data);
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
return null;
} else {
return null;
}
}
/**
* 序列化对象
*
* @param obj
* @return
*/
public static byte[] serilizer(Object obj) {
if (obj != null) {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
oos.flush();
oos.close();
return bos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
} else {
return null;
}
}
}
WrapMethodUtils.java,方法参数封装
package com.jd.wyjm.netty.common.utils;
import com.jd.wyjm.netty.common.entity.MethodInvokeMeta;
import java.lang.reflect.Method;
/**
* @author zhangqiang35
*/
public class WrapMethodUtils {
/**
* 获取到方法的元数据
* @param interfaceClass
* @param method
* @param args
* @return
*/
public static MethodInvokeMeta readMethod(Class interfaceClass, Method method, Object[] args) {
MethodInvokeMeta methodInvokeMeta = new MethodInvokeMeta();
methodInvokeMeta.setInterfaceClass(interfaceClass);
methodInvokeMeta.setArgs(args);
methodInvokeMeta.setMethodName(method.getName());
methodInvokeMeta.setReturnType(method.getReturnType());
Class<?>[] parameterTypes = method.getParameterTypes();
methodInvokeMeta.setParameterTypes(parameterTypes);
return methodInvokeMeta;
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>wyjm-netty</artifactId>
<groupId>com.jd.wyjm.netty</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>wyjm-netty-common</artifactId>
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>5.0.0.Alpha2</version>
</dependency>
</dependencies>
</project>
至此通过Netty实现RPC接口的调用已完成
本文详细介绍使用Netty实现远程过程调用(RPC)的全过程,包括客户端和服务端的代码结构、关键组件如ChannelHandlerAdapter的实现,以及如何通过Spring框架进行配置。
422





