одинокий дымпродвинутый архитектор Java
Что такое шлюз? Зачем мне использовать шлюз?
Как показано на рисунке, без использования шлюза наша служба напрямую доступна вызывающей стороне. Когда количество звонящих увеличивается, необходимо добавить логику, такую как настраиваемые права доступа и проверка. При добавлении шлюза API между сторонним вызывающим абонентом и поставщиком услуг создается стена, и эта стена связывается напрямую с вызывающим абонентом для управления разрешениями.
Исходный код шлюза, реализованный в этой статье, является плагиатом --- О, нет, это ссылка. Опираясь на исходный код шлюза Zuul, извлекая его основные идеи и реализуя набор простых исходных кодов шлюза, блогер переименовал его в Eatuul.
Эта статья является первой статьей, которую можно найти в отрасли для самостоятельной реализации шлюза. Цель серии статей, написанных блогерами, состоит в том, чтобы максимально просто раскрыть основные принципы промежуточного программного обеспечения, чтобы читатели могли быстро понять суть реализации. Следует отметить, что это не серия статей по анализу исходного кода, поэтому в написанном коде пропущено некоторое сложное содержание, в конце концов, каждый может понять основной принцип промежуточного программного обеспечения. Если вы хотите увидеть серию анализов исходного кода, пожалуйста, обратите внимание на блоггеров, и платформы с открытым исходным кодом, такие как spring, spring boot, dubbo и mybatis, будут раскрыты один за другим позже.
Прежде всего, поговорим об этом, то есть о том, чтобы определить сервлет для приема запросов. Затем пройдите через preFilter (инкапсулировать параметры запроса), routeFilter (переадресация запроса), postFilter (выходное содержимое). Между тремя фильтрами совместно используются запрос, ответ и другие глобальные переменные. Как показано ниже
Чем отличается ## от настоящего Зуула?Основные отличия заключаются в следующем
(1) В модуле обработки исключений в Zuul есть ErrorFilter для его обработки.Блогер ленится реализовывать его, поэтому он опущен.
(2) PreFilters, RoutingFilters и PostFilters в Zuul по умолчанию реализуют группу, как показано в следующей таблице.
Блогеры не могут реализовать каждый из них за вас. Так что будьте ленивы и реализуйте только один из них. Но порядок вызова все тот же, он вызывается в порядке PreFilters->RoutingFilters->PostFilters
(3) Фильтр для переадресации запросов в routeFilters действительно есть, но блоггер передумал и вместо этого использовал RestTemplate.
Все идут на официальный сайт spring, чтобы собрать набор проектов springboot, блогеры не будут показывать код pom. Непосредственно отобразите структуру проекта, как показано на следующем рисунке.
# EatuulServlet.java. Это вход в шлюз, и логика тоже очень простая, разделенная на три шага
(1) Поместите запрос и ответ в threadlocal
(2) Выполните три набора фильтров
(3) Очистите переменные среды в threadlocal
Исходный код выглядит следующим образом
package com.rjzheng.eatuul.http;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(name = "eatuul", urlPatterns = "/*")
public class EatuulServlet extends HttpServlet {
private EatRunner eatRunner = new EatRunner();
@Override
public void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//将request,和response放入上下文对象中
eatRunner.init(req, resp);
try {
//执行前置过滤
eatRunner.preRoute();
//执行过滤
eatRunner.route();
//执行后置过滤
eatRunner.postRoute();
} catch (Throwable e) {
RequestContext.getCurrentContext().getResponse()
.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
} finally {
//清除变量
RequestContext.getCurrentContext().unset();
}
}
}
package com.rjzheng.eatuul.http;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.rjzheng.eatuul.filter.EatuulFilter;
import com.rjzheng.eatuul.filter.post.SendResponseFilter;
import com.rjzheng.eatuul.filter.pre.RequestWrapperFilter;
import com.rjzheng.eatuul.filter.route.RoutingFilter;
public class EatRunner {
//静态写死过滤器
private ConcurrentHashMap<String, List<EatuulFilter>> hashFiltersByType = new ConcurrentHashMap<String, List<EatuulFilter>>(){{
put("pre",new ArrayList<EatuulFilter>(){{
add(new RequestWrapperFilter());
}});
put("route",new ArrayList<EatuulFilter>(){{
add(new RoutingFilter());
}});
put("post",new ArrayList<EatuulFilter>(){{
add(new SendResponseFilter());
}});
}};
public void init(HttpServletRequest req, HttpServletResponse resp) {
RequestContext ctx = RequestContext.getCurrentContext();
ctx.setRequest(req);
ctx.setResponse(resp);
}
public void preRoute() throws Throwable {
runFilters("pre");
}
public void route() throws Throwable{
runFilters("route");
}
public void postRoute() throws Throwable{
runFilters("post");
}
public void runFilters(String sType) throws Throwable {
List<EatuulFilter> list = this.hashFiltersByType.get(sType);
if (list != null) {
for (int i = 0; i < list.size(); i++) {
EatuulFilter zuulFilter = list.get(i);
zuulFilter.run();
}
}
}
}
package com.rjzheng.eatuul.filter;
public abstract class EatuulFilter {
abstract public String filterType();
abstract public int filterOrder();
abstract public void run();
}
package com.rjzheng.eatuul.filter.pre;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.RequestEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StreamUtils;
import com.rjzheng.eatuul.filter.EatuulFilter;
import com.rjzheng.eatuul.http.RequestContext;
public class RequestWrapperFilter extends EatuulFilter{
@Override
public String filterType() {
// TODO Auto-generated method stub
return "pre";
}
@Override
public int filterOrder() {
// TODO Auto-generated method stub
return -1;
}
@Override
public void run() {
String rootURL = "http://localhost:9090";
RequestContext ctx =RequestContext.getCurrentContext();
HttpServletRequest servletRequest = ctx.getRequest();
String targetURL = rootURL + servletRequest.getRequestURI();
RequestEntity<byte[]> requestEntity = null;
try {
requestEntity = createRequestEntity(servletRequest, targetURL);
} catch (Exception e) {
e.printStackTrace();
}
//4、将requestEntity放入全局threadlocal之中
ctx.setRequestEntity(requestEntity);
}
private RequestEntity createRequestEntity(HttpServletRequest request,String url) throws URISyntaxException, IOException {
String method = request.getMethod();
HttpMethod httpMethod = HttpMethod.resolve(method);
//1、封装请求头
MultiValueMap<String, String> headers =createRequestHeaders(request);
//2、封装请求体
byte[] body = createRequestBody(request);
//3、构造出RestTemplate能识别的RequestEntity
RequestEntity requestEntity = new RequestEntity<byte[]>(body,headers,httpMethod, new URI(url));
return requestEntity;
}
private byte[] createRequestBody(HttpServletRequest request) throws IOException {
InputStream inputStream = request.getInputStream();
return StreamUtils.copyToByteArray(inputStream);
}
private MultiValueMap<String, String> createRequestHeaders(HttpServletRequest request) {
HttpHeaders headers = new HttpHeaders();
List<String> headerNames = Collections.list(request.getHeaderNames());
for(String headerName:headerNames) {
List<String> headerValues = Collections.list(request.getHeaders(headerName));
for(String headerValue:headerValues) {
headers.add(headerName, headerValue);
}
}
return headers;
}
}
package com.rjzheng.eatuul.filter.route;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import com.rjzheng.eatuul.filter.EatuulFilter;
import com.rjzheng.eatuul.http.RequestContext;
public class RoutingFilter extends EatuulFilter{
@Override
public String filterType() {
// TODO Auto-generated method stub
return "route";
}
@Override
public int filterOrder() {
// TODO Auto-generated method stub
return 0;
}
@Override
public void run(){
RequestContext ctx = RequestContext.getCurrentContext();
RequestEntity requestEntity = ctx.getRequestEntity();
RestTemplate restTemplate = new RestTemplate();
ResponseEntity responseEntity = restTemplate.exchange(requestEntity,byte[].class);
ctx.setResponseEntity(responseEntity);
}
}
Это postFilters, просто выведите ResponseEntity
package com.rjzheng.eatuul.filter.post;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import com.rjzheng.eatuul.filter.EatuulFilter;
import com.rjzheng.eatuul.http.RequestContext;
public class SendResponseFilter extends EatuulFilter{
@Override
public String filterType() {
return "post";
}
@Override
public int filterOrder() {
return 1000;
}
@Override
public void run() {
try {
addResponseHeaders();
writeResponse();
} catch (Exception e) {
e.printStackTrace();
}
}
private void addResponseHeaders() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletResponse servletResponse = ctx.getResponse();
ResponseEntity responseEntity = ctx.getResponseEntity();
HttpHeaders httpHeaders = responseEntity.getHeaders();
for(Map.Entry<String, List<String>> entry:httpHeaders.entrySet()) {
String headerName = entry.getKey();
List<String> headerValues = entry.getValue();
for(String headerValue:headerValues) {
servletResponse.addHeader(headerName, headerValue);
}
}
}
private void writeResponse()throws Exception {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletResponse servletResponse = ctx.getResponse();
if (servletResponse.getCharacterEncoding() == null) { // only set if not set
servletResponse.setCharacterEncoding("UTF-8");
}
ResponseEntity responseEntity = ctx.getResponseEntity();
if(responseEntity.hasBody()) {
byte[] body = (byte[]) responseEntity.getBody();
ServletOutputStream outputStream = servletResponse.getOutputStream();
outputStream.write(body);
outputStream.flush();
}
}
}
Наконец, глобальная переменная threadlocal, о которой было сказано
package com.rjzheng.eatuul.http;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
public class RequestContext extends ConcurrentHashMap<String, Object> {
protected static Class<? extends RequestContext> contextClass = RequestContext.class;
protected static final ThreadLocal<? extends RequestContext> threadLocal = new ThreadLocal<RequestContext>() {
@Override
protected RequestContext initialValue() {
try {
return contextClass.newInstance();
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
};
public static RequestContext getCurrentContext() {
RequestContext context = threadLocal.get();
return context;
}
public HttpServletRequest getRequest() {
return (HttpServletRequest) get("request");
}
public void setRequest(HttpServletRequest request) {
put("request", request);
}
public HttpServletResponse getResponse() {
return (HttpServletResponse) get("response");
}
public void setResponse(HttpServletResponse response) {
set("response", response);
}
public void setRequestEntity(RequestEntity requestEntity){
set("requestEntity",requestEntity);
}
public RequestEntity getRequestEntity() {
return (RequestEntity) get("requestEntity");
}
public void setResponseEntity(ResponseEntity responseEntity){
set("responseEntity",responseEntity);
}
public ResponseEntity getResponseEntity() {
return (ResponseEntity) get("responseEntity");
}
public void set(String key, Object value) {
if (value != null)
put(key, value);
else
remove(key);
}
public void unset() {
threadLocal.remove();
}
}
# Как проверить?
Для себя с сервера порт 9090 как показано ниже
package com.rjzheng.eatservice;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.ServletComponentScan;
import com.rjzheng.eatservice.controller.IndexController;
@SpringBootApplication
@ServletComponentScan(basePackageClasses = IndexController.class)
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).properties("server.port=9090").run(args);
}
}
другой контроллер
package com.rjzheng.eatservice.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class IndexController {
@RequestMapping("/index")
public String index() {
return "hello!world";
}
}
Затем вы обнаружите, что можете перейти к доступу с localhost:8080/index.
В этой статье имитируется исходный код шлюза zuul и используется его сущность. Надеюсь, вы все получите что-то
Источник: http://rjzheng.cnblogs.com/
Автор: Lonely Smoke, официальный аккаунт автора, можете обратить внимание на волну