SpringBoot + Netty 实现 Json字符串 的传输(五)

编解码和数据包都解决了,下面来关注一下,业务处理方面的功能怎样进行设计。

1. 构建 NettyServerHandler 类,完成业务逻辑的处理功能。

    A. 我们需要一个自定义的线程池,用来执行业务逻辑的处理代码;

    B. 我们需要封装一下业务处理环节,服务端的业务处理模式比较简单,基本上采用一个请求对应一个应答的操作,所以,可以提取出统一的调用接口 ServerAction 进行业务处理,之后针对不同的请求设计对应的实现类即可。业务处理环节除了 ServerAction 以外,还需要 请求包的类型 和 应答包的类型 信息。

    C. 构建一个映射关系,Key是请求包的类型,Value是对应的业务处理环节。我们可以通过映射关系快速定位业务处理的 ServerAction 实现类的对象,以及,应答包JavaBean的实例化。

2. 与SpringBoot的集成

    A. 配置参数的自动装配

    B. 映射关系的自动注册

package houlei.net.tcp.hdr;

import io.netty.channel.ChannelHandlerContext;

@FunctionalInterface
public interface ServerAction {

    void execute(ChannelHandlerContext ctx, REQ request, RSP response);

}
package houlei.net.tcp.hdr;

import houlei.net.tcp.pkg.PackageFactory;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Sharable
@Component
public class NettyServerHandler extends SimpleChannelInboundHandler  {

    private static Logger logger = LoggerFactory.getLogger(NettyServerHandler.class);

    private static class ServerActionBean {
        public Class requestType;
        public Class responseType;
        public ServerAction action;
    }
    private static final HashMap actions = new HashMap<>();

    @Resource
    private ApplicationContext applicationContext;
    @Value("${action.executor.corePoolSize:0")
    private int corePoolSize;
    @Value("${action.executor.maxPoolSize:16}")
    private int maxPoolSize;
    @Value("${action.executor.keepAliveTime:5000}")
    private int keepAliveTime;

    private ExecutorService serverActionExecutor;

    @PostConstruct
    public void init(){
        serverActionExecutor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
        String[] names = applicationContext.getBeanNamesForType(ServerAction.class);
        for (String name : names) {
            ServerAction sa = applicationContext.getBean(name, ServerAction.class);
            regist(sa);
        }
    }

    public void regist(ServerAction action) {
        Type[] typeArgs = findActualTypeArguments(action, ServerAction.class);
        if (typeArgs!=null && typeArgs.length>1) {
            ServerActionBean bean = new ServerActionBean();
            bean.requestType  = (Class)typeArgs[0];
            bean.responseType = (Class)typeArgs[1];
            bean.action       = action;
            actions.put(bean.requestType, bean);
            logger.info("[NettyServerHandler][regist] regist server action : {}", action.getClass().getName());
        }
    }

    private static Type[] findActualTypeArguments(ServerAction sa, Class interfaceClass) {
        Type[] genTypes = sa.getClass().getGenericInterfaces();
        for (Type type : genTypes) {
            if (type instanceof ParameterizedType) {
                ParameterizedType pt = (ParameterizedType) type;
                if (pt.getRawType() == interfaceClass) {
                    return pt.getActualTypeArguments();
                }
            }
        }
        return null;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object o) throws Exception {
        ServerActionBean bean = actions.get(o.getClass());

        if (bean != null) {
            serverActionExecutor.submit(()->{
                try {
                    Object request = o;
                    Object response = PackageFactory.create(bean.responseType);
                    bean.action.execute(ctx, request, response);
                    if (response != null) {
                        ctx.writeAndFlush(response);
                    }
                } catch (Exception ex) {
                    logger.error("[NettyServerHandler][ServerActionExecutor] execute action {} failed.", bean.action.getClass().getName(), ex);
                }
            });
        }

    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
        logger.debug("NettyServerHandler#channelActive");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
        logger.debug("NettyServerHandler#channelInactive");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
        ctx.close();
        logger.error("NettyServerHandler#exceptionCaught", cause);
    }

}
package houlei.net.tcp.hdr.action;

import houlei.net.tcp.hdr.ServerAction;
import houlei.net.tcp.pkg.chat.ChatMessage;
import io.netty.channel.ChannelHandlerContext;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;

@Component
public class ChatMessageAction implements ServerAction {

    @Override
    public void execute(ChannelHandlerContext ctx, ChatMessage request, ChatMessage response) {
        BeanUtils.copyProperties(request, response);
    }

}



@Component
public class HartbeatAction implements ServerAction {

    @Override
    public void execute(ChannelHandlerContext ctx, HartbeatPackage request, HartbeatPackage response) {

    }

}


@Component
public class LoginAction implements ServerAction {

    @Override
    public void execute(ChannelHandlerContext ctx, LoginRequestPackage request, LoginResponsePackage response) {
        response.setSucceed(false);
    }

}

代码虽短,思想重要。所有 ServerAction 接口的实现类只要和SpringBoot集成了,就会被自动注册,整合到业务处理流程当中。

由于是Demo程序,就没有添加数据库相关的代码。

你可能感兴趣的:(Java,EE)