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

如(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将在运行期也保留注释,因此可以通过过反射机制读取注解的信息
@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<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,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:

<?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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值