欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot實現(xiàn)反向代理的示例代碼

 更新時間:2023年06月13日 15:16:04   作者:RikyLee  
本文主要介紹了SpringBoot實現(xiàn)反向代理的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

最近收到一個新的需求,需要根據(jù)自定義的負載均衡策略從動態(tài)主機池選主之后,再通過反向代理到選中的主機上,這里面就涉及到服務(wù)注冊、負載均衡策略、反向代理。本篇文章只涉及到如何實現(xiàn)反向代理功能。

功能實現(xiàn)

如果只是需要反向代理功能,那么有很多中間件可以選擇,比如:Nginx、Kong、Spring Cloud Gateway,Zuul等都可以實現(xiàn),但是還有一些客制化的需求,所以只能自己擼代碼實現(xiàn)了,附上源碼。

請求攔截

實現(xiàn)請求攔截有兩種方式,過濾器和攔截器,我們采用過濾器的方式去實現(xiàn)請求攔截。
在Spring 體系中最常用到的過濾器應(yīng)該就是OncePerRequestFilter,這是一個抽象類。我們創(chuàng)建一個類叫ForwardRoutingFilter去繼承這個類,同時實現(xiàn)Ordered,用于設(shè)置過濾器的優(yōu)先級

@Slf4j
@Component
public class ForwardRoutingFilter extends OncePerRequestFilter implements Ordered {
? @Override
? public int getOrder() {
? ? return 0; // 值越小,優(yōu)先級別越高
? }
? @Override
? protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
? ? log.info("ForwardRoutingFilter doFilterInternal,request uri: {}", request.getRequestURI());
? ? filterChain.doFilter(request, response);
? }
}

啟動服務(wù)之后,瀏覽器中輸入http://127.0.0.1:8080/aa,查看console 中的日志,可以看到過濾器以及開始工作了。

2023-06-12T14:25:09.059+08:00  INFO 17472 --- [nio-8080-exec-2] c.r.b.filter.ForwardRoutingFilter        : ForwardRoutingFilter doFilterInternal,request uri: /aa
2023-06-12T14:25:09.735+08:00  INFO 17472 --- [nio-8080-exec-1] c.r.b.filter.ForwardRoutingFilter        : ForwardRoutingFilter doFilterInternal,request uri: /favicon.ico

接下來,我們的實現(xiàn)就圍繞著這個過濾器去做了。

配置規(guī)則定義

通常情況下,我們會在application.yml去配置哪些path需要被轉(zhuǎn)發(fā)到具體的服務(wù)上去,例如

my:
  routes:
    - uri: lb://ai-server
      path: /ai/**
      rewrite: false
    - uri: https://api.oioweb.cn
      path: /oioweb/**
      rewrite: true

參數(shù)說明:

  • txt復(fù)制代碼uri: 最終請求的服務(wù)地址,如果是lb:// 開頭的,說明需要進行負責(zé)均衡
  • path: 用于匹配代理的路徑,命中的會被進行代理轉(zhuǎn)發(fā)
  • rewrite: 是否重寫path,如果true, 訪問 http://127.0.0.1:8080/uomg/api/rand.img1 請求path中/uomg會被刪除,最終訪問的是 https://api.uomg.com/api/rand.img1

在pom.xml dependencies 中添加新的依賴,用于自動裝填配置

<!--讀取文件配置-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-configuration-processor</artifactId>
</dependency>

創(chuàng)建實體類RouteInstance和配置類MyRoutes,這樣服務(wù)啟動之后就會自動讀取裝填my.routes下所有配置的實例到配置類了

@Data
public class RouteInstance {
  private String uri;
  private String path;
  private boolean rewrite;
}
@Configuration
@ConfigurationProperties(prefix = "my", ignoreInvalidFields = true)
@Data
public class MyRoutes {
  private List<RouteInstance> routes;
}

代理實現(xiàn)

在pom.xml dependencies 中添加需要用到的依賴

<dependency>
? <groupId>org.apache.commons</groupId>
? <artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
? <groupId>commons-io</groupId>
? <artifactId>commons-io</artifactId>
? <version>2.11.0</version>
</dependency>
<dependency>
? <groupId>commons-beanutils</groupId>
? <artifactId>commons-beanutils</artifactId>
? <version>1.9.4</version>
</dependency>
<dependency>
? <groupId>org.apache.httpcomponents.client5</groupId>
? <artifactId>httpclient5</artifactId>
? <version>5.2.1</version>
</dependency>
<dependency>
? <groupId>com.alibaba.fastjson2</groupId>
? <artifactId>fastjson2</artifactId>
? <version>2.0.32</version>
</dependency>

接下來就是改造我們之前的ForwardRoutingFilter 過濾器類了

@Slf4j
@Component
public class ForwardRoutingFilter extends OncePerRequestFilter implements Ordered {
? @Resource
? private MyRoutes routes;
? @Resource
? private RoutingDelegateService routingDelegate;
? @Override
? public int getOrder() {
? ? return 0;
? }
? @Override
? protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
? ? log.info("ForwardRoutingFilter doFilterInternal,request uri: {}", request.getRequestURI());
? ? String currentURL = StringUtils.isEmpty(request.getContextPath()) ? request.getRequestURI() :
? ? ? ? StringUtils.substringAfter(request.getRequestURI(), request.getContextPath());
? ? AntPathMatcher matcher = new AntPathMatcher();
? ? RouteInstance instance = routes.getRoutes().stream().filter(i -> matcher.match(i.getPath(), currentURL)).findFirst().orElse(new RouteInstance());
? ? if (instance.getUri() == null) {
? ? ? //轉(zhuǎn)發(fā)的uri為空,不進行代理轉(zhuǎn)發(fā),交由過濾器鏈后續(xù)過濾器處理
? ? ? filterChain.doFilter(request, response);
? ? } else {
? ? ? // 創(chuàng)建一個service 去處理代理轉(zhuǎn)發(fā)邏輯
? ? ? routingDelegate.doForward(instance, request, response);
? ? ? return;
? ? }
? }
}

代理轉(zhuǎn)發(fā)會使用到RestTemplate,默認使用的是java.net.URLConnection去進行http請求,我們這邊替換成httpclient,具體配置就不貼出來了。
編寫兩個工具欄,分別用于轉(zhuǎn)換 HttpServletRequest 為 RequestEntity 和 HttpServletResponse 為 ResponseEntity,并把結(jié)果寫回客戶端

@Slf4j
public class HttpRequestMapper {
? public RequestEntity<byte[]> map(HttpServletRequest request, RouteInstance instance) throws IOException {
? ? byte[] body = extractBody(request);
? ? HttpHeaders headers = extractHeaders(request);
? ? HttpMethod method = extractMethod(request);
? ? URI uri = extractUri(request, instance);
? ? return new RequestEntity<>(body, headers, method, uri);
? }
? private URI extractUri(HttpServletRequest request, RouteInstance instance) throws UnsupportedEncodingException {
? ? //如果content path 不為空,移除content path
? ? String requestURI = StringUtils.isEmpty(request.getContextPath()) ? request.getRequestURI() :
? ? ? ? StringUtils.substringAfter(request.getRequestURI(), request.getContextPath());
? ? //處理中文被自動編碼問題
? ? String query = request.getQueryString() == null ? EMPTY : URLDecoder.decode(request.getQueryString(), "utf-8");
? ? // 需要重寫path
? ? if (instance.isRewrite()) {
? ? ? String prefix = StringUtils.substringBefore(instance.getPath(), "/**");
? ? ? requestURI = StringUtils.substringAfter(requestURI, prefix);
? ? }
? ? URI redirectURL = UriComponentsBuilder.fromUriString(instance.getUri() + requestURI).query(query).build().encode().toUri();
? ? log.info("real request url: {}", redirectURL.toString());
? ? return redirectURL;
? }
? private HttpMethod extractMethod(HttpServletRequest request) {
? ? return valueOf(request.getMethod());
? }
? private HttpHeaders extractHeaders(HttpServletRequest request) {
? ? HttpHeaders headers = new HttpHeaders();
? ? Enumeration<String> headerNames = request.getHeaderNames();
? ? while (headerNames.hasMoreElements()) {
? ? ? String name = headerNames.nextElement();
? ? ? List<String> value = list(request.getHeaders(name));
? ? ? headers.put(name, value);
? ? }
? ? return headers;
? }
? private byte[] extractBody(HttpServletRequest request) throws IOException {
? ? return toByteArray(request.getInputStream());
? }
}
java復(fù)制代碼public class HttpResponseMapper {
? public void map(ResponseEntity<byte[]> responseEntity, HttpServletResponse response) throws IOException {
? ? setStatus(responseEntity, response);
? ? setHeaders(responseEntity, response);
? ? setBody(responseEntity, response);
? }
? private void setStatus(ResponseEntity<byte[]> responseEntity, HttpServletResponse response) {
? ? response.setStatus(responseEntity.getStatusCode().value());
? }
? private void setHeaders(ResponseEntity<byte[]> responseEntity, HttpServletResponse response) {
? ? responseEntity.getHeaders().forEach((name, values) -> values.forEach(value -> response.addHeader(name, value)));
? }
? /**
? ?* 把結(jié)果寫回客戶端
? ?*
? ?* @param responseEntity
? ?* @param response
? ?* @throws IOException
? ?*/
? private void setBody(ResponseEntity<byte[]> responseEntity, HttpServletResponse response) throws IOException {
? ? if (responseEntity.getBody() != null) {
? ? ? response.getOutputStream().write(responseEntity.getBody());
? ? }
? }
}

以下為實際處理邏輯RoutingDelegateService的代碼

@Slf4j
@Service
public class RoutingDelegateService {
? private HttpResponseMapper responseMapper;
? private HttpRequestMapper requestMapper;
? @Resource
? private RestTemplate restTemplate;
? /**
? ?* 根據(jù)相應(yīng)策略轉(zhuǎn)發(fā)請求到對應(yīng)后端服務(wù)
? ?*
? ?* @param instance RouteInstance
? ?* @param request ?HttpServletRequest
? ?* @param response HttpServletResponse
? ?*/
? public void doForward(RouteInstance instance, HttpServletRequest request, HttpServletResponse response) {
? ? boolean shouldLB = StringUtils.startsWith(instance.getUri(), MyConstants.LB_PREFIX);
? ? if (shouldLB) {
? ? ? // 需要負載均衡,獲取appName
? ? ? String appName = StringUtils.substringAfter(instance.getUri(), MyConstants.LB_PREFIX);
? ? ? //從請求頭中獲取是否必須按user去路由到同一節(jié)點
? ? ? // 可用節(jié)點
? ? ? ServerInstance chooseInstance = chooseLBInstance(appName);
? ? ? if (chooseInstance == null) {
? ? ? ? // 無可用節(jié)點,返回異常,
? ? ? ? JSONObject result = new JSONObject();
? ? ? ? result.put("status", MyConstants.NO_AVAILABLE_NODE_STATUS);
? ? ? ? result.put("msg", MyConstants.NO_AVAILABLE_NODE_MSG);
? ? ? ? renderString(response, result.toJSONString());
? ? ? ? return;
? ? ? } else {
? ? ? ? //設(shè)置route instance uri 為負載均衡之后的URI地址
? ? ? ? String uri = MyConstants.HTTP_PREFIX + chooseInstance.getHost() + ":" + chooseInstance.getPort();
? ? ? ? instance.setUri(uri);
? ? ? }
? ? }
? ? // 轉(zhuǎn)發(fā)請求
? ? try {
? ? ? goForward(request, response, instance);
? ? } catch (Exception e) {
? ? ? // 連接超時、返回異常
? ? ? e.printStackTrace();
? ? ? log.error("request error {}", e.getMessage());
? ? ? JSONObject result = new JSONObject();
? ? ? result.put("status", MyConstants.UNKNOWN_EXCEPTION_STATUS);
? ? ? result.put("msg", e.getMessage());
? ? ? renderString(response, result.toJSONString());
? ? }
? }
? /**
? ?* 發(fā)送請求到對應(yīng)后端服務(wù)
? ?*
? ?* @param request ?HttpServletRequest
? ?* @param response HttpServletResponse
? ?* @param instance RouteInstance
? ?* @throws IOException
? ?*/
? private void goForward(HttpServletRequest request, HttpServletResponse response, RouteInstance instance) throws IOException {
? ? requestMapper = new HttpRequestMapper();
? ? RequestEntity<byte[]> requestEntity = requestMapper.map(request, instance);
? ? //用byte數(shù)組處理返回結(jié)果,因為返回結(jié)果可能是字符串也可能是數(shù)據(jù)流
? ? ResponseEntity<byte[]> responseEntity = restTemplate.exchange(requestEntity, byte[].class);
? ? responseMapper = new HttpResponseMapper();
? ? responseMapper.map(responseEntity, response);
? }
? private ServerInstance chooseLBInstance(String appName) {
? ? //TODO 根據(jù)appName 選擇對應(yīng)的host
? ? ServerInstance instance = new ServerInstance();
? ? instance.setHost("127.0.0.1");
? ? instance.setPort(10000);
? ? return instance;
? }
? /**
? ?* 寫回字符串結(jié)果到客戶端
? ?*
? ?* @param response
? ?* @param string
? ?*/
? public void renderString(HttpServletResponse response, String string) {
? ? try {
? ? ? response.setStatus(200);
? ? ? response.setContentType("application/json");
? ? ? response.setCharacterEncoding("utf-8");
? ? ? response.getWriter().print(string);
? ? } catch (IOException e) {
? ? ? e.printStackTrace();
? ? }
? }
}

啟動server,瀏覽器中輸入http://127.0.0.1:8080/oioweb/api/common/rubbish?name=香蕉,就可以把請求代理到https://api.oioweb.cn/api/common/rubbish?name=香蕉了

{
    "code": 200,
    "result": [
        {
            "name": "香蕉",
            "type": 2,
            "aipre": 0,
            "explain": "廚余垃圾是指居民日常生活及食品加工、飲食服務(wù)、單位供餐等活動中產(chǎn)生的垃圾。",
            "contain": "常見包括菜葉、剩菜、剩飯、果皮、蛋殼、茶渣、骨頭等",
            "tip": "純流質(zhì)的食物垃圾、如牛奶等,應(yīng)直接倒進下水口。有包裝物的濕垃圾應(yīng)將包裝物去除后分類投放、包裝物請投放到對應(yīng)的可回收物或干垃圾容器"
        },
        {
            "name": "香蕉干",
            "type": 2,
            "aipre": 0,
            "explain": "廚余垃圾是指居民日常生活及食品加工、飲食服務(wù)、單位供餐等活動中產(chǎn)生的垃圾。",
            "contain": "常見包括菜葉、剩菜、剩飯、果皮、蛋殼、茶渣、骨頭等",
            "tip": "純流質(zhì)的食物垃圾、如牛奶等,應(yīng)直接倒進下水口。有包裝物的濕垃圾應(yīng)將包裝物去除后分類投放、包裝物請投放到對應(yīng)的可回收物或干垃圾容器"
        },
        {
            "name": "香蕉皮",
            "type": 2,
            "aipre": 0,
            "explain": "廚余垃圾是指居民日常生活及食品加工、飲食服務(wù)、單位供餐等活動中產(chǎn)生的垃圾。",
            "contain": "常見包括菜葉、剩菜、剩飯、果皮、蛋殼、茶渣、骨頭等",
            "tip": "純流質(zhì)的食物垃圾、如牛奶等,應(yīng)直接倒進下水口。有包裝物的濕垃圾應(yīng)將包裝物去除后分類投放、包裝物請投放到對應(yīng)的可回收物或干垃圾容器"
        }
    ],
    "msg": "success"
}

到此這篇關(guān)于SpringBoot實現(xiàn)反向代理的示例代碼的文章就介紹到這了,更多相關(guān)SpringBoot 反向代理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JavaEE開發(fā)基于Eclipse的環(huán)境搭建以及Maven Web App的創(chuàng)建

    JavaEE開發(fā)基于Eclipse的環(huán)境搭建以及Maven Web App的創(chuàng)建

    本文主要介紹了如何在Eclipse中創(chuàng)建的Maven Project,本文是JavaEE開發(fā)的開篇,也是基礎(chǔ)。下面內(nèi)容主要包括了JDK1.8的安裝、JavaEE版本的Eclipse的安裝、Maven的安裝、Tomcat 9.0的配置、Eclipse上的M2Eclipse插件以及STS插件的安裝。
    2017-03-03
  • java多線程編程之管道通信詳解

    java多線程編程之管道通信詳解

    這篇文章主要為大家詳細介紹了java多線程編程之線程間的通信,探討使用管道進行通信,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • 解決JPA?save()方法null值覆蓋掉mysql預(yù)設(shè)的默認值問題

    解決JPA?save()方法null值覆蓋掉mysql預(yù)設(shè)的默認值問題

    這篇文章主要介紹了解決JPA?save()方法null值覆蓋掉mysql預(yù)設(shè)的默認值問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • java枚舉類型-Enum

    java枚舉類型-Enum

    本文詳細介紹了 Java1.5 引入的新特性枚舉中的關(guān)鍵字enum,運用大量的代碼加以解釋,相信可以幫助到正在學(xué)習(xí)該知識的小伙伴,大家可以參考一下
    2021-08-08
  • Java 進制轉(zhuǎn)換的方法

    Java 進制轉(zhuǎn)換的方法

    這篇文章介紹了Java 進制轉(zhuǎn)換的方法,有需要的朋友可以參考一下
    2013-09-09
  • Spring+Quartz實現(xiàn)動態(tài)任務(wù)調(diào)度詳解

    Spring+Quartz實現(xiàn)動態(tài)任務(wù)調(diào)度詳解

    這篇文章主要介紹了Spring+Quartz實現(xiàn)動態(tài)任務(wù)調(diào)度詳解,最近經(jīng)?;趕pring?boot寫定時任務(wù),并且是使用注解的方式進行實現(xiàn),分成的方便將自己的類注入spring容器,需要的朋友可以參考下
    2024-01-01
  • idea聚合工程搭建過程詳解

    idea聚合工程搭建過程詳解

    本章主要以order訂單服務(wù)來遠程調(diào)用payment支付服務(wù)為例,當然這里只是簡單的一個遠程調(diào)用,沒有太復(fù)雜的邏輯,重點是要掌握的是maven的聚合工程搭建,微服務(wù)分模塊的思想,每一個步驟我都會詳細記錄,并且文章下方還提供了git源碼地址
    2022-06-06
  • SpringBoot實現(xiàn)異步的八種方法

    SpringBoot實現(xiàn)異步的八種方法

    Spring Boot 的異步處理主要是通過非阻塞I/O和回調(diào)機制來實現(xiàn)的,目的是提高應(yīng)用的并發(fā)性能,它支持多種方式來創(chuàng)建異步任務(wù),本文給大家介紹了SpringBoot實現(xiàn)異步的八種方法,需要的朋友可以參考下
    2024-07-07
  • MyBatis一級緩存與二級緩存原理與作用分析

    MyBatis一級緩存與二級緩存原理與作用分析

    mybatis-plus是一個Mybatis的增強工具,在Mybatis的基礎(chǔ)上只做增強不做改變,為簡化開發(fā)、提高效率而生,這篇文章帶你了解Mybatis的一級和二級緩存
    2022-12-12
  • java遞歸實現(xiàn)拼裝多個api的結(jié)果操作方法

    java遞歸實現(xiàn)拼裝多個api的結(jié)果操作方法

    本文給大家分享java遞歸實現(xiàn)拼裝多個api的結(jié)果的方法,說白了就是好幾個API結(jié)果拼裝成的,本文通過實例代碼給大家介紹的非常詳細,需要的朋友參考下吧
    2021-09-09

最新評論