最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

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

功能實(shí)現(xiàn)

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

請(qǐng)求攔截

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

@Slf4j
@Component
public class ForwardRoutingFilter extends OncePerRequestFilter implements Ordered {
? @Override
? public int getOrder() {
? ? return 0; // 值越小,優(yōu)先級(jí)別越高
? }
? @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);
? }
}

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

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

接下來(lái),我們的實(shí)現(xiàn)就圍繞著這個(gè)過(guò)濾器去做了。

配置規(guī)則定義

通常情況下,我們會(huì)在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ù)說(shuō)明:

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

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

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

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

@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;
}

代理實(shí)現(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>

接下來(lái)就是改造我們之前的ForwardRoutingFilter 過(guò)濾器類了

@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為空,不進(jìn)行代理轉(zhuǎn)發(fā),交由過(guò)濾器鏈后續(xù)過(guò)濾器處理
? ? ? filterChain.doFilter(request, response);
? ? } else {
? ? ? // 創(chuàng)建一個(gè)service 去處理代理轉(zhuǎn)發(fā)邏輯
? ? ? routingDelegate.doForward(instance, request, response);
? ? ? return;
? ? }
? }
}

代理轉(zhuǎn)發(fā)會(huì)使用到RestTemplate,默認(rèn)使用的是java.net.URLConnection去進(jìn)行http請(qǐng)求,我們這邊替換成httpclient,具體配置就不貼出來(lái)了。
編寫兩個(gè)工具欄,分別用于轉(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());
? ? //處理中文被自動(dòng)編碼問題
? ? 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());
? ? }
? }
}

以下為實(shí)際處理邏輯RoutingDelegateService的代碼

@Slf4j
@Service
public class RoutingDelegateService {
? private HttpResponseMapper responseMapper;
? private HttpRequestMapper requestMapper;
? @Resource
? private RestTemplate restTemplate;
? /**
? ?* 根據(jù)相應(yīng)策略轉(zhuǎn)發(fā)請(qǐng)求到對(duì)應(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) {
? ? ? // 需要負(fù)載均衡,獲取appName
? ? ? String appName = StringUtils.substringAfter(instance.getUri(), MyConstants.LB_PREFIX);
? ? ? //從請(qǐng)求頭中獲取是否必須按user去路由到同一節(jié)點(diǎn)
? ? ? // 可用節(jié)點(diǎn)
? ? ? ServerInstance chooseInstance = chooseLBInstance(appName);
? ? ? if (chooseInstance == null) {
? ? ? ? // 無(wú)可用節(jié)點(diǎn),返回異常,
? ? ? ? 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 為負(fù)載均衡之后的URI地址
? ? ? ? String uri = MyConstants.HTTP_PREFIX + chooseInstance.getHost() + ":" + chooseInstance.getPort();
? ? ? ? instance.setUri(uri);
? ? ? }
? ? }
? ? // 轉(zhuǎn)發(fā)請(qǐng)求
? ? try {
? ? ? goForward(request, response, instance);
? ? } catch (Exception e) {
? ? ? // 連接超時(shí)、返回異常
? ? ? 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ā)送請(qǐng)求到對(duì)應(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é)果,因?yàn)榉祷亟Y(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 選擇對(duì)應(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();
? ? }
? }
}

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

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

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

相關(guān)文章

  • Java系統(tǒng)運(yùn)行緩慢等問題的排查思路

    Java系統(tǒng)運(yùn)行緩慢等問題的排查思路

    這篇文章主要介紹了Java系統(tǒng)運(yùn)行緩慢等問題的排查思路,讀者可以根據(jù)具體情況具體分析,從而解決問題
    2021-04-04
  • 使用JDBC連接Mysql 8.0.11出現(xiàn)了各種錯(cuò)誤的解決

    使用JDBC連接Mysql 8.0.11出現(xiàn)了各種錯(cuò)誤的解決

    這篇文章主要介紹了使用JDBC連接Mysql 8.0.11出現(xiàn)了各種錯(cuò)誤的解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • Java的深拷貝和淺拷貝深入了解

    Java的深拷貝和淺拷貝深入了解

    這篇文章主要為大家介紹了Java的深拷貝和淺拷貝,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-01-01
  • Java Swing組件單選框JRadioButton用法示例

    Java Swing組件單選框JRadioButton用法示例

    這篇文章主要介紹了Java Swing組件單選框JRadioButton用法,結(jié)合具體實(shí)例形式分析了Swing單選框JRadioButton的使用方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下
    2017-11-11
  • POS機(jī)如何與Java交互的方式探討

    POS機(jī)如何與Java交互的方式探討

    本文深入探討POS機(jī)與Java語(yǔ)言的交互機(jī)制,詳細(xì)介紹了通過(guò)RESTfulAPI、Socket編程和消息隊(duì)列等方式實(shí)現(xiàn)數(shù)據(jù)交換和功能調(diào)用,文章還包含了代碼示例、狀態(tài)圖與關(guān)系圖,幫助開發(fā)者理解和實(shí)現(xiàn)POS機(jī)與Java之間的高效交互
    2024-09-09
  • Spring詳細(xì)講解循環(huán)依賴是什么

    Spring詳細(xì)講解循環(huán)依賴是什么

    這篇文章主要介紹了Java中的Spring循環(huán)依賴詳情,文章基于Java的相關(guān)資料展開詳細(xì)介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-08-08
  • Java深入探究關(guān)鍵字abstract的使用

    Java深入探究關(guān)鍵字abstract的使用

    如果一個(gè)方法使用 abstract 來(lái)修飾,則說(shuō)明該方法是抽象方法,抽象方法只有聲明沒有實(shí)現(xiàn)。需要注意的是 abstract 關(guān)鍵字只能用于普通方法,不能用于 static 方法或者構(gòu)造方法中
    2022-05-05
  • 教你構(gòu)建第一個(gè)Java Applet程序

    教你構(gòu)建第一個(gè)Java Applet程序

    本文的主要目的是創(chuàng)建一個(gè)簡(jiǎn)單的Java applet,需要的朋友可以參考下
    2014-10-10
  • 詳解java面試題中的i++和++i

    詳解java面試題中的i++和++i

    這篇文章主要介紹了java面試題中的i++和++i的相關(guān)資料,需要的朋友可以參考下
    2018-03-03
  • Java中的FutureTask實(shí)現(xiàn)異步任務(wù)代碼實(shí)例

    Java中的FutureTask實(shí)現(xiàn)異步任務(wù)代碼實(shí)例

    這篇文章主要介紹了Java中的FutureTask實(shí)現(xiàn)異步任務(wù)代碼實(shí)例,普通的線程執(zhí)行是無(wú)法獲取到執(zhí)行結(jié)果的,FutureTask?間接實(shí)現(xiàn)了?Runnable?和?Future?接口,可以得到子線程耗時(shí)操作的執(zhí)行結(jié)果,AsyncTask?異步任務(wù)就是使用了該機(jī)制,需要的朋友可以參考下
    2024-01-01

最新評(píng)論

古田县| 客服| 阳朔县| 横峰县| 连城县| 静海县| 古交市| 黄平县| 高州市| 正安县| 岑巩县| 寿宁县| 鹤庆县| 靖远县| 改则县| 河北省| 沙洋县| 石狮市| 吴旗县| 同德县| 南乐县| 恩施市| 九龙城区| 开化县| 措美县| 盐边县| 德保县| 左云县| 沈阳市| 报价| 永城市| 星子县| 玛曲县| 克拉玛依市| 景德镇市| 平陆县| 营口市| 微博| 遂溪县| 商都县| 江城|