SpringCloud(5)---Feign服務調用
上一篇寫了通過Ribbon進行服務調用,這篇其它都一樣,唯一不一樣的就是通過Feign進行服務調用。
注冊中心和商品微服務不變,和上篇博客一樣,具體參考:SpringCloud(4)---Ribbon服務調用,源碼分析
這邊只重寫訂單微服務。
項目代碼GitHub地址:https://github.com/yudiandemingzi/spring-cloud-study
一、OrderService 訂單微服務
1、pom.xml
這里相對於上一篇的訂單微服務只要新添加一個jar包
<!--feign依賴--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
2、application.yml
server: port: 9001 #指定注冊中心地址 eureka: client: serviceUrl: defaultZone: http://localhost:7001/eureka/ #服務的名稱 spring: application: name: order-service #自定義負載均衡策略(一般不用配用默認的) product-service: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
3、SpringBoot啟動類
@SpringBootApplication //添加@EnableFeignClients注解 @EnableFeignClients public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } }
4、ProductOrderServiceImpl訂單接口實現類
訂單實體類和訂單接口這里就不寫類,和上篇一樣
@Service public class ProductOrderServiceImpl implements ProductOrderService { @Autowired private ProductClient productClient; @Override public ProductOrder save(int userId, int productId) { //獲取json格式的字符串數據 String response = productClient.findById(productId); //Json字符串轉換成JsonNode對象 JsonNode jsonNode = JsonUtils.str2JsonNode(response); //將數據封裝到訂單實體中 ProductOrder productOrder = new ProductOrder(); productOrder.setCreateTime(new Date()); productOrder.setUserId(userId); productOrder.setTradeNo(UUID.randomUUID().toString()); //獲取商品名稱和商品價格 productOrder.setProductName(jsonNode.get("name").toString()); productOrder.setPrice(Integer.parseInt(jsonNode.get("price").toString())); //因為在商品微服務配置了集群,所以這里打印看下調用了是哪個集群節點,輸出端口號。 System.out.println(jsonNode.get("name").toString()); return productOrder; } }
5、ProductClient類
可以把這里類理解成,就是你需要調用的微服務的controller層(這里指商品微服務),這樣相對於Ribbon來講代碼的可讀性就高多了。
/** * 商品服務客戶端 * name = "product-service"是服務端名稱 */ @FeignClient(name = "product-service") public interface ProductClient { //這樣組合就相當於http://product-service/api/v1/product/find @GetMapping("/api/v1/product/find") String findById(@RequestParam(value = "id") int id); }
6、JsonUtils工具類
/** * json工具類 */ public class JsonUtils { private static final ObjectMapper objectMappper = new ObjectMapper(); //json字符串轉JsonNode對象的方法 public static JsonNode str2JsonNode(String str){ try { return objectMappper.readTree(str); } catch (IOException e) { return null; } } }
7、OrderController類
@RestController @RequestMapping("api/v1/order") public class OrderController { @Autowired private ProductOrderService productOrderService; @RequestMapping("save") public Object save(@RequestParam("user_id")int userId, @RequestParam("product_id") int productId){ return productOrderService.save(userId, productId); } }
8、查看運行結果
同時同樣它可以達到負載均衡的效果。
二、概念講解
在使用Feign的時候,要注意使用requestBody,應該使用@PostMapping
1、執行流程
總到來說,Feign的源碼實現的過程如下:
(1)首先通過@EnableFeignCleints注解開啟FeignCleint
(2)根據Feign的規則實現接口,並加@FeignCleint注解
(3)程序啟動后,會進行包掃描,掃描所有的@ FeignCleint的注解的類,並將這些信息注入到ioc容器中。
(4)當接口的方法被調用,通過jdk的代理,來生成具體的RequesTemplate
(5)RequesTemplate在生成Request
(6)Request交給Client去處理,其中Client可以是HttpUrlConnection、HttpClient也可以是Okhttp
(7)最后Client被封裝到LoadBalanceClient類,這個類結合類Ribbon做到了負載均衡。
2、Feign和Ribbon比較優點
(1) feign本身里面就包含有了ribbon,只是對於ribbon進行進一步封裝
(2) feign自身是一個聲明式的偽http客戶端,寫起來更加思路清晰和方便
(3) fegin是一個采用基於接口的注解的編程方式,更加簡便
最后推薦一篇對源碼解析不錯的博客:深入理解Feign之源碼解析。
我只是偶爾安靜下來,對過去的種種思忖一番。那些曾經的舊時光里即便有過天真愚鈍,也不值得譴責。畢竟,往后的日子,還很長。不斷鼓勵自己,
天一亮,又是嶄新的起點,又是未知的征程(上校7)