├── README.md
├── img
└── proxy.png
├── netty-proxy
├── pom.xml
└── src
│ └── main
│ └── java
│ └── com
│ └── liukai
│ └── netty
│ ├── HttpServerBootStrap.java
│ ├── data
│ └── JedisClient.java
│ ├── handle
│ └── HttpHandle.java
│ ├── proxyclient
│ ├── HttpProxyClientHandle.java
│ └── HttpProxyInitializer.java
│ ├── serializer
│ ├── Serializer.java
│ └── impl
│ │ └── JSONSerializer.java
│ └── service
│ ├── OtherService.java
│ ├── RouterServerService.java
│ ├── UserClientService.java
│ └── impl
│ ├── OtherServiceImpl.java
│ ├── RouterServerServiceImpl.java
│ └── UserClientServiceImpl.java
└── spring-netty-proxy
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── spring
│ │ └── netty
│ │ └── springnetty
│ │ ├── SpringNettyApplication.java
│ │ ├── bootstrap
│ │ └── HttpServer.java
│ │ ├── config
│ │ └── BeanConfig.java
│ │ ├── handler
│ │ └── HttpHandle.java
│ │ ├── proxy
│ │ ├── HttpProxyClientHandle.java
│ │ └── HttpProxyInitializer.java
│ │ ├── serializer
│ │ ├── Serializer.java
│ │ └── impl
│ │ │ └── JSONSerializer.java
│ │ ├── service
│ │ ├── OtherService.java
│ │ ├── RouterService.java
│ │ ├── UserService.java
│ │ └── impl
│ │ │ ├── OtherServiceImpl.java
│ │ │ ├── RouterServiceImpl.java
│ │ │ └── UserServiceImpl.java
│ │ └── value
│ │ └── Values.java
└── resources
│ ├── application.properties
│ └── netty.properties
└── test
└── java
└── com
└── spring
└── netty
└── springnetty
└── SpringNettyApplicationTests.java
/README.md:
--------------------------------------------------------------------------------
1 | # netty-proxy
2 | netty实现的http动态代理服务器
3 |
4 |
5 |
6 | # 1.需求
7 |
8 | 通过app来管理家用路由器,路由器是经过定制的路由器系统里搭载着一个http服务,通过移动端访问路由器可对家庭中其他嵌入式设备进行管理。在家中可直接连接路由器即可,如不在 在家中就无法连接到路由器。要解决这个问题就需要一个远程服务器来做代理,移动端通过远程代理服务器访问路由器端。
9 |
10 | # 2.设计
11 |
12 | 因为在公网中一个设备的ip可能是经常变化的,所以不能用ip标识一个设备是不可靠的,在后台数据库设计中将用户账号与路由器mac地址绑定,或者说路由器的唯一标识,在后期中可具体设计。此项目是负责动态代理服务器模块,路由器向动态代理服务器发送心跳,代理服务器吧路由器的唯一标识做为key,把ip和端口做为value存储到redis,路由器每三十秒向代理服务器发送一次心跳保活,若超过三十秒则认为远程主机不可用。移动端用户登陆后,通过账号获得所对应的路由器标识,每次发送请求携带路由器的key,代理服务器通过路由器key查询到远程主机,建立连接通道。原理如图:
13 |
14 |
15 |
16 | 
17 |
18 |
--------------------------------------------------------------------------------
/img/proxy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liukai90/netty-proxy/fe0e9004a74d3de96c46b064873a14403c4debae/img/proxy.png
--------------------------------------------------------------------------------
/netty-proxy/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.liukai.netty
8 | netty
9 | 1.0-SNAPSHOT
10 |
11 |
12 |
13 | io.netty
14 | netty-all
15 | 4.1.32.Final
16 |
17 |
18 |
19 | ch.qos.logback
20 | logback-classic
21 | 1.2.3
22 |
23 |
24 | com.alibaba
25 | fastjson
26 | 1.2.56
27 |
28 |
29 | org.projectlombok
30 | lombok
31 | 1.18.6
32 |
33 |
34 |
35 |
36 | redis.clients
37 | jedis
38 | 2.9.0
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/netty-proxy/src/main/java/com/liukai/netty/HttpServerBootStrap.java:
--------------------------------------------------------------------------------
1 | package com.liukai.netty;
2 |
3 | import com.liukai.netty.handle.HttpHandle;
4 | import io.netty.bootstrap.ServerBootstrap;
5 | import io.netty.channel.*;
6 | import io.netty.channel.nio.NioEventLoopGroup;
7 | import io.netty.channel.socket.nio.NioServerSocketChannel;
8 | import io.netty.handler.codec.http.HttpObjectAggregator;
9 | import io.netty.handler.codec.http.HttpRequestDecoder;
10 | import io.netty.handler.codec.http.HttpResponseEncoder;
11 | import io.netty.handler.codec.http.HttpServerCodec;
12 | import io.netty.handler.logging.LogLevel;
13 | import io.netty.handler.logging.LoggingHandler;
14 |
15 | public class HttpServerBootStrap {
16 | private static final int PORT = 8080;
17 |
18 | public static void main(String[] args) {
19 | EventLoopGroup bossGroup = new NioEventLoopGroup(1);
20 | EventLoopGroup workerGroup = new NioEventLoopGroup();
21 | try{
22 | ServerBootstrap sb = new ServerBootstrap();
23 | sb.group(bossGroup,workerGroup).
24 | channel(NioServerSocketChannel.class).
25 | handler(new LoggingHandler(LogLevel.INFO)).
26 | option(ChannelOption.SO_BACKLOG,100).
27 | childOption(ChannelOption.TCP_NODELAY,true).
28 | childOption(ChannelOption.SO_KEEPALIVE,true).
29 | childHandler(new ChannelInitializer() {
30 | protected void initChannel(Channel channel) throws Exception {
31 | ChannelPipeline channelPipeline = channel.pipeline();
32 | channelPipeline.addLast(new HttpServerCodec());
33 | channelPipeline.addLast(new HttpObjectAggregator(1048576*10));
34 | channelPipeline.addLast(new HttpHandle());
35 | }
36 | });
37 | Channel channel = sb.bind(PORT).sync().channel();
38 | channel.closeFuture().sync();
39 |
40 | } catch (InterruptedException e) {
41 | e.printStackTrace();
42 | } finally {
43 | bossGroup.shutdownGracefully();
44 | workerGroup.shutdownGracefully();
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/netty-proxy/src/main/java/com/liukai/netty/data/JedisClient.java:
--------------------------------------------------------------------------------
1 | package com.liukai.netty.data;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 | import redis.clients.jedis.Jedis;
6 | import redis.clients.jedis.JedisPool;
7 |
8 | import java.util.Map;
9 |
10 | public class JedisClient {
11 | private final Logger logger = LoggerFactory.getLogger(JedisClient.class);
12 |
13 | private JedisPool jedisPool = new JedisPool();
14 |
15 | public String hget(){
16 | Jedis jedis = this.jedisPool.getResource();
17 | String address = jedis.hget("server","address");
18 | jedis.close();
19 | return address;
20 | }
21 |
22 | public Map hGetAll(String key){
23 | Jedis jedis = this.jedisPool.getResource();
24 | Map address = jedis.hgetAll(key);
25 | jedis.close();
26 | return address;
27 | }
28 |
29 | public void hset(String key, String host, String port){
30 |
31 | Jedis jedis = jedisPool.getResource();
32 | jedis.hset(key,"host", host);
33 | jedis.hset(key,"port", port);
34 | jedis.expire(key,30);
35 | jedis.close();
36 |
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/netty-proxy/src/main/java/com/liukai/netty/handle/HttpHandle.java:
--------------------------------------------------------------------------------
1 | package com.liukai.netty.handle;
2 |
3 | import com.liukai.netty.service.OtherService;
4 | import com.liukai.netty.service.RouterServerService;
5 | import com.liukai.netty.service.UserClientService;
6 | import com.liukai.netty.service.impl.OtherServiceImpl;
7 | import com.liukai.netty.service.impl.RouterServerServiceImpl;
8 | import com.liukai.netty.service.impl.UserClientServiceImpl;
9 | import io.netty.channel.*;
10 | import io.netty.handler.codec.http.*;
11 | import org.slf4j.Logger;
12 | import org.slf4j.LoggerFactory;
13 |
14 | @ChannelHandler.Sharable
15 | public class HttpHandle extends ChannelInboundHandlerAdapter {
16 |
17 | private final Logger logger = LoggerFactory.getLogger(HttpHandle.class);
18 | private RouterServerService routerServerService = RouterServerServiceImpl.getInstance();
19 | private UserClientService userClientService = UserClientServiceImpl.getInstance();
20 | private OtherService otherService = OtherServiceImpl.getInstance();
21 |
22 |
23 |
24 | @Override
25 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
26 | // super.channelRead(ctx, msg);
27 | logger.info(msg.toString());
28 | FullHttpRequest request = null;
29 | if (msg instanceof FullHttpRequest){
30 | request = (FullHttpRequest) msg;
31 | }
32 | int client = request.headers().getInt("client",0);
33 | logger.info("client:"+client);
34 | if (client == 1){
35 | logger.info("client call");
36 | userClientService.proxy(ctx,request);
37 | }else if (client == 2){
38 | logger.info("server call");
39 | routerServerService.keepAlive(ctx,request);
40 | }else {
41 | logger.info("other call");
42 | otherService.other(ctx,request);
43 | }
44 |
45 | }
46 |
47 | @Override
48 | public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
49 | ctx.flush();
50 | }
51 |
52 | @Override
53 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
54 | cause.printStackTrace();
55 | ctx.close();
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/netty-proxy/src/main/java/com/liukai/netty/proxyclient/HttpProxyClientHandle.java:
--------------------------------------------------------------------------------
1 | package com.liukai.netty.proxyclient;
2 |
3 | import io.netty.channel.Channel;
4 | import io.netty.channel.ChannelHandlerContext;
5 | import io.netty.channel.ChannelInboundHandlerAdapter;
6 | import io.netty.handler.codec.http.FullHttpResponse;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 |
10 |
11 | public class HttpProxyClientHandle extends ChannelInboundHandlerAdapter {
12 |
13 | private final Logger logger = LoggerFactory.getLogger(HttpProxyClientHandle.class);
14 |
15 | private Channel clientChannel;
16 |
17 | public HttpProxyClientHandle(Channel clientChannel) {
18 | this.clientChannel = clientChannel;
19 | }
20 |
21 |
22 | @Override
23 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
24 | logger.info("客户端消息:"+msg.toString());
25 | FullHttpResponse response = (FullHttpResponse) msg;
26 |
27 | clientChannel.writeAndFlush(response);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/netty-proxy/src/main/java/com/liukai/netty/proxyclient/HttpProxyInitializer.java:
--------------------------------------------------------------------------------
1 | package com.liukai.netty.proxyclient;
2 |
3 | import io.netty.channel.Channel;
4 | import io.netty.channel.ChannelInitializer;
5 | import io.netty.handler.codec.http.HttpClientCodec;
6 | import io.netty.handler.codec.http.HttpObjectAggregator;
7 |
8 | public class HttpProxyInitializer extends ChannelInitializer {
9 |
10 | private Channel clientChannel;
11 |
12 | public HttpProxyInitializer(Channel clientChannel) {
13 | this.clientChannel = clientChannel;
14 | }
15 |
16 | @Override
17 | protected void initChannel(Channel ch) throws Exception {
18 | ch.pipeline().addLast(new HttpClientCodec());
19 | ch.pipeline().addLast(new HttpObjectAggregator(1048576*10));
20 | ch.pipeline().addLast(new HttpProxyClientHandle(clientChannel));
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/netty-proxy/src/main/java/com/liukai/netty/serializer/Serializer.java:
--------------------------------------------------------------------------------
1 | package com.liukai.netty.serializer;
2 |
3 | public interface Serializer {
4 |
5 | byte[] serialize(Object object);
6 |
7 | /**
8 | * 二进制转换成 java 对象
9 | */
10 | T deserialize(Class clazz, byte[] bytes);
11 | }
12 |
--------------------------------------------------------------------------------
/netty-proxy/src/main/java/com/liukai/netty/serializer/impl/JSONSerializer.java:
--------------------------------------------------------------------------------
1 | package com.liukai.netty.serializer.impl;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.liukai.netty.serializer.Serializer;
5 |
6 |
7 | public class JSONSerializer implements Serializer {
8 |
9 | public byte[] serialize(Object object) {
10 | return JSON.toJSONBytes(object);
11 | }
12 |
13 | public T deserialize(Class clazz, byte[] bytes) {
14 | return JSON.parseObject(bytes,clazz);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/netty-proxy/src/main/java/com/liukai/netty/service/OtherService.java:
--------------------------------------------------------------------------------
1 | package com.liukai.netty.service;
2 |
3 | import io.netty.channel.ChannelHandlerContext;
4 | import io.netty.handler.codec.http.FullHttpRequest;
5 |
6 | public interface OtherService {
7 |
8 | void other(ChannelHandlerContext ctx, FullHttpRequest msg);
9 | }
10 |
--------------------------------------------------------------------------------
/netty-proxy/src/main/java/com/liukai/netty/service/RouterServerService.java:
--------------------------------------------------------------------------------
1 | package com.liukai.netty.service;
2 |
3 | import io.netty.channel.ChannelHandlerContext;
4 | import io.netty.handler.codec.http.FullHttpRequest;
5 |
6 | public interface RouterServerService {
7 | void keepAlive(final ChannelHandlerContext ctx, final FullHttpRequest msg);
8 | }
9 |
--------------------------------------------------------------------------------
/netty-proxy/src/main/java/com/liukai/netty/service/UserClientService.java:
--------------------------------------------------------------------------------
1 | package com.liukai.netty.service;
2 |
3 | import io.netty.channel.ChannelHandlerContext;
4 | import io.netty.handler.codec.http.FullHttpRequest;
5 |
6 | public interface UserClientService {
7 |
8 | void proxy(final ChannelHandlerContext ctx, final FullHttpRequest msg);
9 | }
10 |
--------------------------------------------------------------------------------
/netty-proxy/src/main/java/com/liukai/netty/service/impl/OtherServiceImpl.java:
--------------------------------------------------------------------------------
1 | package com.liukai.netty.service.impl;
2 |
3 | import com.liukai.netty.serializer.impl.JSONSerializer;
4 | import com.liukai.netty.service.OtherService;
5 | import io.netty.buffer.Unpooled;
6 | import io.netty.channel.ChannelFutureListener;
7 | import io.netty.channel.ChannelHandlerContext;
8 | import io.netty.handler.codec.http.*;
9 | import io.netty.util.ReferenceCountUtil;
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 |
13 | import static com.sun.deploy.net.HttpRequest.CONTENT_TYPE;
14 | import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
15 | import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE;
16 | import static io.netty.handler.codec.http.HttpHeaders.Names.*;
17 | import static io.netty.handler.codec.http.HttpResponseStatus.OK;
18 | import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
19 |
20 | public class OtherServiceImpl implements OtherService {
21 |
22 | private final Logger logger = LoggerFactory.getLogger(OtherServiceImpl.class);
23 |
24 | private static OtherService otherService = null;
25 |
26 | private OtherServiceImpl(){
27 |
28 | }
29 |
30 | public void other(ChannelHandlerContext ctx, FullHttpRequest msg) {
31 |
32 | JSONSerializer jsonSerializer = new JSONSerializer();
33 |
34 | FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(jsonSerializer.serialize("拒绝访问")));
35 | // response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");
36 | response.headers().set(CONTENT_TYPE, "application/json;charset=UTF-8");
37 | String host = msg.headers().get("Host");
38 | logger.info("host:"+host);
39 | //允许跨域访问
40 | response.headers().set(ACCESS_CONTROL_ALLOW_ORIGIN,"*");
41 | response.headers().set(ACCESS_CONTROL_ALLOW_HEADERS,"*");
42 | response.headers().set(ACCESS_CONTROL_ALLOW_METHODS,"GET, POST, PUT,DELETE");
43 | response.headers().set(ACCESS_CONTROL_ALLOW_CREDENTIALS,"true");
44 |
45 | response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes());
46 |
47 | boolean keepAlive = HttpUtil.isKeepAlive(msg);
48 | if (!keepAlive) {
49 | logger.info("!keepAlive");
50 | ctx.write(response).addListener(ChannelFutureListener.CLOSE);
51 | } else {
52 | logger.info("keepAlive");
53 | response.headers().set(CONNECTION, KEEP_ALIVE);
54 | ctx.write(response);
55 | }
56 | ctx.flush();
57 | }
58 |
59 | public static OtherService getInstance(){
60 | if (otherService == null){
61 | otherService = new OtherServiceImpl();
62 | }
63 | return otherService;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/netty-proxy/src/main/java/com/liukai/netty/service/impl/RouterServerServiceImpl.java:
--------------------------------------------------------------------------------
1 | package com.liukai.netty.service.impl;
2 |
3 |
4 | import com.liukai.netty.data.JedisClient;
5 | import com.liukai.netty.serializer.impl.JSONSerializer;
6 | import com.liukai.netty.service.RouterServerService;
7 | import io.netty.buffer.Unpooled;
8 | import io.netty.channel.ChannelFutureListener;
9 | import io.netty.channel.ChannelHandlerContext;
10 | import io.netty.handler.codec.http.DefaultFullHttpResponse;
11 | import io.netty.handler.codec.http.FullHttpRequest;
12 | import io.netty.handler.codec.http.FullHttpResponse;
13 | import io.netty.handler.codec.http.HttpUtil;
14 | import io.netty.util.CharsetUtil;
15 | import org.slf4j.Logger;
16 | import org.slf4j.LoggerFactory;
17 |
18 | import java.util.Map;
19 |
20 | import static com.sun.deploy.net.HttpRequest.CONTENT_TYPE;
21 | import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
22 | import static io.netty.handler.codec.http.HttpHeaderValues.CHARSET;
23 | import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE;
24 | import static io.netty.handler.codec.http.HttpHeaders.Names.*;
25 | import static io.netty.handler.codec.http.HttpResponseStatus.OK;
26 | import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
27 |
28 | public class RouterServerServiceImpl implements RouterServerService {
29 |
30 | private final Logger logger = LoggerFactory.getLogger(RouterServerServiceImpl.class);
31 |
32 | private static RouterServerService routerServerService = null;
33 |
34 | private JedisClient jedisClient = new JedisClient();
35 |
36 | public void keepAlive(ChannelHandlerContext ctx,FullHttpRequest msg) {
37 | String key = msg.headers().get("key");
38 | String address = msg.headers().get("Host");
39 | String s[] = address.split(":");
40 | logger.info("key="+key+";"+"address="+address+";"+"host:"+s[0]+";"+"port:"+s[1]+";");
41 | jedisClient.hset(key, s[0], s[1]);
42 |
43 | Map map = jedisClient.hGetAll(key);
44 |
45 | JSONSerializer jsonSerializer = new JSONSerializer();
46 | byte[] content = jsonSerializer.serialize(map);
47 |
48 | FullHttpResponse response =
49 | new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(content));
50 | //允许跨域访问
51 | response.headers().set(CONTENT_TYPE, "application/json;charset=UTF-8; charset=UTF-8");
52 | response.headers().set(ACCESS_CONTROL_ALLOW_ORIGIN,"*");;
53 | response.headers().set(ACCESS_CONTROL_ALLOW_HEADERS,"*");
54 | response.headers().set(ACCESS_CONTROL_ALLOW_CREDENTIALS,"true");
55 | response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes());
56 |
57 | boolean keepAlive = HttpUtil.isKeepAlive(msg);
58 | if (!keepAlive) {
59 | ctx.write(response).addListener(ChannelFutureListener.CLOSE);
60 | } else {
61 | response.headers().set(CONNECTION, KEEP_ALIVE);
62 | ctx.write(response);
63 | }
64 | }
65 |
66 | public static RouterServerService getInstance(){
67 | if (routerServerService == null){
68 | routerServerService = new RouterServerServiceImpl();
69 | }
70 | return routerServerService;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/netty-proxy/src/main/java/com/liukai/netty/service/impl/UserClientServiceImpl.java:
--------------------------------------------------------------------------------
1 | package com.liukai.netty.service.impl;
2 |
3 | import com.liukai.netty.data.JedisClient;
4 | import com.liukai.netty.proxyclient.HttpProxyInitializer;
5 | import com.liukai.netty.service.UserClientService;
6 | import io.netty.bootstrap.Bootstrap;
7 | import io.netty.channel.ChannelFuture;
8 | import io.netty.channel.ChannelFutureListener;
9 | import io.netty.channel.ChannelHandlerContext;
10 | import io.netty.handler.codec.http.FullHttpRequest;
11 | import org.slf4j.Logger;
12 | import org.slf4j.LoggerFactory;
13 |
14 | import java.util.Map;
15 |
16 | public class UserClientServiceImpl implements UserClientService {
17 |
18 | private final Logger logger = LoggerFactory.getLogger(UserClientServiceImpl.class);
19 |
20 | private static UserClientServiceImpl userClientService = null;
21 | private JedisClient jedisClient = new JedisClient();
22 |
23 | private UserClientServiceImpl(){
24 |
25 | }
26 | public void proxy(final ChannelHandlerContext ctx,final FullHttpRequest msg) {
27 |
28 | String key = msg.headers().get("key");
29 | Map address = jedisClient.hGetAll(key);
30 | String host = address.get("host");
31 | int port = Integer.parseInt(address.get("port"));
32 | logger.info(host+":"+port);
33 |
34 | //连接至目标服务器
35 | Bootstrap bootstrap = new Bootstrap();
36 | bootstrap.group(ctx.channel().eventLoop()) // 注册线程池
37 | .channel(ctx.channel().getClass()) // 使用NioSocketChannel来作为连接用的channel类
38 | .handler(new HttpProxyInitializer(ctx.channel()));
39 |
40 | ChannelFuture cf = bootstrap.connect(host, port);
41 | cf.addListener(new ChannelFutureListener() {
42 | public void operationComplete(ChannelFuture future) throws Exception {
43 | if (future.isSuccess()) {
44 |
45 | future.channel().writeAndFlush(msg);
46 | } else {
47 | ctx.channel().close();
48 | }
49 | }
50 | });
51 | }
52 |
53 | public static UserClientServiceImpl getInstance(){
54 | if (userClientService == null){
55 | userClientService = new UserClientServiceImpl();
56 | }
57 | return userClientService;
58 | }
59 |
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/spring-netty-proxy/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 2.1.3.RELEASE
9 |
10 |
11 | com.spring.netty
12 | spring-netty
13 | 0.0.1-SNAPSHOT
14 | spring-netty
15 | Demo project for Spring Boot
16 |
17 |
18 | 1.8
19 |
20 |
21 |
22 |
23 | org.springframework.boot
24 | spring-boot-starter-data-redis
25 |
26 |
27 |
28 | org.projectlombok
29 | lombok
30 | true
31 |
32 |
33 | org.springframework.boot
34 | spring-boot-starter-test
35 | test
36 |
37 |
38 | io.netty
39 | netty-all
40 | 4.1.32.Final
41 |
42 |
43 | com.alibaba
44 | fastjson
45 | 1.2.56
46 |
47 |
48 |
49 |
50 |
51 |
52 | org.springframework.boot
53 | spring-boot-maven-plugin
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/spring-netty-proxy/src/main/java/com/spring/netty/springnetty/SpringNettyApplication.java:
--------------------------------------------------------------------------------
1 | package com.spring.netty.springnetty;
2 |
3 | import com.spring.netty.springnetty.bootstrap.HttpServer;
4 | import org.springframework.boot.SpringApplication;
5 | import org.springframework.boot.autoconfigure.SpringBootApplication;
6 | import org.springframework.context.ConfigurableApplicationContext;
7 |
8 | @SpringBootApplication
9 | public class SpringNettyApplication {
10 |
11 | public static void main(String[] args) {
12 | ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(SpringNettyApplication.class, args);
13 | HttpServer httpServer = configurableApplicationContext.getBean(HttpServer.class);
14 | httpServer.serverStart();
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/spring-netty-proxy/src/main/java/com/spring/netty/springnetty/bootstrap/HttpServer.java:
--------------------------------------------------------------------------------
1 | package com.spring.netty.springnetty.bootstrap;
2 |
3 | import io.netty.bootstrap.ServerBootstrap;
4 | import io.netty.channel.Channel;
5 | import lombok.Data;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.beans.factory.annotation.Qualifier;
8 | import org.springframework.stereotype.Component;
9 |
10 | import javax.annotation.PostConstruct;
11 |
12 | @Component
13 | @Data
14 | public class HttpServer {
15 |
16 | @Autowired
17 | @Qualifier(value = "serverBootstrap")
18 | private ServerBootstrap serverBootstrap;
19 |
20 | @Autowired
21 | @Qualifier(value = "port")
22 | private int port;
23 |
24 | public void serverStart(){
25 | try {
26 | Channel channel = serverBootstrap.bind(port).sync().channel();
27 | channel.closeFuture().sync();
28 | } catch (InterruptedException e) {
29 | e.printStackTrace();
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/spring-netty-proxy/src/main/java/com/spring/netty/springnetty/config/BeanConfig.java:
--------------------------------------------------------------------------------
1 | package com.spring.netty.springnetty.config;
2 |
3 | import com.spring.netty.springnetty.handler.HttpHandle;
4 | import io.netty.bootstrap.ServerBootstrap;
5 | import io.netty.channel.Channel;
6 | import io.netty.channel.ChannelInitializer;
7 | import io.netty.channel.ChannelOption;
8 | import io.netty.channel.ChannelPipeline;
9 | import io.netty.channel.nio.NioEventLoopGroup;
10 | import io.netty.channel.socket.nio.NioServerSocketChannel;
11 | import io.netty.handler.codec.http.HttpObjectAggregator;
12 | import io.netty.handler.codec.http.HttpServerCodec;
13 | import io.netty.handler.logging.LogLevel;
14 | import io.netty.handler.logging.LoggingHandler;
15 | import org.springframework.beans.factory.annotation.Autowired;
16 | import org.springframework.beans.factory.annotation.Value;
17 | import org.springframework.context.annotation.Bean;
18 | import org.springframework.context.annotation.Configuration;
19 | import org.springframework.context.annotation.PropertySource;
20 |
21 | @Configuration
22 | @PropertySource(value = "classpath:/netty.properties")
23 | public class BeanConfig {
24 |
25 | @Value("${boss.thread.count}")
26 | private int bossCount;
27 |
28 | @Value("${worker.thread.count}")
29 | private int workerCount;
30 |
31 | @Value("${server.port}")
32 | private int serverPort;
33 |
34 | @Value("${so.keepalive}")
35 | private boolean keepAlive;
36 |
37 | @Value("${tcp.nodelay}")
38 | private boolean tcpNodelay;
39 |
40 | @Value("${so.backlog}")
41 | private int backlog;
42 |
43 | @Value("${maxContentLength}")
44 | private int maxContentLength;
45 |
46 |
47 | @Autowired
48 | private HttpHandle httpHandle;
49 |
50 | @Bean(name = "port")
51 | public Integer port(){
52 | return serverPort;
53 | }
54 |
55 | @Bean(name = "bossGroup",destroyMethod = "shutdownGracefully")
56 | public NioEventLoopGroup bossGroup(){
57 | return new NioEventLoopGroup(bossCount);
58 | }
59 |
60 | @Bean(name = "workerGroup",destroyMethod = "shutdownGracefully")
61 | public NioEventLoopGroup workerGroup(){
62 | return new NioEventLoopGroup(workerCount);
63 | }
64 |
65 | @Bean(name = "serverCodec")
66 | public HttpServerCodec serverCodec(){
67 | return new HttpServerCodec();
68 | }
69 |
70 | @Bean(name = "aggregator")
71 | public HttpObjectAggregator aggregator(){
72 | return new HttpObjectAggregator(maxContentLength);
73 | }
74 |
75 | @Bean(name = "channelInitializer")
76 | public ChannelInitializer channelInitializer(){
77 | return new ChannelInitializer() {
78 | @Override
79 | protected void initChannel(Channel channel) throws Exception {
80 | ChannelPipeline channelPipeline = channel.pipeline();
81 | channelPipeline.addLast(serverCodec());
82 | channelPipeline.addLast(aggregator());
83 | channelPipeline.addLast(httpHandle);
84 | }
85 | };
86 | }
87 |
88 | @Bean(name = "serverBootstrap")
89 | public ServerBootstrap serverBootstrap(){
90 |
91 | return new ServerBootstrap().group(bossGroup(),workerGroup()).
92 | channel(NioServerSocketChannel.class).
93 | handler(new LoggingHandler(LogLevel.INFO)).
94 | childHandler(channelInitializer()).
95 | option(ChannelOption.SO_BACKLOG,backlog).
96 | childOption(ChannelOption.TCP_NODELAY,tcpNodelay).
97 | childOption(ChannelOption.SO_KEEPALIVE,keepAlive);
98 |
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/spring-netty-proxy/src/main/java/com/spring/netty/springnetty/handler/HttpHandle.java:
--------------------------------------------------------------------------------
1 | package com.spring.netty.springnetty.handler;
2 |
3 | import com.spring.netty.springnetty.service.OtherService;
4 | import com.spring.netty.springnetty.service.RouterService;
5 | import com.spring.netty.springnetty.service.UserService;
6 | import com.spring.netty.springnetty.value.Values;
7 | import io.netty.channel.ChannelHandler;
8 | import io.netty.channel.ChannelHandlerContext;
9 | import io.netty.channel.ChannelInboundHandlerAdapter;
10 | import io.netty.handler.codec.http.FullHttpRequest;
11 | import org.slf4j.Logger;
12 | import org.slf4j.LoggerFactory;
13 | import org.springframework.beans.factory.annotation.Autowired;
14 | import org.springframework.stereotype.Component;
15 |
16 | @Component
17 | @ChannelHandler.Sharable
18 | public class HttpHandle extends ChannelInboundHandlerAdapter {
19 |
20 | private final Logger logger = LoggerFactory.getLogger(HttpHandle.class);
21 |
22 | @Autowired
23 | private RouterService routerService;
24 |
25 | @Autowired
26 | private UserService userService;
27 |
28 | @Autowired
29 | private OtherService otherService;
30 |
31 |
32 | @Override
33 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
34 |
35 | logger.info(msg.toString());
36 | FullHttpRequest request = null;
37 | if (msg instanceof FullHttpRequest){
38 | request = (FullHttpRequest) msg;
39 | }
40 | int client = Integer.parseInt(request.headers().get("client","0"));
41 | logger.info("client:"+client);
42 |
43 | if (client == Values.REQUEST_TYPE_APP){
44 | userService.proxy(ctx,request);
45 | }else if (client == Values.REQUEST_TYPE_ROUTER){
46 | routerService.keepAlive(ctx,request);
47 | }else {
48 | logger.error("非法请求");
49 | otherService.other(ctx,request);
50 | }
51 |
52 | }
53 |
54 | @Override
55 | public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
56 | ctx.flush();
57 | }
58 |
59 | @Override
60 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
61 | cause.printStackTrace();
62 | ctx.close();
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/spring-netty-proxy/src/main/java/com/spring/netty/springnetty/proxy/HttpProxyClientHandle.java:
--------------------------------------------------------------------------------
1 | package com.spring.netty.springnetty.proxy;
2 |
3 | import io.netty.channel.Channel;
4 | import io.netty.channel.ChannelHandlerContext;
5 | import io.netty.channel.ChannelInboundHandlerAdapter;
6 | import io.netty.handler.codec.http.FullHttpResponse;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 |
10 | public class HttpProxyClientHandle extends ChannelInboundHandlerAdapter {
11 |
12 | private final Logger logger = LoggerFactory.getLogger(HttpProxyClientHandle.class);
13 |
14 | private Channel clientChannel;
15 |
16 | public HttpProxyClientHandle(Channel clientChannel) {
17 | this.clientChannel = clientChannel;
18 | }
19 |
20 |
21 | @Override
22 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
23 | logger.info("客户端消息:"+msg.toString());
24 | FullHttpResponse response = (FullHttpResponse) msg;
25 | clientChannel.writeAndFlush(response);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/spring-netty-proxy/src/main/java/com/spring/netty/springnetty/proxy/HttpProxyInitializer.java:
--------------------------------------------------------------------------------
1 | package com.spring.netty.springnetty.proxy;
2 |
3 | import io.netty.channel.Channel;
4 | import io.netty.channel.ChannelInitializer;
5 | import io.netty.handler.codec.http.HttpClientCodec;
6 | import io.netty.handler.codec.http.HttpObjectAggregator;
7 | import org.springframework.stereotype.Component;
8 |
9 | public class HttpProxyInitializer extends ChannelInitializer {
10 | private Channel clientChannel;
11 |
12 | public HttpProxyInitializer(Channel clientChannel) {
13 | this.clientChannel = clientChannel;
14 | }
15 |
16 | @Override
17 | protected void initChannel(Channel ch) throws Exception {
18 | ch.pipeline().addLast(new HttpClientCodec());
19 | ch.pipeline().addLast(new HttpObjectAggregator(1048576*10));
20 | ch.pipeline().addLast(new HttpProxyClientHandle(clientChannel));
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/spring-netty-proxy/src/main/java/com/spring/netty/springnetty/serializer/Serializer.java:
--------------------------------------------------------------------------------
1 | package com.spring.netty.springnetty.serializer;
2 |
3 | public interface Serializer {
4 |
5 | byte[] serialize(Object object);
6 |
7 | /**
8 | * 二进制转换成 java 对象
9 | */
10 | T deserialize(Class clazz, byte[] bytes);
11 | }
12 |
--------------------------------------------------------------------------------
/spring-netty-proxy/src/main/java/com/spring/netty/springnetty/serializer/impl/JSONSerializer.java:
--------------------------------------------------------------------------------
1 | package com.spring.netty.springnetty.serializer.impl;
2 |
3 |
4 | import com.alibaba.fastjson.JSON;
5 | import com.spring.netty.springnetty.serializer.Serializer;
6 | import org.springframework.stereotype.Component;
7 |
8 | @Component
9 | public class JSONSerializer implements Serializer {
10 |
11 | public byte[] serialize(Object object) {
12 | return JSON.toJSONBytes(object);
13 | }
14 |
15 | public T deserialize(Class clazz, byte[] bytes) {
16 | return JSON.parseObject(bytes,clazz);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/spring-netty-proxy/src/main/java/com/spring/netty/springnetty/service/OtherService.java:
--------------------------------------------------------------------------------
1 | package com.spring.netty.springnetty.service;
2 |
3 | import io.netty.channel.ChannelHandlerContext;
4 | import io.netty.handler.codec.http.FullHttpRequest;
5 |
6 | public interface OtherService {
7 |
8 | void other(ChannelHandlerContext ctx, FullHttpRequest msg);
9 | }
10 |
--------------------------------------------------------------------------------
/spring-netty-proxy/src/main/java/com/spring/netty/springnetty/service/RouterService.java:
--------------------------------------------------------------------------------
1 | package com.spring.netty.springnetty.service;
2 |
3 | import io.netty.channel.ChannelHandlerContext;
4 | import io.netty.handler.codec.http.FullHttpRequest;
5 |
6 | public interface RouterService {
7 | void keepAlive(final ChannelHandlerContext ctx, final FullHttpRequest msg);
8 | }
9 |
--------------------------------------------------------------------------------
/spring-netty-proxy/src/main/java/com/spring/netty/springnetty/service/UserService.java:
--------------------------------------------------------------------------------
1 | package com.spring.netty.springnetty.service;
2 |
3 | import io.netty.channel.ChannelHandlerContext;
4 | import io.netty.handler.codec.http.FullHttpRequest;
5 |
6 | public interface UserService {
7 |
8 | void proxy(final ChannelHandlerContext ctx, final FullHttpRequest msg);
9 | }
10 |
--------------------------------------------------------------------------------
/spring-netty-proxy/src/main/java/com/spring/netty/springnetty/service/impl/OtherServiceImpl.java:
--------------------------------------------------------------------------------
1 | package com.spring.netty.springnetty.service.impl;
2 |
3 | import com.spring.netty.springnetty.serializer.impl.JSONSerializer;
4 | import com.spring.netty.springnetty.service.OtherService;
5 | import io.netty.buffer.Unpooled;
6 | import io.netty.channel.ChannelFutureListener;
7 | import io.netty.channel.ChannelHandlerContext;
8 | import io.netty.handler.codec.http.DefaultFullHttpResponse;
9 | import io.netty.handler.codec.http.FullHttpRequest;
10 | import io.netty.handler.codec.http.FullHttpResponse;
11 | import io.netty.handler.codec.http.HttpUtil;
12 | import org.slf4j.Logger;
13 | import org.slf4j.LoggerFactory;
14 | import org.springframework.beans.factory.annotation.Autowired;
15 | import org.springframework.stereotype.Component;
16 |
17 | import static com.sun.deploy.net.HttpRequest.CONTENT_TYPE;
18 | import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
19 | import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE;
20 | import static io.netty.handler.codec.http.HttpHeaders.Names.*;
21 | import static io.netty.handler.codec.http.HttpResponseStatus.OK;
22 | import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
23 |
24 | @Component
25 | public class OtherServiceImpl implements OtherService {
26 |
27 | private final Logger logger = LoggerFactory.getLogger(OtherServiceImpl.class);
28 |
29 | @Autowired
30 | private JSONSerializer jsonSerializer;
31 |
32 | public void other(ChannelHandlerContext ctx, FullHttpRequest msg) {
33 |
34 | FullHttpResponse response =
35 | new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(jsonSerializer.serialize("access denied")));
36 | response.headers().set(CONTENT_TYPE, "application/json;charset=UTF-8");
37 | String host = msg.headers().get("Host");
38 | logger.info("host:"+host);
39 | //允许跨域访问
40 | response.headers().set(ACCESS_CONTROL_ALLOW_ORIGIN,"*");
41 | response.headers().set(ACCESS_CONTROL_ALLOW_HEADERS,"*");
42 | response.headers().set(ACCESS_CONTROL_ALLOW_CREDENTIALS,"true");
43 | response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes());
44 |
45 | boolean keepAlive = HttpUtil.isKeepAlive(msg);
46 | if (!keepAlive) {
47 | logger.info("!keepAlive");
48 | ctx.write(response).addListener(ChannelFutureListener.CLOSE);
49 | } else {
50 | logger.info("keepAlive");
51 | response.headers().set(CONNECTION, KEEP_ALIVE);
52 | ctx.write(response);
53 | }
54 | ctx.flush();
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/spring-netty-proxy/src/main/java/com/spring/netty/springnetty/service/impl/RouterServiceImpl.java:
--------------------------------------------------------------------------------
1 | package com.spring.netty.springnetty.service.impl;
2 |
3 | import com.spring.netty.springnetty.serializer.impl.JSONSerializer;
4 | import com.spring.netty.springnetty.service.RouterService;
5 | import io.netty.buffer.Unpooled;
6 | import io.netty.channel.ChannelFutureListener;
7 | import io.netty.channel.ChannelHandlerContext;
8 | import io.netty.handler.codec.http.DefaultFullHttpResponse;
9 | import io.netty.handler.codec.http.FullHttpRequest;
10 | import io.netty.handler.codec.http.FullHttpResponse;
11 | import io.netty.handler.codec.http.HttpUtil;
12 | import org.slf4j.Logger;
13 | import org.slf4j.LoggerFactory;
14 | import org.springframework.beans.factory.annotation.Autowired;
15 | import org.springframework.data.redis.core.StringRedisTemplate;
16 | import org.springframework.stereotype.Component;
17 | import java.util.HashMap;
18 | import java.util.Map;
19 | import java.util.concurrent.TimeUnit;
20 |
21 | import static com.sun.deploy.net.HttpRequest.CONTENT_TYPE;
22 | import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
23 | import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE;
24 | import static io.netty.handler.codec.http.HttpHeaders.Names.*;
25 | import static io.netty.handler.codec.http.HttpResponseStatus.OK;
26 | import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
27 |
28 | @Component
29 | public class RouterServiceImpl implements RouterService {
30 | private final Logger logger = LoggerFactory.getLogger(RouterServiceImpl.class);
31 |
32 | @Autowired
33 | private JSONSerializer jsonSerializer;
34 |
35 | @Autowired
36 | private StringRedisTemplate stringRedisTemplate;
37 |
38 | public void keepAlive(ChannelHandlerContext ctx, FullHttpRequest msg) {
39 | String key = msg.headers().get("key");
40 | String address = msg.headers().get("Host");
41 | String s[] = address.split(":");
42 |
43 | logger.info("key=" + key + ";" + "address=" + address + ";" + "host:" + s[0] + ";" + "port:" + s[1] + ";");
44 | Map addressMap = new HashMap<>();
45 | addressMap.put("host", s[0]);
46 | addressMap.put("port", s[1]);
47 |
48 | stringRedisTemplate.boundHashOps(key).putAll(addressMap);
49 | stringRedisTemplate.boundHashOps(key).expire(30L, TimeUnit.SECONDS);
50 |
51 | FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(jsonSerializer.serialize(addressMap)));
52 | response.headers().set(CONTENT_TYPE, "application/json;charset=UTF-8");
53 | response.headers().set(ACCESS_CONTROL_ALLOW_ORIGIN, "*");
54 | response.headers().set(ACCESS_CONTROL_ALLOW_HEADERS,"*");
55 | response.headers().set(ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
56 | response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes());
57 |
58 | boolean keepAlive = HttpUtil.isKeepAlive(msg);
59 | if (!keepAlive) {
60 | ctx.write(response).addListener(ChannelFutureListener.CLOSE);
61 | } else {
62 | response.headers().set(CONNECTION, KEEP_ALIVE);
63 | ctx.write(response);
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/spring-netty-proxy/src/main/java/com/spring/netty/springnetty/service/impl/UserServiceImpl.java:
--------------------------------------------------------------------------------
1 | package com.spring.netty.springnetty.service.impl;
2 |
3 | import com.spring.netty.springnetty.proxy.HttpProxyInitializer;
4 | import com.spring.netty.springnetty.serializer.impl.JSONSerializer;
5 | import com.spring.netty.springnetty.service.UserService;
6 | import io.netty.bootstrap.Bootstrap;
7 | import io.netty.buffer.Unpooled;
8 | import io.netty.channel.ChannelFuture;
9 | import io.netty.channel.ChannelFutureListener;
10 | import io.netty.channel.ChannelHandlerContext;
11 | import io.netty.handler.codec.http.DefaultFullHttpResponse;
12 | import io.netty.handler.codec.http.FullHttpRequest;
13 | import org.slf4j.Logger;
14 | import org.slf4j.LoggerFactory;
15 | import org.springframework.beans.factory.annotation.Autowired;
16 | import org.springframework.data.redis.core.StringRedisTemplate;
17 | import org.springframework.stereotype.Component;
18 |
19 | import java.util.Map;
20 |
21 | import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND;
22 | import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
23 |
24 | @Component
25 | public class UserServiceImpl implements UserService {
26 |
27 | private final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
28 |
29 | @Autowired
30 | private StringRedisTemplate stringRedisTemplate;
31 |
32 | @Autowired
33 | private JSONSerializer jsonSerializer;
34 |
35 | public void proxy(final ChannelHandlerContext ctx, final FullHttpRequest msg) {
36 |
37 | String key = msg.headers().get("key");
38 | Map