Springboot專案Netty做服務端並自定義Gson配置類解析資料包
阿新 • • 發佈:2018-12-18
簡述
Springboot專案中使用 Netty 作為服務端,接收並處理其他平臺傳送的 Json資料包,處理拆包、粘包及資料包中時間型別是 long 型別需轉成 ***Date***的情況。
專案流程
- 啟動專案,開啟Netty服務埠11111
- 載入Bean
- 本地開啟socket tool,模擬傳送驚悚資料包
- Netty解析json包,處理特殊情況,例如 拆包、粘包。
- Gson按照配置檔案處理json檔案,並轉換成JavaBeen
- 入庫。
專案結構(下載即可:傳送門)
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.angus.demo</groupId> <artifactId>externalDataConnector</artifactId> <version>1.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.6.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>1.5.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.jboss.netty</groupId> <artifactId>netty</artifactId> <version>3.2.10.Final</version> </dependency> <dependency> <groupId>net.sf.json-lib</groupId> <artifactId>json-lib</artifactId> <version>2.4</version> <classifier>jdk15</classifier> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.18</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.7</version> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.1</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-mongodb</artifactId> <version>1.10.6.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.21</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <version>1.5.6.RELEASE</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>fluent-hc</artifactId> <version>4.5.3</version> </dependency> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.30.Final</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>cn.angus.demo.Application</mainClass> </configuration> </plugin> </plugins> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> </build> </project>
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<Date>) (json, typeOfT, context) -> new Date(json.getAsJsonPrimitive().getAsLong()))
.registerTypeAdapter(Date.class, (JsonSerializer<Date>) (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 入庫:
專案下載: https://download.csdn.net/download/qq_35974759/10850042
下載後匯入到Idea中,配置maven,修改application-dev中的資料庫地址改為你的,啟動專案,利用SocketTool模擬傳送資料包。