Springboot项目Netty做服务端并自定义Gson配置类解析数据包

简述

Springboot项目中使用 Netty 作为服务端,接收并处理其他平台发送的 Json数据包,处理拆包、粘包及数据包中时间类型是 long 类型需转成 ***Date***的情况。

项目流程

  1. 启动项目,开启Netty服务端口11111
  2. 加载Bean
  3. 本地开启socket tool,模拟发送惊悚数据包
  4. Netty解析json包,处理特殊情况,例如 拆包粘包
  5. Gson按照配置文件处理json文件,并转换成JavaBeen
  6. 入库。

项目结构(下载即可:传送门)

Springboot项目Netty做服务端并自定义Gson配置类解析数据包_第1张图片
pom.xml



    4.0.0

    cn.angus.demo
    externalDataConnector
    1.0-SNAPSHOT

    
        UTF-8
        1.8
    

    
        org.springframework.boot
        spring-boot-starter-parent
        1.5.6.RELEASE
    

    
        
            org.springframework.boot
            spring-boot-starter
            1.5.4.RELEASE
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
            org.jboss.netty
            netty
            3.2.10.Final
        
        
            net.sf.json-lib
            json-lib
            2.4
            jdk15
        
        
            org.projectlombok
            lombok
            1.16.18
        
        
            io.springfox
            springfox-swagger2
            2.7.0
        
        
            io.springfox
            springfox-swagger-ui
            2.7.0
        
        
            commons-codec
            commons-codec
            1.7
        
        
            com.google.code.gson
            gson
            2.8.1
        
		
            org.springframework.data
            spring-data-mongodb
            1.10.6.RELEASE
        
        
            mysql
            mysql-connector-java
            5.1.21
        
        
            org.springframework.boot
            spring-boot-starter-data-jpa
            1.5.6.RELEASE
        
        
            org.apache.httpcomponents
            fluent-hc
            4.5.3
        
        
            io.netty
            netty-all
            4.1.30.Final
        
    

    
    
        
            org.springframework.boot
            spring-boot-maven-plugin
            
                cn.angus.demo.Application
            
        
    
    
        
            src/main/resources
            true
        
    



Listener

package cn.angus.demo.listener;

import cn.angus.demo.consts.Ports;
import cn.angus.demo.handler.VehicleGasSyncHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.EventExecutorGroup;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import io.netty.handler.codec.json.JsonObjectDecoder;
import javax.annotation.PostConstruct;

@Component
@Slf4j
public class VehicleGasSyncListener {
    @Autowired
    private VehicleGasSyncHandler vehicleGasSyncHandler;

    @PostConstruct
    private void startNettyServerAsync(){
        new Thread(this::startNettyServer).start();
    }

    private void startNettyServer(){
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        final EventExecutorGroup businessGroup = new DefaultEventExecutorGroup(10);
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childHandler(new ChannelInitializer() {
                        @Override
                        protected void initChannel(Channel channel) throws Exception {
                        	// 设置连接超时时间(很重要)
                            channel.pipeline().addLast("readtime",new ReadTimeoutHandler(60));
                            // 解决粘包问题
                            channel.pipeline().addLast(new JsonObjectDecoder());
                            channel.pipeline().addLast(businessGroup, "executer", vehicleGasSyncHandler);
                        }
                    });
            ChannelFuture f = b.bind(Ports.VEHICLE_GAS_PORT).sync();
            f.channel().closeFuture().sync();
        } catch(Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

Handler

package cn.angus.demo.handler;

import cn.angus.demo.dao.SpotDao;
import cn.angus.demo.domain.Request;
import cn.angus.demo.domain.Spot;
import com.google.gson.Gson;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.nio.charset.Charset;

@Component
@ChannelHandler.Sharable
@Slf4j
public class VehicleGasSyncHandler extends ChannelInboundHandlerAdapter {

    private final Gson gson;

    @Autowired
    private SpotDao spotDao;

    private static final String SPOT = "spot";

    @Autowired
    public VehicleGasSyncHandler(Gson gson) {
        this.gson = gson;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg){
        ByteBuf m = (ByteBuf) msg;
        String rawMsg = m.toString(Charset.forName("utf-8"));
        log.info("VehicleGasSyncHandler Receive message: " +  rawMsg);
        if (rawMsg.length() == 0 || rawMsg.length() -1 != rawMsg.lastIndexOf("}"))
            return;
        String response = persistAndResponse(rawMsg);
        if (response != null) {
            ctx.writeAndFlush(Unpooled.wrappedBuffer(response.getBytes()))
                    .addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE)
                    .addListener((ChannelFutureListener) channelFuture -> log.info("VehicleGasSyncHandler 成功发送响应{}", response));
            m.release();
        }
    }

    private String persistAndResponse(String rawMsg) {
        try {
            gson.fromJson(rawMsg, Spot.class);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        // 省略返回结果。
        return "";
    }

}

GsonConfig.java

package cn.angus.demo.config;

import com.google.gson.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Date;

@Configuration
public class GsonConfig {
    @Bean
    public Gson gson(){
        return new GsonBuilder()
                .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
                // Date 类型适配器
                .registerTypeAdapter(Date.class, (JsonDeserializer) (json, typeOfT, context) -> new Date(json.getAsJsonPrimitive().getAsLong()))
                .registerTypeAdapter(Date.class, (JsonSerializer) (date, type, jsonSerializationContext) -> new JsonPrimitive(date.getTime()))
                .create();
    }
}

实体类(createTime:将long转成Date)

@Data
@Document(collection = "vehicle_spot")
@JsonIgnoreProperties(ignoreUnknown = true)
public class Spot {
    @Id
    private String id;
    private String area;
    private String address;
    private String longitude;
    private String latitude;
    private Double slope;
    private Date createTime;
    private Boolean inUse;
    private Integer fuelTypeId;
    private String code;
}

Ports.java

public class Ports {
    public static final int VEHICLE_GAS_PORT = 11111;
}

测试用例:

{
    "_id" : "297e0587671b749801671b754b020000",
    "area" : "京",
    "address" : "北京",
    "longitude" : "489",
    "latitude" : "125",
    "slope" : 5.0,
    "create_time" :1544756993000,
    "fuel_type_id" : 1,
    "code" : "testCode"
}

mongo 入库:
Springboot项目Netty做服务端并自定义Gson配置类解析数据包_第2张图片
项目下载: https://download.csdn.net/download/qq_35974759/10850042
下载后导入到Idea中,配置maven,修改application-dev中的数据库地址改为你的,启动项目,利用SocketTool模拟发送数据包。

你可能感兴趣的:(Spring)