微服務之二:Feign(1)

一: REST客戶端

在spring cloud集羣中,各個角色的通信是基於REST服務。因此在調用服務時,就不可避免地需要使用REST服務的請求客戶端。 本節介紹另一種REST服務:Feign
REST客戶端和瀏覽器作用有基本相同,但不能理解爲瀏覽器,REST客戶端沒有界面。而瀏覽器需要界面。

1.1 REST客戶端

首先介紹兩種Web Service框架 Apache CXF和Restlet

1.1.1 CXF

是目前一個較爲流行的Web Service框架,是Apache下的一個開源項目

public class MyCxfClient {

    public static void main(String[] args) throws IOException {

        //創建WebClient
        WebClient webClient=WebClient.create("http://localhost:8080/person/1");

        //獲取響應
        Response response=webClient.get();

        //獲取響應內容
        InputStream inputStream=(InputStream) response.getEntity();
        String contStr=IOUtils.readStringFromStream(inputStream);

        //輸出字符串
        System.out.println(contStr);
    }

1.1.2 Restlet

public class MyRestletClient {

    public static void main(String[] args) throws Exception {

        ClientResource clientResource = new ClientResource(
                "http://localhost:8080/person/1");

        // 調用GET方法,服務端發佈的是GET
        Representation representation = clientResource
                .get(MediaType.APPLICATION_JSON);

        // 創建JacksonRepresentation實例,將響應轉化爲Map
        JacksonRepresentation jacksonRepresentation = new JacksonRepresentation(
                representation, HashMap.class);

        // 獲取轉化後的Map對象
        Map map = (HashMap) jacksonRepresentation.getObject();

        // 輸出結果
        System.out.println(map.get("id") + "--" + map.get("name") + "--"
                + map.get("age"));
    }

二: Feign框架介紹

  • Feign是Github上的開源項目,目的是簡化Web Service客戶端的開發,在使用時,可以使用註解來修飾接口,被修飾的接口具有訪問Web Service的能力。
  • *初次之外,Feign還支持插件式的編碼器解碼器,使用者可以通過該特性對請求和響應進行不同的封裝與解析。
  • Spring cloud 將Feign集成到Netflix項目中,當與Eureka、Ribbon集成時,Feign就具備了負載均衡的能力
  • 利用的自身的註解@RequestLine(“GET /hello”)

2.1 Get

public interface HelloClient {

    /**
     * 使用了@RequestLine註解
     * 表示用GET方法,向"/hello"發送請求
     */
    @RequestLine("GET /hello")
    String sayHello();
}
-------------------------------------------------------------------------------------------
public static void main(String[] args) {

        // 調用Hello接口
        HelloClient helloClient = Feign.builder().target(HelloClient.class,
                "http://localhost:8080/");
        System.out.println(helloClient.sayHello());
    }

2.2 傳入參數

訪問另外一個地址”/person/{personId}”,需要傳入參數並且返回JSON字符串

public interface PersonClient {

    @RequestLine("GET /person/{personId}")
    Person findPerson(@Param("personId") Integer personId);

    class Person {
        Integer id;
        String name;
        Integer age;

        //省略setter、getter
        }

 
-------------------------------------
public static void main(String[] args) {
        PersonClient personClient = Feign.builder().decoder(new GsonDecoder())
                .target(PersonClient.class, "http://localhost:8080/");

        //對比可以發現
         //1.創建客戶端
         //2.調用(甚至無需關心API地址)
        Person person=personClient.findPerson(2);

        System.out.println(person.id);
        System.out.println(person.name);
        System.out.println(person.age);
    }

2.3 編碼器

向服務發送請求的過程中,有些情況需要對請求的內容進行處理
例如:服務端發佈的服務接收的是JSON格式參數,而客戶端使用的是對象,這種情況下,就可以使用編碼器,將對象轉換成JSON對象

2.3.1 提供服務端(demo01)

@Controller
public class MyController {

    /*@GetMapping("/hello")
    @ResponseBody
    public String hello() {
        return "Hello world";
    }*/

    @PostMapping("/person/create" method=RequestMethod.POST, consums=MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public String createPerson(@RequestBody Person person) {
        System.out.println(person.getName() + "---" + person.getAge());
        return "創建成功:" + person.getId();
    }
}

通過createPerson可以知道,需要請求的參數爲JSON類型

2.3.2 客戶端服務接口(demo02)

public interface PersonClient {


    @RequestLine("POST /person/create")
    @Headers("Content-Type: application/json")
    String createPerson(Person person);

    @Data
    class Person {
        Integer id;
        String name;
        Integer age;
    }
}

客戶端服務接口中,使用了@Headers註解,聲明請求的內容類型爲JSON

2.3.3 客戶端運行類(demo02)

public class EncoderMain {

    public static void main(String[] args) {

        // 獲取服務接口
        PersonClient personClient = Feign.builder()
                .encoder(new GsonEncoder())
                .target(PersonClient.class, "http://localhost:8080/");

        // 創建參數的實例
        Person person = new Person();

        person.id = 1;
        person.name = "aitemi";
        person.age = 18;

        String reponseStr = personClient.createPerson(person);

        System.out.println(reponseStr);
    }

}
  • 在運行類中,在創建服務接口實例時,使用了encoder方法來指定編碼器
  • 使用(Feign提供的GsonEncoder類),該類會在發送請求過程中,將請求的對象轉換爲JSON字符串
  • Feign支持插件式的編碼器,如果Feign提供的編碼器無法滿足要求,可以使用自定義的編碼器
    注:
    實際請求的參數類型爲Person類型,而服務端需要請求的爲JSON類型,所以此時需要使用編碼器,以滿足服務端輸入的請求

2.4 解碼器

  • 編碼器是對請求的內容進行處理,
  • 解碼器則會對服務響應的內容進行處理
    例如:解析響應的JSON或者XML字符串,轉化爲我們需要的對象
  // 獲取服務接口
        PersonClient personClient = Feign.builder()
                .decoder(new GsonEncoder())
                .target(PersonClient.class, "http://localhost:8080/");

採用decoder(new GsonEncoder()) 將響應的內容解析爲JSON對象

2.5 第三方註解

2.5.1 配置文件

如果想使用JAXRS規範來註解,可以使用Feign的“feign-jaxrs”模塊

<!-- Feign 對 JAXRS 的支持 -->
<dependency>
   <groupId>io.github.openfeign</groupId>
   <artifactId>feign-jaxrs</artifactId>
   <version>9.5.0</version>
</dependency>
<!-- JAXRS -->
<dependency>
   <groupId>javax.ws.rs</groupId>
   <artifactId>jsr311-api</artifactId>
   <version>1.1.1</version>
</dependency>

2.5.2 客戶端接口

public interface HelloClient {

   /*@RequestLine("GET /hello")
   public String sayHello();*/

   @GET
   @Path("/hello")
   public String sayHello();
}

特殊之處

  • 使用 @GET @Path("/hello")註解,代替Feign中的@RequestLine(“POST /person/create”)

2.5.3 客戶端測試

public class HelloClient {

    public static void main(String[] args) {

        //設置了 JAXRSContract 類,Feign 就知道如何處理 JAXRS 的相關注解
        HelloClient helloClient = Feign.builder().contract(new JAXRSContract())
                .target(HelloClient.class, "http://localhost:8080/");

        String result = helloClient.sayHello();

        System.out.println("接口響應內容:" + result);
    }
}

2.6 請求攔截器

  • 在發送請求的時候需要設置公共信息(權限、請求類型等)

自定義攔截器

public class MyInterceptor implements RequestInterceptor {

    public void apply(RequestTemplate template) {
        //設置請求頭信息(這樣就可以不需要在每一個方法中都設置了)
        //template.header("Content-Type", "application/json");
        System.out.println("   --->>> 這是自定義攔截器   <<<---  ");
    }

}

請求設置攔截器(Feign)


public class InterceptorMain {
    public static void main(String[] args) {
        HelloClient helloClient = Feign.builder()
                .requestInterceptor(new MyInterceptor())
                .target(HelloClient.class, "http://localhost:8080/");
        String result=helloClient.sayHello();
        System.out.println(result);
    }
}

2.7 接口日誌

  • 默認情況下,接口不會記錄接口日誌
  • 如果想要了解接口的調用情況,可以配置接口日誌

測試代碼:


public class LogMain {
    public static void main(String[] args) {
        /**
         * NONE:    默認值,不記錄日誌
         * BASIC:   記錄請求方法、URL、響應狀態代碼和執行時間
         * HEADERS: 除BASIC記錄日誌外,還會記錄請求頭和響應頭的信息
         * FULL:    在HEADERS的基礎上,請求和響應的元數據,都會保存
         */
        HelloClient helloClient = Feign.builder().logLevel(Logger.Level.FULL)
                .logger(new Logger.JavaLogger().appendToFile("logs/main.log"))
                .target(HelloClient.class, "http://localhost:8080/");
        String result = helloClient.sayHello();
        System.out.println(result);
    }
}

以上日誌輸出到文件中,同時需要創建日誌文件

發佈了21 篇原創文章 · 獲贊 1 · 訪問量 348
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章