自定义基于netty的rpc框架(2)---服务端的实现

来源:互联网 发布:淘宝网背带裤女装 编辑:程序博客网 时间:2024/05/17 07:05

如(1)所介绍的下面我们来进行服务器端的实现:

1、服务器端

1.1、rpc-server-demo服务端框架的实现

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将在运行期也保留注释,因此可以通过过反射机制读取注解的信息@Componentpublic @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<String, Object> handlerMap = new HashMap<String, Object>();    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<String, Object> 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<SocketChannel>() {                        @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<String, Object> handlerMap;    public RpcHandler(Map<String, Object> 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.demo</artifactId>        <groupId>tj.cmcc.org</groupId>        <version>1.0-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>    <artifactId>rpc-server</artifactId>    <packaging>jar</packaging>    <name>rpc-server Maven Webapp</name>    <url>http://maven.apache.org</url>    <dependencies>        <!-- SLF4J -->        <dependency>            <groupId>org.slf4j</groupId>            <artifactId>slf4j-log4j12</artifactId>        </dependency>        <!-- Spring -->        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-context</artifactId>        </dependency>        <!-- Apache Commons Collections -->        <dependency>            <groupId>org.apache.commons</groupId>            <artifactId>commons-collections4</artifactId>        </dependency>        <!-- CGLib -->        <dependency>            <groupId>cglib</groupId>            <artifactId>cglib</artifactId>        </dependency>        <!--rpc module-->        <dependency>            <groupId>${project.groupId}</groupId>            <artifactId>rpc-utils</artifactId>        </dependency>        <dependency>            <groupId>${project.groupId}</groupId>            <artifactId>rpc-zk</artifactId>        </dependency>    </dependencies>    <build>        <finalName>rpc-server</finalName>    </build></project>

1.2、rpc-server-impl-demo服务器端业务的实现

log4j.properties:

log4j.rootLogger=ERROR,consolelog4j.appender.console=org.apache.log4j.ConsoleAppenderlog4j.appender.console.target=System.outlog4j.appender.console.layout=org.apache.log4j.PatternLayoutlog4j.appender.console.layout.ConversionPattern=%m%nlog4j.logger.com.xxx.rpc=DEBUG

rpc.properties:

# zookeeper serverregistry.address=mini04:2181,mini05:2181,mini06:2181# rpc serverserver.address=localhost:8000

spring.xml:

<?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: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.demo</artifactId>        <groupId>tj.cmcc.org</groupId>        <version>1.0-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>    <artifactId>rpc-server-impl</artifactId>    <packaging>jar</packaging>    <name>rpc-server-impl Maven Webapp</name>    <url>http://maven.apache.org</url>    <dependencies>        <!-- JUnit -->        <dependency>            <groupId>junit</groupId>            <artifactId>junit</artifactId>        </dependency>        <!--rpc modules-->        <dependency>            <groupId>tj.cmcc.org</groupId>            <artifactId>rpc-server</artifactId>        </dependency>        <dependency>            <groupId>tj.cmcc.org</groupId>            <artifactId>rpc-protocol</artifactId>        </dependency>        <dependency>            <groupId>tj.cmcc.org</groupId>            <artifactId>rpc-zk</artifactId>        </dependency>    </dependencies>    <build>        <finalName>rpc-server-impl</finalName>    </build></project>
0 1
原创粉丝点击