Skip to content

Commit f85c0d9

Browse files
committed
add hystrix
1 parent 05d0017 commit f85c0d9

File tree

6 files changed

+144
-64
lines changed

6 files changed

+144
-64
lines changed

pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@
3030
<artifactId>netty-all</artifactId>
3131
<version>4.1.25.Final</version>
3232
</dependency>
33+
<dependency>
34+
<groupId>com.netflix.hystrix</groupId>
35+
<artifactId>hystrix-core</artifactId>
36+
<version>1.5.12</version>
37+
</dependency>
3338
<dependency>
3439
<groupId>com.fasterxml.jackson.core</groupId>
3540
<artifactId>jackson-databind</artifactId>

src/main/java/love/wangqi/codec/DefaultHttpRequestBuilder.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,13 @@ public class DefaultHttpRequestBuilder implements HttpRequestBuilder {
3737
HttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE);
3838

3939
public class RequestHolder {
40+
public Route route;
4041
public URL url;
4142
public HttpRequest request;
4243
public HttpPostRequestEncoder bodyRequestEncoder;
4344

44-
public RequestHolder(URL url, HttpRequest request, HttpPostRequestEncoder bodyRequestEncoder) {
45+
public RequestHolder(Route route, URL url, HttpRequest request, HttpPostRequestEncoder bodyRequestEncoder) {
46+
this.route = route;
4547
this.url = url;
4648
this.request = request;
4749
this.bodyRequestEncoder = bodyRequestEncoder;
@@ -133,7 +135,7 @@ public RequestHolder build() throws Exception {
133135
((FullHttpRequest)newRequest).content().writeBytes(bbuf);
134136
}
135137
}
136-
return new RequestHolder(url, newRequest, newBodyRequestEncoder);
138+
return new RequestHolder(route, url, newRequest, newBodyRequestEncoder);
137139
}
138140

139141
/**

src/main/java/love/wangqi/handler/FrontHandler.java

Lines changed: 5 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,16 @@
11
package love.wangqi.handler;
22

3-
import io.netty.bootstrap.Bootstrap;
4-
import io.netty.channel.Channel;
53
import io.netty.channel.ChannelHandlerContext;
64
import io.netty.channel.ChannelInboundHandlerAdapter;
7-
import io.netty.channel.EventLoopGroup;
8-
import io.netty.channel.nio.NioEventLoopGroup;
9-
import io.netty.channel.socket.nio.NioSocketChannel;
105
import io.netty.handler.codec.http.FullHttpRequest;
11-
import io.netty.handler.codec.http.HttpRequest;
12-
import io.netty.handler.codec.http.multipart.HttpPostRequestEncoder;
13-
import io.netty.handler.ssl.SslContext;
14-
import io.netty.handler.ssl.SslContextBuilder;
15-
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
16-
import org.slf4j.Logger;
17-
import org.slf4j.LoggerFactory;
186
import love.wangqi.codec.DefaultHttpRequestBuilder;
197
import love.wangqi.codec.HttpRequestBuilder;
208
import love.wangqi.exception.handler.DefaultExceptionHandler;
219
import love.wangqi.filter.HttpRequestFilter;
10+
import love.wangqi.handler.command.ForwardCommand;
2211
import love.wangqi.server.GatewayServer;
23-
24-
import java.net.URL;
12+
import org.slf4j.Logger;
13+
import org.slf4j.LoggerFactory;
2514

2615

2716
/**
@@ -51,7 +40,8 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
5140
.setOriginRequest(httpRequest);
5241

5342
DefaultHttpRequestBuilder.RequestHolder requestHolder = httpRequestBuilder.build();
54-
forward(ctx.channel(), requestHolder.url, requestHolder.request, requestHolder.bodyRequestEncoder);
43+
ForwardCommand forwardCommand = new ForwardCommand(ctx.channel(), requestHolder);
44+
forwardCommand.queue();
5545
} catch (Exception e) {
5646
logger.error(e.toString());
5747
if (GatewayServer.config.getExceptionHandler() != null) {
@@ -64,53 +54,6 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
6454
}
6555
}
6656

67-
private void forward(Channel inboundChannel, URL url, HttpRequest request, HttpPostRequestEncoder bodyRequestEncoder) throws Exception {
68-
String protocol = url.getProtocol() == null ? "http" : url.getProtocol();
69-
String host = url.getHost() == null ? "127.0.0.1" : url.getHost();
70-
int port = url.getPort();
71-
if (port == -1) {
72-
if ("http".equalsIgnoreCase(protocol)) {
73-
port = 80;
74-
} else if ("https".equalsIgnoreCase(protocol)) {
75-
port = 443;
76-
}
77-
}
78-
79-
if (!"http".equalsIgnoreCase(protocol) && !"https".equalsIgnoreCase(protocol)) {
80-
System.err.println("Only HTTP(S) is supported.");
81-
return;
82-
}
83-
84-
// Configure SSL context if necessary.
85-
final boolean ssl = "https".equalsIgnoreCase(protocol);
86-
final SslContext sslCtx;
87-
if (ssl) {
88-
sslCtx = SslContextBuilder.forClient()
89-
.trustManager(InsecureTrustManagerFactory.INSTANCE).build();
90-
} else {
91-
sslCtx = null;
92-
}
93-
94-
EventLoopGroup group = new NioEventLoopGroup();
95-
try {
96-
Bootstrap b = new Bootstrap();
97-
b.group(group)
98-
.channel(NioSocketChannel.class)
99-
.handler(new BackendFilter(sslCtx, inboundChannel));
100-
101-
Channel ch = b.connect(host, port).sync().channel();
102-
103-
ch.write(request);
104-
if (bodyRequestEncoder != null && bodyRequestEncoder.isChunked()) {
105-
ch.write(bodyRequestEncoder);
106-
}
107-
ch.flush();
108-
ch.closeFuture().sync();
109-
} finally {
110-
group.shutdownGracefully();
111-
}
112-
}
113-
11457
@Override
11558
public void channelActive(ChannelHandlerContext ctx) throws Exception {
11659
logger.info("连接的客户端地址:{}", ctx.channel().remoteAddress());
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package love.wangqi.handler.command;
2+
3+
import com.netflix.hystrix.*;
4+
import io.netty.bootstrap.Bootstrap;
5+
import io.netty.channel.Channel;
6+
import io.netty.channel.EventLoopGroup;
7+
import io.netty.channel.nio.NioEventLoopGroup;
8+
import io.netty.channel.socket.nio.NioSocketChannel;
9+
import io.netty.handler.codec.http.HttpRequest;
10+
import io.netty.handler.codec.http.multipart.HttpPostRequestEncoder;
11+
import io.netty.handler.ssl.SslContext;
12+
import io.netty.handler.ssl.SslContextBuilder;
13+
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
14+
import love.wangqi.codec.DefaultHttpRequestBuilder;
15+
import love.wangqi.config.GatewayConfig;
16+
import love.wangqi.handler.BackendFilter;
17+
import love.wangqi.server.GatewayServer;
18+
19+
import java.net.URL;
20+
21+
/**
22+
* @author: wangqi
23+
* @description:
24+
* @date: Created in 2018/7/27 下午3:10
25+
*/
26+
public class ForwardCommand extends HystrixCommand<Void> {
27+
private Channel inboundChannel;
28+
private DefaultHttpRequestBuilder.RequestHolder requestHolder;
29+
30+
public ForwardCommand(Channel inboundChannel, DefaultHttpRequestBuilder.RequestHolder requestHolder) {
31+
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ForwardCommandGroup"))
32+
.andCommandKey(HystrixCommandKey.Factory.asKey("ForwardCommand"))
33+
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("threadPool-" + requestHolder.route.getId()))
34+
.andThreadPoolPropertiesDefaults(
35+
HystrixThreadPoolProperties.Setter()
36+
.withCoreSize(1)
37+
.withMaximumSize(10)
38+
.withAllowMaximumSizeToDivergeFromCoreSize(true)
39+
)
40+
.andCommandPropertiesDefaults(
41+
HystrixCommandProperties.Setter()
42+
.withExecutionTimeoutInMilliseconds(requestHolder.route.getTimeoutInMilliseconds())
43+
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)
44+
.withFallbackIsolationSemaphoreMaxConcurrentRequests(100)
45+
)
46+
);
47+
48+
this.inboundChannel = inboundChannel;
49+
this.requestHolder = requestHolder;
50+
}
51+
52+
53+
@Override
54+
protected Void run() throws Exception {
55+
forward(inboundChannel, requestHolder.url, requestHolder.request, requestHolder.bodyRequestEncoder);
56+
return null;
57+
}
58+
59+
@Override
60+
protected Void getFallback() {
61+
return null;
62+
}
63+
64+
private void forward(Channel inboundChannel, URL url, HttpRequest request, HttpPostRequestEncoder bodyRequestEncoder) throws Exception {
65+
String protocol = url.getProtocol() == null ? "http" : url.getProtocol();
66+
String host = url.getHost() == null ? "127.0.0.1" : url.getHost();
67+
int port = url.getPort();
68+
if (port == -1) {
69+
if ("http".equalsIgnoreCase(protocol)) {
70+
port = 80;
71+
} else if ("https".equalsIgnoreCase(protocol)) {
72+
port = 443;
73+
}
74+
}
75+
76+
if (!"http".equalsIgnoreCase(protocol) && !"https".equalsIgnoreCase(protocol)) {
77+
System.err.println("Only HTTP(S) is supported.");
78+
return;
79+
}
80+
81+
// Configure SSL context if necessary.
82+
final boolean ssl = "https".equalsIgnoreCase(protocol);
83+
final SslContext sslCtx;
84+
if (ssl) {
85+
sslCtx = SslContextBuilder.forClient()
86+
.trustManager(InsecureTrustManagerFactory.INSTANCE).build();
87+
} else {
88+
sslCtx = null;
89+
}
90+
91+
EventLoopGroup group = new NioEventLoopGroup();
92+
try {
93+
Bootstrap b = new Bootstrap();
94+
b.group(group)
95+
.channel(NioSocketChannel.class)
96+
.handler(new BackendFilter(sslCtx, inboundChannel));
97+
98+
Channel ch = b.connect(host, port).sync().channel();
99+
100+
ch.write(request);
101+
if (bodyRequestEncoder != null && bodyRequestEncoder.isChunked()) {
102+
ch.write(bodyRequestEncoder);
103+
}
104+
ch.flush();
105+
ch.closeFuture().sync();
106+
} finally {
107+
group.shutdownGracefully();
108+
}
109+
}
110+
}

src/main/java/love/wangqi/route/Route.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,26 @@ public class Route {
2626
* 请求方法
2727
*/
2828
private HttpMethod method;
29+
/**
30+
* 请求超时时间
31+
*/
32+
private Integer timeoutInMilliseconds;
33+
34+
private static final Integer DEFAULT_TIMEOUT = 3000;
2935

3036
public Route() {
3137
}
3238

3339
public Route(Long id, HttpMethod method, String path, URL mapUrl) {
40+
this(id, method, path, mapUrl, DEFAULT_TIMEOUT);
41+
}
42+
43+
public Route(Long id, HttpMethod method, String path, URL mapUrl, Integer timeoutInMilliseconds) {
3444
this.id = id;
3545
this.path = path;
3646
this.mapUrl = mapUrl;
3747
this.method = method;
48+
this.timeoutInMilliseconds = timeoutInMilliseconds;
3849
}
3950

4051
public Long getId() {
@@ -68,4 +79,12 @@ public HttpMethod getMethod() {
6879
public void setMethod(HttpMethod method) {
6980
this.method = method;
7081
}
82+
83+
public Integer getTimeoutInMilliseconds() {
84+
return timeoutInMilliseconds;
85+
}
86+
87+
public void setTimeoutInMilliseconds(Integer timeoutInMilliseconds) {
88+
this.timeoutInMilliseconds = timeoutInMilliseconds;
89+
}
7190
}

src/test/java/love/wangqi/GatewayServerTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ protected List<Route> locateRouteList(Set<Long> ids) {
3535
routeList.add(new Route(5L, HttpMethod.GET, "/oschina", new URL("https://www.oschina.net/")));
3636
routeList.add(new Route(6L, HttpMethod.POST, "/users/{id}", new URL("http://127.0.0.1/pre/users/{id}")));
3737
routeList.add(new Route(7L, HttpMethod.GET, "/css/main.css", new URL("https://blog.wangqi.love/css/main.css")));
38+
routeList.add(new Route(8L, HttpMethod.GET, "/path", new URL("http://127.0.0.1:9990/path")));
3839
} catch (MalformedURLException e) {
3940
}
4041
return routeList;

0 commit comments

Comments
 (0)