RPC框架 和 fegin原理


打個比方,你有一些想法,你把他們變成文字寫在信紙上,這是http

你把這個信紙塞進信封,這個信封是tcp

你把這個信封寫上地址交給郵局,這地址是IP

一層套一層

 

會話層,表示層,應用層歸到一起  就是應用層,,http 和 rpc 都是 應用層

 

 

 

2. fegin 原理

1 SpringCloud 中 Feign 核心原理

如果不了解 SpringCloud 中 Feign 核心原理,不會真正的了解 SpringCloud 的性能優化和配置優化,也就不可能做到真正掌握 SpringCloud。

本章從Feign 遠程調用的重要組件開始,圖文並茂的介紹 Feigh 遠程調用的執行流程、Feign 本地 JDK Proxy 實例的創建流程,徹底的為大家解讀 SpringCloud 的核心知識。使得廣大的工程師不光做到知其然,更能知其所以然。

1.1 簡介:Feign遠程調用的基本流程

Feign遠程調用,核心就是通過一系列的封裝和處理,將以JAVA注解的方式定義的遠程調用API接口,最終轉換成HTTP的請求形式,然后將HTTP的請求的響應結果,解碼成JAVA Bean,放回給調用者。Feign遠程調用的基本流程,大致如下圖所示。
在這里插入圖片描述

圖1 Feign遠程調用的基本流程

 

從上圖可以看到,Feign通過處理注解,將請求模板化,當實際調用的時候,傳入參數,根據參數再應用到請求上,進而轉化成真正的 Request 請求。通過Feign以及JAVA的動態代理機制,使得Java 開發人員,可以不用通過HTTP框架去封裝HTTP請求報文的方式,完成遠程服務的HTTP調用。

 

 

一.遠程調用方式 Http和RPC區別

無論是微服務還是分布式服務(都是SOA,都是面向服務編程),都面臨着服務間的遠程調用。那么服務間的遠程調用方式有哪些呢?

常見的遠程調用方式有以下幾種:

  • RPC:Remote Produce Call遠程過程調用,類似的還有RMI(Remote Methods Invoke 遠程方法調用,是JAVA中的概念,是JAVA十三大技術之一)。自定義數據格式,基於原生TCP通信,速度快,效率高。早期的webservice,現在熱門的dubbo,都是RPC的典型

    • RPC的框架:webservie(cxf)、dubbo
    • RMI的框架:hessian
  • Http:http其實是一種網絡傳輸協議,基於TCP,規定了數據傳輸的格式。現在客戶端瀏覽器與服務端通信基本都是采用Http協議。也可以用來進行遠程服務調用。缺點是消息封裝臃腫。

    現在熱門的Rest風格,就可以通過http協議來實現。

    • http的實現技術:HttpClient
  • 相同點:底層通訊都是基於socket,都可以實現遠程調用,都可以實現服務調用服務

  • 不同點:
    RPC:框架有:dubbo、cxf、(RMI遠程方法調用)Hessian
    當使用RPC框架實現服務間調用的時候,要求服務提供方和服務消費方 都必須使用統一的RPC框架,要么都dubbo,要么都cxf

    跨操作系統在同一編程語言內使用
    優勢:調用快、處理快

    http:框架有:httpClient
    當使用http進行服務間調用的時候,無需關注服務提供方使用的編程語言,也無需關注服務消費方使用的編程語言,服務提供方只需要提供restful風格的接口,服務消費方,按照restful的原則,請求服務,即可

    跨系統跨編程語言的遠程調用框架
    優勢:通用性強

    總結:對比RPC和http的區別
    1 RPC要求服務提供方和服務調用方都需要使用相同的技術,要么都hessian,要么都dubbo
    而http無需關注語言的實現,只需要遵循rest規范
    2 RPC的開發要求較多,像Hessian框架還需要服務器提供完整的接口代碼(包名.類名.方法名必須完全一致),否則客戶端無法運行
    3 Hessian只支持POST請求
    4 Hessian只支持JAVA語言

1.1.認識RPC

RPC,即 Remote Procedure Call(遠程過程調用),是一個計算機通信協議。 該協議允許運行於一台計算機的程序調用另一台計算機的子程序,而程序員無需額外地為這個交互作用編程。說得通俗一點就是:A計算機提供一個服務,B計算機可以像調用本地服務那樣調用A計算機的服務。

通過上面的概念,我們可以知道,實現RPC主要是做到兩點:

  • 實現遠程調用其他計算機的服務
    • 要實現遠程調用,肯定是通過網絡傳輸數據。A程序提供服務,B程序通過網絡將請求參數傳遞給A,A本地執行后得到結果,再將結果返回給B程序。這里需要關注的有兩點:
      • 1)采用何種網絡通訊協議?
        • 現在比較流行的RPC框架,都會采用TCP作為底層傳輸協議
      • 2)數據傳輸的格式怎樣?
        • 兩個程序進行通訊,必須約定好數據傳輸格式。就好比兩個人聊天,要用同一種語言,否則無法溝通。所以,我們必須定義好請求和響應的格式。另外,數據在網路中傳輸需要進行序列化,所以還需要約定統一的序列化的方式。
  • 像調用本地服務一樣調用遠程服務
    • 如果僅僅是遠程調用,還不算是RPC,因為RPC強調的是過程調用,調用的過程對用戶而言是應該是透明的,用戶不應該關心調用的細節,可以像調用本地服務一樣調用遠程服務。所以RPC一定要對調用的過程進行封裝

RPC調用流程圖:

在這里插入圖片描述

1.2.認識Http

Http協議:超文本傳輸協議,是一種應用層協議。規定了網絡傳輸的請求格式、響應格式、資源定位和操作的方式等。但是底層采用什么網絡傳輸協議,並沒有規定,不過現在都是采用TCP協議作為底層傳輸協議。說到這里,大家可能覺得,Http與RPC的遠程調用非常像,都是按照某種規定好的數據格式進行網絡通信,有請求,有響應。沒錯,在這點來看,兩者非常相似,但是還是有一些細微差別。

  • RPC並沒有規定數據傳輸格式,這個格式可以任意指定,不同的RPC協議,數據格式不一定相同。
  • Http中還定義了資源定位的路徑,RPC中並不需要
  • 最重要的一點:RPC需要滿足像調用本地服務一樣調用遠程服務,也就是對調用過程在API層面進行封裝。Http協議沒有這樣的要求,因此請求、響應等細節需要我們自己去實現。
    • 優點:RPC方式更加透明,對用戶更方便。Http方式更靈活,沒有規定API和語言,跨語言、跨平台
    • 缺點:RPC方式需要在API層面進行封裝,限制了開發的語言環境。

例如我們通過瀏覽器訪問網站,就是通過Http協議。只不過瀏覽器把請求封裝,發起請求以及接收響應,解析響應的事情都幫我們做了。如果是不通過瀏覽器,那么這些事情都需要自己去完成。

在這里插入圖片描述

1.3.如何選擇?

既然兩種方式都可以實現遠程調用,我們該如何選擇呢?

  • 速度來看,RPC要比http更快,雖然底層都是TCP,但是http協議的信息往往比較臃腫
  • 難度來看,RPC實現較為復雜,http相對比較簡單
  • 靈活性來看,http更勝一籌,因為它不關心實現細節,跨平台、跨語言。

因此,兩者都有不同的使用場景:

  • 如果對效率要求更高,並且開發過程使用統一的技術棧,那么用RPC還是不錯的。
  • 如果需要更加靈活,跨語言、跨平台,顯然http更合適

那么我們該怎么選擇呢?

微服務,更加強調的是獨立、自治、靈活。而RPC方式的限制較多,因此微服務框架中,一般都會采用基於Http的Rest風格服務。

1.4.Http客戶端工具

  既然微服務選擇了Http,那么我們就需要考慮自己來實現對請求和響應的處理。不過開源世界已經有很多的http客戶端工具,能夠幫助我們做這些事情,例如:
  - HttpClient
  - OKHttp
  - URLConnection
  接下來,我們就一起了解一款比較流行的客戶端工具:HttpClient
  public void testGet() throws IOException {
          HttpGet request = new HttpGet("http://www.baidu.com");
          String response = this.httpClient.execute(request, new BasicResponseHandler());
          System.out.println(response);
     }
  public void testPost() throws IOException {
      HttpPost request = new HttpPost("http://www.oschina.net/");
      request.setHeader("User-Agent",
                      "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36");
      String response = this.httpClient.execute(request, new BasicResponseHandler());
      System.out.println(response);
  }
  
HttpClient請求數據后是json字符串,需要我們自己把Json字符串反序列化為對象,我們會使用JacksonJson工具來實現。
`JacksonJson`是SpringMVC內置的json處理工具,其中有一個`ObjectMapper`類,可以方便的實現對json的處理:
#### 對象轉json
// json處理工具
    private ObjectMapper mapper = new ObjectMapper();
    @Test
    public void testJson() throws JsonProcessingException {
        User user = new User();
        user.setId(8L);
        user.setAge(21);
        user.setName("柳岩");
        user.setUserName("liuyan");
        // 序列化
        String json = mapper.writeValueAsString(user);
        System.out.println("json = " + json);
    }

#### json轉對象
```java
// json處理工具
private ObjectMapper mapper = new ObjectMapper();
@Test
public void testJson() throws IOException {
    User user = new User();
    user.setId(8L);
    user.setAge(21);
    user.setName("柳岩");
    user.setUserName("liuyan");
    // 序列化
    String json = mapper.writeValueAsString(user);
    // 反序列化,接收兩個參數:json數據,反序列化的目標類字節碼
    User result = mapper.readValue(json, User.class);
    System.out.println("result = " + result);
}
#### json轉集合
json轉集合比較麻煩,因為你無法同時把集合的class和元素的class同時傳遞到一個參數。
因此Jackson做了一個類型工廠,用來解決這個問題:
```java
// json處理工具
private ObjectMapper mapper = new ObjectMapper();
@Test
public void testJson() throws IOException {
    User user = new User();
    user.setId(8L);
    user.setAge(21);
    user.setName("柳岩");
    user.setUserName("liuyan");
    // 序列化,得到對象集合的json字符串
    String json = mapper.writeValueAsString(Arrays.asList(user, user));
    // 反序列化,接收兩個參數:json數據,反序列化的目標類字節碼
    List<User> users = mapper.readValue(json, mapper.getTypeFactory().constructCollectionType(List.class, User.class));
    for (User u : users) {
        System.out.println("u = " + u);
    }
}
#### json轉任意復雜類型
當對象泛型關系復雜時,類型工廠也不好使了。這個時候Jackson提供了TypeReference來接收類型泛型,然后底層通過反射來獲取泛型上的具體類型。實現數據轉換。
```java
// json處理工具
private ObjectMapper mapper = new ObjectMapper();
@Test
public void testJson() throws IOException {
    User user = new User();
    user.setId(8L);
    user.setAge(21);
    user.setName("柳岩");
    user.setUserName("liuyan");
    // 序列化,得到對象集合的json字符串
    String json = mapper.writeValueAsString(Arrays.asList(user, user));
    // 反序列化,接收兩個參數:json數據,反序列化的目標類字節碼
    List<User> users = mapper.readValue(json, new TypeReference<List<User>>(){});
    for (User u : users) {
        System.out.println("u = " + u);
    }
}
 
 
Spring提供了一個RestTemplate模板工具類,對基於Http的客戶端進行了封裝,並且實現了對象與json的序列化和反序列化,非常方便。RestTemplate並沒有限定Http的客戶端類型,而是進行了抽象,目前常用的3種都有支持:
- HttpClient
- OkHttp
- JDK原生的URLConnection(默認的)
首先在項目中注冊一個`RestTemplate`對象,可以在啟動類位置注冊:
```java
@SpringBootApplication
public class HttpDemoApplication {
 public static void main(String[] args) {
  SpringApplication.run(HttpDemoApplication.class, args);
 }
 @Bean
 public RestTemplate restTemplate() {
        // 默認的RestTemplate,底層是走JDK的URLConnection方式。
  return new RestTemplate();
 }
}
```
 
在測試類中直接`@Autowired`注入:
```java
@RunWith(SpringRunner.class)
@SpringBootTest(classes = HttpDemoApplication.class)
public class HttpDemoApplicationTests {
 @Autowired
 private RestTemplate restTemplate;
 @Test
 public void httpGet() {
  User user = this.restTemplate.getForObject("http://localhost/hello", User.class);
  System.out.println(user);
 }
}
```
- 通過RestTemplate的getForObject()方法,傳遞url地址及實體類的字節碼,RestTemplate會自動發起請求,接收響應,並且幫我們對響應結果進行反序列化。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM