使用Spring Boot重試失敗編寫一個反向代理 - Ashrith
在這個微服務世界中,我們總是強調透過 API/服務閘道器層傳遞任何 HTTP 請求,該層連線多個微服務,並有一個最低要求,即記錄每個服務的所有請求和響應以獲得更清晰的可見性。
我們可以考慮在以下場景中編寫我們的反向代理層。
- 1.假設具有API服務像“PHP”或“Python”等另一種語言,我們需要以部分地變換該請求。
- 2. 新增授權或在服務未能響應請求時實現重試
- 3. 記錄所有流經此反向代理層的請求和響應,稍後推送到某些日誌記錄堆疊,如 ELK
- 4. 為服務傳播實現我們的自定義跟蹤邏輯.
- 5. 或者重新發明輪子只是為了好玩
在這個示例中,我使用 Springboot 及其嵌入式 Tomcat 伺服器。和 Spring Spring retry Dependency 來實現請求失敗時的重試邏輯。
Springboot 控制器攔截所有請求並將它們傳遞給代理服務類:
@RestController public class ProxyController { @Autowired ProxyService service; @RequestMapping("/**") public ResponseEntity<String> sendRequestToSPM(@RequestBody(required = false) String body, HttpMethod method, HttpServletRequest request, HttpServletResponse response) throws URISyntaxException { return service.processProxyRequest(body,method,request,response, UUID.randomUUID().toString()); } } |
ProxySerivce:
當我們收到來自控制器的請求時,Simple RestTemplate 會向所需的域執行 HTTP 請求。這是可以擴充套件到我們的要求的類,例如執行自定義日誌記錄,或者在這個例子中實現重試,當 HTTP 呼叫失敗時。
@Service public class ProxyService { String domain = "example.com"; private final static Logger logger = LogManager.getLogger(ProxyService.class); @Retryable(exclude = { HttpStatusCodeException.class}, include = Exception.class, backoff = @Backoff(delay = 5000, multiplier = 4.0), maxAttempts = 4) public ResponseEntity<String> processProxyRequest(String body, HttpMethod method, HttpServletRequest request, HttpServletResponse response, String traceId) throws URISyntaxException { ThreadContext.put("traceId", traceId); String requestUrl = request.getRequestURI(); //log if required in this line URI uri = new URI("https", null, domain, -1, null, null, null); // replacing context path form urI to match actual gateway URI uri = UriComponentsBuilder.fromUri(uri) .path(requestUrl) .query(request.getQueryString()) .build(true).toUri(); HttpHeaders headers = new HttpHeaders(); Enumeration<String> headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()) { String headerName = headerNames.nextElement(); headers.set(headerName, request.getHeader(headerName)); } headers.set("TRACE", traceId); headers.remove(HttpHeaders.ACCEPT_ENCODING); HttpEntity<String> httpEntity = new HttpEntity<>(body, headers); ClientHttpRequestFactory factory = new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()); RestTemplate restTemplate = new RestTemplate(factory); try { ResponseEntity<String> serverResponse = restTemplate.exchange(uri, method, httpEntity, String.class); HttpHeaders responseHeaders = new HttpHeaders(); responseHeaders.put(HttpHeaders.CONTENT_TYPE, serverResponse.getHeaders().get(HttpHeaders.CONTENT_TYPE)); logger.info(serverResponse); return serverResponse; } catch (HttpStatusCodeException e) { logger.error(e.getMessage()); return ResponseEntity.status(e.getRawStatusCode()) .headers(e.getResponseHeaders()) .body(e.getResponseBodyAsString()); } } @Recover public ResponseEntity<String> recoverFromRestClientErrors(Exception e, String body, HttpMethod method, HttpServletRequest request, HttpServletResponse response, String traceId) { logger.error("retry method for the following url " + request.getRequestURI() + " has failed" + e.getMessage()); logger.error(e.getStackTrace()); throw new RuntimeException("There was an error trying to process you request. Please try again later"); } } 主類: @SpringBootApplication @EnableRetry public class ProxyAppApplication { public static void main(String[] args) { SpringApplication.run(ProxyAppApplication.class, args); } } pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.1</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.ashrithgn.example</groupId> <artifactId>proxyApp</artifactId> <version>0.0.1-SNAPSHOT</version> <name>proxyApp</name> <description>Demo project for Spring Boot</description> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> <version>1.2.5.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.2.8.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> |
相關文章
- Spring Boot package打包失敗Spring BootPackage
- 配置 Spring Batch 批處理失敗重試機制SpringBAT
- Spring Cloud Stream消費失敗後的處理策略(一):自動重試SpringCloud
- Spring Boot 動手寫一個 StartSpring Boot
- TestNG測試框架之失敗測試重跑框架
- Spring Boot中@Retryable重試教程Spring Boot
- Spring Cloud 學習筆記 ——Spring Cloud Config 請求失敗重試SpringCloud筆記
- 使用HTTP代理失敗的常見原因HTTP
- Spring Boot 樂觀鎖加鎖失敗 - 使用AOP恢復錯誤Spring Boot
- Spring Boot 樂觀鎖加鎖失敗 - 整合AOPSpring Boot
- MQ消費失敗,自動重試思路MQ
- 用了Redisson的Spring Boot Starter搞的我都想重寫個RedisSpring Boot
- wsl 2 unbuntu 部署 asp.net core 使用 nginx 做反向代理,除錯檔案上傳失敗ASP.NETNginx除錯
- 使用代理ip訪問網站卻失敗網站
- spring-boot - 編寫自己的starterSpringboot
- Cypress系列(65)- 測試執行失敗自動重試
- 尋找寫程式碼感覺(三)之使用 Spring Boot 編寫介面Spring Boot
- nginx 反向代理 公用一個外網埠Nginx
- nginx 反向代理 swoole 使用Nginx
- Spring Boot 老啟動失敗,這次再也不怕了!Spring Boot
- 攤牌了!我要手寫一個“Spring Boot”Spring Boot
- 學記:為spring boot寫一個自動配置Spring Boot
- 22. 從零用Rust編寫正反向代理,一個資料包的神奇HTTP歷險記!RustHTTP
- perl 資料庫連結失敗重試機制資料庫
- Spring Boot系列十七 Spring Boot 整合 websocket,使用RabbitMQ做為訊息代理Spring BootWebMQ
- 如何建立自己的Spring Boot Starter併為其編寫單元測試Spring Boot
- 反向代理學習筆記(一) Nginx與反向代理緒論筆記Nginx
- 使用Python請求http/https時設定失敗重試次數PythonHTTP
- 使用Python編寫一個滲透測試探測工具Python
- 其中一個mview失敗,一個命令來剔除失敗mview的所需的logView
- 在Spring boot中通過ApplicationContext獲取bean失敗Spring BootAPPContextBean
- pbootcms後臺“登入失敗:表單提交校驗失敗,請重新整理後重試”boot
- 重學 Spring BootSpring Boot
- Feign失敗重試與全域性異常捕獲
- KU FPGA FLASH boot失敗debugFPGAboot
- 效能測試: 編寫一個 Locust 檔案
- Spring Boot乾貨系列:(十二)Spring Boot使用單元測試Spring Boot
- Micronaut使用提前編譯支援Spring Boot編譯Spring Boot