上一篇:SpringCloud 入門(1)—— nacos 注冊中心-CSDN博客
1.RestTemplate 跨服務請求
RestTemplate
是 Spring 框架中的一個同步客戶端,用于與 HTTP 服務進行交互。它簡化了與 HTTP 服務器通信的過程,并且提供了對多種 HTTP 方法(如 GET、POST、PUT、DELETE 等)的支持,用于發送跨服務請求。
<!--負載均衡器--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency>
?1.1 配置Bean
在 Spring Boot 2.0 及以上版本中,RestTemplate
不再自動配置,因此需要自己創建 RestTemplate
Bean
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;@Configuration
public class RemoteCallConfig {@Bean@LoadBalancedpublic RestTemplate restTemplate() {return new RestTemplate();}
}
?1.2 構造函數注入RestTemplate
spring不推薦使用@AutoWired注解,進行自動注入。我們可以自己書寫構造函數注入
public class CartServiceImpl extends ServiceImpl<CartMapper, Cart> implements ICartService {private RestTemplate restTemplate;public CartServiceImpl(RestTemplate restTemplate) {this.restTemplate = restTemplate;}
}
?也可以通過lombok注解,自動生成構造函數,進行注入
?@AllArgsConstructor全參構造注解,但使用該注解可能導致一些不需要通過構造傳參的變量,也會生成構造函數
@RequiredArgsConstructor注解,只有通過final修飾值,才會生成構造函數。?因為通過final修飾后,必需在定義時進行賦初始值,或者通過構造函數初始化
@RequiredArgsConstructor
public class CartServiceImpl extends ServiceImpl<CartMapper, Cart> implements ICartService {private final RestTemplate restTemplate;
}
1.3 RestTemplate的使用
ResponseEntity<List<ItemDTO>> response = restTemplate.exchange("http://localhost:8081/items?ids={ids}",//請求路徑HttpMethod.GET,//請求方式null,//請求實體new ParameterizedTypeReference<List<ItemDTO>>() { },//返回值類型List<ItemDTO>Map.of("ids", CollUtil.join(itemIds, ","))//請求參數,
);
//CollUtil.join(itemIds, ",")將集合轉換成字符串,用逗號分隔。即集合123轉化為字符串1,2,3。
???????//Map.of() "ids"是鍵,字符串1,2,3是值
?我們可以看到http://localhost:8081/items中存在硬編碼,這里我們可以使用上一篇學習到的nacos服務注冊中心,將該微服務注冊到nacos中,然后通過服務名發送請求。
當你通過 RestTemplate
發起請求時,Spring Cloud 提供了客戶端負載均衡機制來決定具體發送到哪臺計算機。默認的負載均衡策略是輪詢(Round Robin)
這樣如果該微服務在多臺計算機都進行部署,并在nacos注冊后,就可以實現負載均衡了
nacos注冊中心地址教程:SpringCloud 入門(1)—— nacos 注冊中心-CSDN博客
注冊中心搭建完成后,使用構造函數將注入
@RequiredArgsConstructor
public class CartServiceImpl extends ServiceImpl<CartMapper, Cart> implements ICartService {private final RestTemplate restTemplate;private final DiscoveryClient discoveryClient;//注冊中心
}
?默認情況下,采用輪詢的方式進行負載均衡
// 發起請求時,直接使用服務名稱
ResponseEntity<List<ItemDTO>> response = restTemplate.exchange("http://item-service/items?ids={ids}", // 使用服務名稱而不是具體實例 URIHttpMethod.GET,null,new ParameterizedTypeReference<List<ItemDTO>>() {},Map.of("ids", CollUtil.join(itemIds, ","))
);
?指定服務實例方式為隨機
// 查找 item-service 服務的實例列表List<ServiceInstance> instances = discoveryClient.getInstances("item-service");if (instances.isEmpty()) {throw new RuntimeException("No instances available for item-service");}// 隨機選擇一個服務實例Random random = new Random();ServiceInstance instance = instances.get(random.nextInt(instances.size()));// 發起請求,查詢商品ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(instance.getUri() + "/items?ids={ids}",HttpMethod.GET,null,new ParameterizedTypeReference<List<ItemDTO>>() {},Map.of("ids", CollUtil.join(itemIds, ",")));
利用Nacos實現了服務的治理,利用RestTemplate實現了服務的遠程調用。但是遠程調用的代碼太復雜了,下面介紹一款更簡單的方法OpenFeign。
2.OpenFeign 跨服務請求
OpenFeign 是一個聲明式的 Web 服務客戶端,它使得編寫 HTTP 客戶端變得更加簡單。它是 Netflix Feign 的增強版本,并且與 Spring Cloud 深度集成,允許開發者通過創建接口并用注解描述 HTTP 請求來定義和使用 RESTful 客戶端。這簡化了代碼的編寫,因為你不需要構建 URL、手動解析 JSON 或處理其他繁瑣的任務,對跨服務請求進行簡化了。
2.1 設計思路
為了避免重復編碼,下面有兩種抽取思路:
-
思路1:抽取到微服務之外的公共module(適用與聚合工程)
-
思路2:每個微服務自己抽取一個module
如圖:
方案1抽取更加簡單,工程結構也比較清晰,但缺點是整個項目耦合度偏高。(適用于maven聚合模塊中使用)
方案2抽取相對麻煩,工程結構相對更復雜,但服務之間耦合度降低。
?下面我們采用第一個思路,新建一名模板api模板,單獨存放openFeign請求
?2.2 導入依賴
<!--openFeign--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!--負載均衡器--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency>
2.3 編寫Feign客戶端
import com.heima.cart.domain.dto.ItemDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;import java.util.Collection;
import java.util.List;@FeignClient("item-service") //遠程請求的服務
public interface ItemClient {@GetMapping("/items")//請求的服務路徑List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
}
?2.4 啟動OpenFeign功能
在需要發送跨服務請求(即:需要用到openFeign功能模塊)的微服務的pom.xml中添加hm-api模塊,
在啟動類上添加注解@EnableFeignClients,開啟openFeign功能 ,并且指明所在客戶端的位置(類)
-
方式1:聲明掃描包:
-
方式2:聲明要用的API客戶端
? 將客戶端注入,發起請求
//注入private final ItemClient itemClient;//發起請求
List<ItemDTO> items = itemClient.queryItemByIds(itemIds);
2.5 openFeign日志
默認情況下,openFeign請求中,后臺是沒有日志的,一旦出錯,很難發現。
需要手動創建config包,配置日志類
import feign.Logger;
import org.springframework.context.annotation.Bean;public class DefaultFeignConfig {@Beanpublic Logger.Level feignLogLevel(){return Logger.Level.FULL;}
}
?在啟動類中,開啟日志,
全局生效:在@EnableFeignClients
中配置,針對所有FeignClient
生效。
@EnableFeignClients(defaultConfiguration = DefaultFeignConfig.class)
2.6 openFeign請求頭
利用openFeign發送請求時,需要攜帶當前發起請求的用戶信息。
這里我們將用戶id放到請求頭中,轉發給另一個微服務。前端對后端發起的請求,交給網關處理,網關負責對jwt進行解析驗證。網關驗證完成后,才會轉交給其他微服務。
后續更新網關處理方案.....
import com.hmall.common.utils.UserContext;
import feign.Logger;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.context.annotation.Bean;public class DefaultFeignConfig {@Beanpublic Logger.Level feignLogLevel(){return Logger.Level.FULL;}@Beanpublic RequestInterceptor userInfoRequestInterceptor(){return new RequestInterceptor() {@Overridepublic void apply(RequestTemplate template) {// 獲取登錄用戶Long userId = UserContext.getUser();if(userId == null) {// 如果為空則直接跳過return;}// 如果不為空則放入請求頭中,傳遞給下游微服務template.header("user-info", userId.toString());}};}
}
2.7 openFeign連接池
Feign底層發起http請求,依賴于其它的框架。其底層支持的http客戶端實現
-
HttpURLConnection:默認實現,不支持連接池
-
Apache HttpClient :支持連接池
-
OKHttp:支持連接池
因此我們通常會使用帶有連接池的客戶端來代替默認的HttpURLConnection。比如,我們使用OK Http.
導入OKHttp依賴
<!--OK http 的依賴 -->
<dependency><groupId>io.github.openfeign</groupId><artifactId>feign-okhttp</artifactId>
</dependency>
application.yml
配置文件中開啟Feign的連接池功能,重啟服務,連接池就生效了。
feign:okhttp:enabled: true # 開啟OKHttp功能
下一篇
SpringCloud 入門(3)—— Nacos配置中心-CSDN博客