Files
fizz-gateway-node/src/main/java/we/proxy/CallbackService.java

303 lines
12 KiB
Java
Raw Normal View History

2021-01-29 15:16:18 +08:00
/*
* Copyright (C) 2020 the original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package we.proxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
2021-01-31 20:02:57 +08:00
import org.springframework.http.HttpMethod;
2021-01-29 15:16:18 +08:00
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.BodyExtractors;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import we.config.SystemConfig;
2021-01-31 20:02:57 +08:00
import we.constants.CommonConstants;
2021-01-29 15:16:18 +08:00
import we.fizz.AggregateResult;
import we.fizz.AggregateService;
import we.flume.clients.log4j2appender.LogService;
import we.plugin.auth.ApiConfig;
2021-01-31 20:02:57 +08:00
import we.plugin.auth.ApiConfigService;
2021-01-29 15:16:18 +08:00
import we.plugin.auth.CallbackConfig;
import we.plugin.auth.Receiver;
2021-01-31 20:02:57 +08:00
import we.util.*;
2021-01-29 15:16:18 +08:00
import javax.annotation.PostConstruct;
2021-01-29 15:16:18 +08:00
import javax.annotation.Resource;
import java.util.ArrayList;
2021-01-31 20:02:57 +08:00
import java.util.List;
2021-02-02 15:20:03 +08:00
import java.util.Map;
2021-01-31 20:02:57 +08:00
import java.util.function.Function;
2021-01-29 15:16:18 +08:00
/**
* @author hongqiaowei
*/
@Service
public class CallbackService {
2021-01-31 20:02:57 +08:00
private static final Logger log = LoggerFactory.getLogger(CallbackService.class);
private static final String callback = "callback";
2021-01-29 15:16:18 +08:00
@Resource
private FizzWebClient fizzWebClient;
2021-01-29 15:16:18 +08:00
@Resource
private AggregateService aggregateService;
2021-01-31 20:02:57 +08:00
@Resource
private ApiConfigService apiConfigService;
@Resource
private SystemConfig systemConfig;
private String aggrConfigPrefix;
@PostConstruct
public void postConstruct() {
aggrConfigPrefix = systemConfig.gatewayPrefix + '/';
}
2021-02-02 15:20:03 +08:00
public Mono<? extends Void> requestBackends(ServerWebExchange exchange, HttpHeaders headers, DataBuffer body, CallbackConfig cc, Map<String, ServiceInstance> service2instMap) {
2021-01-29 15:16:18 +08:00
ServerHttpRequest req = exchange.getRequest();
2021-01-31 20:02:57 +08:00
String reqId = req.getId();
HttpMethod method = req.getMethod();
if (log.isDebugEnabled()) {
log.debug("service2instMap: " + JacksonUtils.writeValueAsString(service2instMap), LogService.BIZ_ID, reqId);
}
2021-01-29 15:16:18 +08:00
int rs = cc.receivers.size();
2021-01-31 20:02:57 +08:00
Mono<Object>[] sends = new Mono[rs];
2021-01-29 15:16:18 +08:00
for (int i = 0; i < rs; i++) {
Receiver r = cc.receivers.get(i);
Mono send;
if (r.type == ApiConfig.Type.SERVICE_DISCOVERY) {
ServiceInstance si = service2instMap.get(r.service);
if (si == null) {
2021-01-31 20:02:57 +08:00
send = fizzWebClient.proxySend2service(reqId, method, r.service, r.path, headers, body)
.onErrorResume( crError(exchange, r, method, headers, body) );
2021-01-29 15:16:18 +08:00
} else {
String uri = buildUri(req, si, r.path);
2021-01-31 20:02:57 +08:00
send = fizzWebClient.send(reqId, method, uri, headers, body)
.onErrorResume( crError(exchange, r, method, headers, body) );
2021-01-29 15:16:18 +08:00
}
} else {
2021-01-31 20:02:57 +08:00
send = aggregateService.request(WebUtils.getTraceId(exchange), WebUtils.getClientReqPathPrefix(exchange), method.name(), r.service, r.path, req.getQueryParams(), headers, body)
.onErrorResume( arError(exchange, r, method, headers, body) );
2021-01-29 15:16:18 +08:00
}
2021-01-31 20:02:57 +08:00
sends[i] = send;
2021-01-29 15:16:18 +08:00
}
2021-01-31 20:02:57 +08:00
return Flux.mergeSequential(sends)
2021-02-02 11:37:16 +08:00
.collectList()
2021-01-29 15:16:18 +08:00
.flatMap(
2021-02-02 11:37:16 +08:00
sendResults -> {
2021-01-29 15:16:18 +08:00
Object r = null;
2021-02-02 11:37:16 +08:00
for (int i = 1; i < sendResults.size(); i++) {
r = sendResults.get(i);
2021-02-05 11:46:20 +08:00
if (r instanceof ClientResponse && !(r instanceof FizzFailClientResponse)) {
2021-01-31 20:02:57 +08:00
clean((ClientResponse) r);
2021-01-29 15:16:18 +08:00
}
}
2021-02-02 11:37:16 +08:00
r = sendResults.get(0);
2021-02-05 11:46:20 +08:00
Throwable t = null;
if (r instanceof FizzFailClientResponse) {
t = ((FizzFailClientResponse) r).throwable;
return Mono.error(Utils.runtimeExceptionWithoutStack(t.getMessage()));
} if (r instanceof FailAggregateResult) {
t = ((FailAggregateResult) r).throwable;
return Mono.error(Utils.runtimeExceptionWithoutStack(t.getMessage()));
2021-01-31 20:02:57 +08:00
} else if (r instanceof ClientResponse) {
2021-02-02 11:37:16 +08:00
return genServerResponse(exchange, (ClientResponse) r);
2021-01-31 20:02:57 +08:00
} else {
2021-02-02 11:37:16 +08:00
return aggregateService.genAggregateResponse(exchange, (AggregateResult) r);
2021-01-29 15:16:18 +08:00
}
}
)
;
}
2021-01-31 20:02:57 +08:00
private Function<Throwable, Mono<? extends ClientResponse>> crError(ServerWebExchange exchange, Receiver r, HttpMethod method, HttpHeaders headers, DataBuffer body) {
return t -> {
log(exchange, r, method, headers, body, t);
2021-02-05 11:46:20 +08:00
return Mono.just(new FizzFailClientResponse(t));
2021-01-31 20:02:57 +08:00
};
}
private Function<Throwable, Mono<AggregateResult>> arError(ServerWebExchange exchange, Receiver r, HttpMethod method, HttpHeaders headers, DataBuffer body) {
return t -> {
log(exchange, r, method, headers, body, t);
2021-02-05 11:46:20 +08:00
return Mono.just(new FailAggregateResult(t));
2021-01-31 20:02:57 +08:00
};
}
private void log(ServerWebExchange exchange, Receiver r, HttpMethod method, HttpHeaders headers, DataBuffer body, Throwable t) {
StringBuilder b = ThreadContext.getStringBuilder();
WebUtils.request2stringBuilder(exchange, b);
b.append(Constants.Symbol.LINE_SEPARATOR).append(callback).append(Constants.Symbol.LINE_SEPARATOR);
String id = exchange.getRequest().getId();
WebUtils.request2stringBuilder(id, method, r.service + Constants.Symbol.FORWARD_SLASH + r.path, headers, body, b);
log.error(b.toString(), LogService.BIZ_ID, id, t);
}
2021-01-29 15:16:18 +08:00
private String buildUri(ServerHttpRequest req, ServiceInstance si, String path) {
StringBuilder b = ThreadContext.getStringBuilder();
b.append(req.getURI().getScheme()) .append(Constants.Symbol.COLON) .append(Constants.Symbol.FORWARD_SLASH) .append(Constants.Symbol.FORWARD_SLASH);
b.append(si.ip) .append(Constants.Symbol.COLON) .append(si.port) .append(path);
return b.toString();
}
2021-01-31 20:02:57 +08:00
private String buildUri(String scheme, ServiceInstance si, String path) {
StringBuilder b = ThreadContext.getStringBuilder();
b.append(scheme) .append(Constants.Symbol.COLON) .append(Constants.Symbol.FORWARD_SLASH) .append(Constants.Symbol.FORWARD_SLASH);
b.append(si.ip) .append(Constants.Symbol.COLON) .append(si.port) .append(path);
return b.toString();
}
2021-01-29 15:16:18 +08:00
private Mono<? extends Void> genServerResponse(ServerWebExchange exchange, ClientResponse remoteResp) {
ServerHttpResponse clientResp = exchange.getResponse();
clientResp.setStatusCode(remoteResp.statusCode());
HttpHeaders clientRespHeaders = clientResp.getHeaders();
HttpHeaders remoteRespHeaders = remoteResp.headers().asHttpHeaders();
remoteRespHeaders.entrySet().forEach(
h -> {
String k = h.getKey();
if (clientRespHeaders.containsKey(k)) {
if (k.equals(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN) || k.equals(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)
|| k.equals(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS) || k.equals(HttpHeaders.ACCESS_CONTROL_MAX_AGE)) {
} else {
clientRespHeaders.put(k, h.getValue());
}
} else {
clientRespHeaders.put(k, h.getValue());
}
}
);
if (log.isDebugEnabled()) {
StringBuilder b = ThreadContext.getStringBuilder();
String rid = exchange.getRequest().getId();
WebUtils.response2stringBuilder(rid, remoteResp, b);
log.debug(b.toString(), LogService.BIZ_ID, rid);
}
return clientResp.writeWith(remoteResp.body(BodyExtractors.toDataBuffers()))
2021-01-31 20:02:57 +08:00
.doOnError(throwable -> clean(remoteResp)).doOnCancel(() -> clean(remoteResp));
2021-01-29 15:16:18 +08:00
}
2021-01-31 20:02:57 +08:00
public Mono<ReactiveResult> replay(CallbackReplayReq req) {
2021-02-02 11:37:16 +08:00
ApiConfig ac = apiConfigService.getApiConfig(req.service, req.method, req.path, req.gatewayGroup, req.app);
if (ac == null) {
return Mono.just(ReactiveResult.fail("no api config for " + req.path));
}
CallbackConfig cc = ac.callbackConfig;
2021-01-31 20:02:57 +08:00
List<Mono<Object>> sends = new ArrayList<>(); Mono send;
if (req.replayType == CallbackReplayReq.Type.ORIGINAL_PATH) {
int rs = cc.receivers.size();
for (int i = 0; i < rs; i++) {
Receiver r = cc.receivers.get(i);
if (r.type == ApiConfig.Type.SERVICE_DISCOVERY) {
ServiceInstance si = req.receivers.get(r.service);
if (si != null) {
String uri = buildUri("http", si, r.path);
send = fizzWebClient.send(req.id, req.method, uri, req.headers, req.body)
.onErrorResume( crError(req, r.service, r.path) );
sends.add(send);
}
} else {
String traceId = CommonConstants.TRACE_ID_PREFIX + req.id;
send = aggregateService.request(traceId, aggrConfigPrefix, req.method.name(), r.service, r.path, null, req.headers, req.body)
2021-01-31 20:02:57 +08:00
.onErrorResume( arError(req, r.service, r.path) );
sends.add(send);
}
}
} else {
for (ServiceTypePath stp : req.assignServices) {
if (stp.type == ApiConfig.Type.SERVICE_DISCOVERY) {
send = fizzWebClient.proxySend2service(req.id, req.method, stp.service, stp.path, req.headers, req.body)
.onErrorResume( crError(req, stp.service, stp.path) );
} else {
String traceId = CommonConstants.TRACE_ID_PREFIX + req.id;
send = aggregateService.request(traceId, aggrConfigPrefix, req.method.name(), stp.service, stp.path, null, req.headers, req.body)
2021-01-31 20:02:57 +08:00
.onErrorResume( arError(req, stp.service, stp.path) );
}
2021-02-02 11:37:16 +08:00
sends.add(send);
2021-01-31 20:02:57 +08:00
}
}
int ss = sends.size();
Mono<Object>[] sendArr = sends.toArray(new Mono[ss]);
return Flux.mergeSequential(sendArr)
2021-02-02 11:37:16 +08:00
.collectList()
2021-01-31 20:02:57 +08:00
.map(
2021-02-02 11:37:16 +08:00
sendResults -> {
2021-01-31 20:02:57 +08:00
int c = ReactiveResult.SUCC;
2021-02-05 11:46:20 +08:00
Throwable t = null;
2021-02-02 11:37:16 +08:00
for (int i = 0; i < sendResults.size(); i++) {
Object r = sendResults.get(i);
2021-02-05 11:46:20 +08:00
if (r instanceof FizzFailClientResponse) {
c = ReactiveResult.FAIL;
t = ((FizzFailClientResponse) r).throwable;
} else if (r instanceof FailAggregateResult) {
2021-01-31 20:02:57 +08:00
c = ReactiveResult.FAIL;
2021-02-05 11:46:20 +08:00
t = ((FailAggregateResult) r).throwable;
2021-01-31 20:02:57 +08:00
} else if (r instanceof ClientResponse) {
clean((ClientResponse) r);
}
}
2021-02-05 11:46:20 +08:00
return ReactiveResult.with(c, t);
2021-01-31 20:02:57 +08:00
}
)
;
}
private Function<Throwable, Mono<? extends AggregateResult>> arError(CallbackReplayReq req, String service, String path) {
return t -> {
log(req, service, path, t);
2021-02-05 11:46:20 +08:00
return Mono.just(new FailAggregateResult(t));
2021-01-31 20:02:57 +08:00
};
}
private Function<Throwable, Mono<? extends ClientResponse>> crError(CallbackReplayReq req, String service, String path) {
return t -> {
log(req, service, path, t);
2021-02-05 11:46:20 +08:00
return Mono.just(new FizzFailClientResponse(t));
2021-01-31 20:02:57 +08:00
};
}
private void log(CallbackReplayReq req, String service, String path, Throwable t) {
StringBuilder b = ThreadContext.getStringBuilder();
b.append(req.service).append(Constants.Symbol.FORWARD_SLASH).append(req.path);
b.append(Constants.Symbol.LINE_SEPARATOR).append(callback).append(Constants.Symbol.LINE_SEPARATOR);
WebUtils.request2stringBuilder(req.id, req.method, service + Constants.Symbol.FORWARD_SLASH + path, req.headers, req.body, b);
log.error(b.toString(), LogService.BIZ_ID, req.id, t);
}
private void clean(ClientResponse cr) {
cr.bodyToMono(Void.class).subscribe();
2021-01-29 15:16:18 +08:00
}
}