如(1)所介绍的下面我们来进行服务器端的实现:
cn.tianjun.rpc.server.RpcService
package cn.tianjun.rpc.server;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.stereotype.Component;
/**
* RPC 请求注解(标注在服务实现类上)
*/
@Target({ ElementType.TYPE })//注解用在接口上
@Retention(RetentionPolicy.RUNTIME)//VM将在运行期也保留注释,因此可以通过过反射机制读取注解的信息
@Component
public @interface RpcService {
Class> value();
}
cn.tianjun.rpc.server.RpcServer
package cn.tianjun.rpc.server;
import cn.tianjun.rpc.utils.RpcDecoder;
import cn.tianjun.rpc.utils.RpcEncoder;
import cn.tianjun.rpc.utils.RpcRequest;
import cn.tianjun.rpc.utils.RpcResponse;
import cn.tianjun.zk.ServiceRegistry;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.collections4.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* 框架的RPC 服务器(用于将用户系统的业务类发布为 RPC 服务)
* 使用时可由用户通过spring-bean的方式注入到用户的业务系统中
* 由于本类实现了ApplicationContextAware InitializingBean
* spring构造本对象时会调用setApplicationContext()方法,从而可以在方法中通过自定义注解获得用户的业务接口和实现
* 还会调用afterPropertiesSet()方法,在方法中启动netty服务器
*
*/
public class RpcServer implements ApplicationContextAware, InitializingBean {
private static final Logger LOGGER = LoggerFactory
.getLogger(RpcServer.class);
private String serverAddress;
private ServiceRegistry serviceRegistry;
//用于存储业务接口和实现类的实例对象(由spring所构造)
private Map handlerMap = new HashMap();
public RpcServer(String serverAddress) {
this.serverAddress = serverAddress;
}
//服务器绑定的地址和端口由spring在构造本类时从配置文件中传入
public RpcServer(String serverAddress, ServiceRegistry serviceRegistry) {
this.serverAddress = serverAddress;
//用于向zookeeper注册名称服务的工具类
this.serviceRegistry = serviceRegistry;
}
/**
* 通过注解,获取标注了rpc服务注解的业务类的----接口及impl对象,将它放到handlerMap中
*/
public void setApplicationContext(ApplicationContext ctx)
throws BeansException {
Map serviceBeanMap = ctx
.getBeansWithAnnotation(RpcService.class);
if (MapUtils.isNotEmpty(serviceBeanMap)) {
for (Object serviceBean : serviceBeanMap.values()) {
//从业务实现类上的自定义注解中获取到value,从来获取到业务接口的全名
String interfaceName = serviceBean.getClass()
.getAnnotation(RpcService.class).value().getName();
handlerMap.put(interfaceName, serviceBean);
}
}
}
/**
* 在此启动netty服务,绑定handle流水线:
* 1、接收请求数据进行反序列化得到request对象
* 2、根据request中的参数,让RpcHandler从handlerMap中找到对应的业务imple,调用指定方法,获取返回结果
* 3、将业务调用结果封装到response并序列化后发往客户端
*
*/
public void afterPropertiesSet() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap
.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer() {
@Override
public void initChannel(SocketChannel channel)
throws Exception {
channel.pipeline()
.addLast(new RpcDecoder(RpcRequest.class))// 注册解码 IN-1
.addLast(new RpcEncoder(RpcResponse.class))// 注册编码 OUT
.addLast(new RpcHandler(handlerMap));//注册RpcHandler IN-2
}
}).option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
String[] array = serverAddress.split(":");
String host = array[0];
int port = Integer.parseInt(array[1]);
ChannelFuture future = bootstrap.bind(host, port).sync();
LOGGER.debug("server started on port {}", port);
if (serviceRegistry != null) {
serviceRegistry.register(serverAddress);
}
future.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
cn.tianjun.rpc.server.RpcHandler
package cn.tianjun.rpc.server;
import cn.tianjun.rpc.utils.RpcRequest;
import cn.tianjun.rpc.utils.RpcResponse;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
import java.util.Map;
/**
* 处理具体的业务调用
* 通过构造时传入的“业务接口及实现”handlerMap,来调用客户端所请求的业务方法
* 并将业务方法返回值封装成response对象写入下一个handler(即编码handler——RpcEncoder)
* Created by Administrator on 2016/12/26 0026.
*/
public class RpcHandler extends SimpleChannelInboundHandler<RpcRequest> {
private static final Logger LOGGER = LoggerFactory
.getLogger(RpcHandler.class);
private final Map handlerMap;
public RpcHandler(Map handlerMap) {
this.handlerMap = handlerMap;
}
/**
* 接收消息,处理消息,返回结果
*/
@Override
public void channelRead0(final ChannelHandlerContext ctx, RpcRequest request)
throws Exception {
RpcResponse response = new RpcResponse();
response.setRequestId(request.getRequestId());
try {
//根据request来处理具体的业务调用
Object result = handle(request);
response.setResult(result);
} catch (Throwable t) {
response.setError(t);
}
//写入 outbundle(即RpcEncoder)进行下一步处理(即编码)后发送到channel中给客户端
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
/**
* 根据request来处理具体的业务调用
* 调用是通过反射的方式来完成
*
* @param request
* @return
* @throws Throwable
*/
private Object handle(RpcRequest request) throws Throwable {
String className = request.getClassName();
//拿到实现类对象
Object serviceBean = handlerMap.get(className);
//拿到要调用的方法名、参数类型、参数值
String methodName = request.getMethodName();
Class>[] parameterTypes = request.getParameterTypes();
Object[] parameters = request.getParameters();
//拿到接口类
Class> forName = Class.forName(className);
//调用实现类对象的指定方法并返回结果
Method method = forName.getMethod(methodName, parameterTypes);
return method.invoke(serviceBean, parameters);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
LOGGER.error("server caught exception", cause);
ctx.close();
}
}
pom.xml
<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/maven-v4_0_0.xsd">
<parent>
<artifactId>rpc.demoartifactId>
<groupId>tj.cmcc.orggroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>rpc-serverartifactId>
<packaging>jarpackaging>
<name>rpc-server Maven Webappname>
<url>http://maven.apache.orgurl>
<dependencies>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-collections4artifactId>
dependency>
<dependency>
<groupId>cglibgroupId>
<artifactId>cglibartifactId>
dependency>
<dependency>
<groupId>${project.groupId}groupId>
<artifactId>rpc-utilsartifactId>
dependency>
<dependency>
<groupId>${project.groupId}groupId>
<artifactId>rpc-zkartifactId>
dependency>
dependencies>
<build>
<finalName>rpc-serverfinalName>
build>
project>
log4j.properties:
log4j.rootLogger=ERROR,console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.target=System.out
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%m%n
log4j.logger.com.xxx.rpc=DEBUG
rpc.properties:
# zookeeper server
registry.address=mini04:2181,mini05:2181,mini06:2181
# rpc server
server.address=localhost:8000
spring.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="cn.tianjun.rpcServerImpl"/>
<context:property-placeholder location="classpath:rpc.properties"/>
<bean id="serviceRegistry" class="cn.tianjun.zk.ServiceRegistry">
<constructor-arg name="registryAddress" value="${registry.address}"/>
bean>
<bean id="rpcServer" class="cn.tianjun.rpc.server.RpcServer">
<constructor-arg name="serverAddress" value="${server.address}"/>
<constructor-arg name="serviceRegistry" ref="serviceRegistry"/>
bean>
beans>
cn.tianjun.rpcServerImpl.HelloServiceImpl
package cn.tianjun.rpcServerImpl;
import cn.tianjun.rpc.protocol.HelloService;
import cn.tianjun.rpc.protocol.Person;
import cn.tianjun.rpc.server.RpcService;
@RpcService(HelloService.class)
public class HelloServiceImpl implements HelloService {
public String hello(String name) {
System.out.println("已经调用服务端接口实现,业务处理结果为:");
System.out.println("Hello! " + name);
return "入门! " + name;
}
public String hello(Person person) {
System.out.println("已经调用服务端接口实现,业务处理为:");
System.out.println("Hello! " + person.getFirstName() + " " + person.getLastName());
return "我靠! " + person.getFirstName() + " " + person.getLastName();
}
}
cn.tianjun.rpcServerImpl.RpcBootstrap
package cn.tianjun.rpcServerImpl;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 用户系统服务端的启动入口
* 其意义是启动springcontext,从而构造框架中的RpcServer
* 亦即:将用户系统中所有标注了RpcService注解的业务发布到RpcServer中
*
*/
public class RpcBootstrap {
public static void main(String[] args) {
new ClassPathXmlApplicationContext("spring.xml");
}
}
pom.xml
<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/maven-v4_0_0.xsd">
<parent>
<artifactId>rpc.demoartifactId>
<groupId>tj.cmcc.orggroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>rpc-server-implartifactId>
<packaging>jarpackaging>
<name>rpc-server-impl Maven Webappname>
<url>http://maven.apache.orgurl>
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
dependency>
<dependency>
<groupId>tj.cmcc.orggroupId>
<artifactId>rpc-serverartifactId>
dependency>
<dependency>
<groupId>tj.cmcc.orggroupId>
<artifactId>rpc-protocolartifactId>
dependency>
<dependency>
<groupId>tj.cmcc.orggroupId>
<artifactId>rpc-zkartifactId>
dependency>
dependencies>
<build>
<finalName>rpc-server-implfinalName>
build>
project>