feign簡單使用


JAVA 項目中接口調用

1)Httpclient

HttpClient 是 Apache Jakarta Common 下的子項目,用來提供高效的、最新的、功能豐富的支持 Http 協議的客戶端編程工具包,並且它支持 HTTP 協議最新版本和建議。

HttpClient 相比傳統 JDK 自帶的 URLConnection,提升了易用性和靈活性,使客戶端發送 HTTP 請求變得容易,提高了開發的效率。

2)Okhttp

一個處理網絡請求的開源項目,是安卓端最火的輕量級框架,由 Square 公司貢獻,用於替代 HttpUrlConnection 和 Apache HttpClient。OkHttp 擁有簡潔的 API、高效的性能,並支持多種協議(HTTP/2 和 SPDY)。

3)HttpURLConnection

HttpURLConnection 是 Java 的標准類,它繼承自 URLConnection,可用於向指定網站發送 GET 請求、POST 請求。HttpURLConnection 使用比較復雜,不像 HttpClient 那樣容易使用。

4)RestTemplate

RestTemplate 是 Spring 提供的用於訪問 Rest 服務的客戶端,RestTemplate 提供了多種便捷訪問遠程 HTTP 服務的方法,能夠大大提高客戶端的編寫效率。

上面介紹的是最常見的幾種調用接口的方法,我們下面要介紹的方法比上面的更簡單、方便,它就是 Feign。

Feign 是一個聲明式的 REST 客戶端,它能讓 REST 調用更加簡單。Feign 供了 HTTP 請求的模板,通過編寫簡單的接口和插入注解,就可以定義好 HTTP 請求的參數、格式、地址等信息。

而 Feign 則會完全代理 HTTP 請求,我們只需要像調用方法一樣調用它就可以完成服務請求及相關處理。

Spring Cloud 對 Feign 進行了封裝,使其支持 SpringMVC 標准注解和 HttpMessageConverters。Feign 可以與 Eureka 和 Ribbon 組合使用以支持負載均衡。

在Spring Cloud中集成Feign

在 Spring Cloud 中集成 Feign 的步驟相當簡單,首先還是加入 Feign 的依賴,代碼如下所示。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

 

在啟動類上加 @EnableFeignClients 注解,如果你的 Feign 接口定義跟你的啟動類不在同一個包名下,還需要制定掃描的包名 @EnableFeignClients(basePackages=“com.fangjia.api.client”),代碼如下所示

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackages = "com.fangjia.api.client")
public class FshSubstitutionServiceApplication {
public static void main(String[] args) {
SpringApplication.run(FshSubstitutionServiceApplication.class, args);
}
}

 

使用Feign調用接口

定義一個 Feign 的客戶端,以接口形式存在,代碼如下所示。

@FeignClient(value = "eureka-client-user-service")
public interface UserRemoteClient {
@GetMapping("/user/hello")
String hello();
}

 

首先我們來看接口上加的 @FeignClient 注解。這個注解標識當前是一個 Feign 的客戶端,value 屬性是對應的服務名稱,也就是你需要調用哪個服務中的接口。

定義方法時直接復制接口的定義即可,當然還有另一種做法,就是將接口單獨抽出來定義,然后在 Controller 中實現接口。

在調用的客戶端中也實現了接口,從而達到接口共用的目的。我這里的做法是不共用的,即單獨創建一個 API Client 的公共項目,基於約定的模式,每寫一個接口就要對應寫一個調用的 Client,后面打成公共的 jar,這樣無論是哪個項目需要調用接口,只要引入公共的接口 SDK jar 即可,不用重新定義一遍了。

定義之后可以直接通過注入 UserRemoteClient 來調用,這對於開發人員來說就像調用本地方法一樣。

接下來采用 Feign 來調用 /user/hello 接口,代碼如下所示。

@Autowired
private UserRemoteClient userRemoteClient;
@GetMapping("/callHello")
public String callHello() {
//return restTemplate.getForObject("http://localhost:8083/house/hello",String.class);
//String result = restTemplate.getForObject("http://eureka-client-user-service/user/hello",String.class);
String result = userRemoteClient.hello();
System.out.println("調用結果:" + result);
return result;
}

 

通過跟注釋掉的代碼相比可以發現,我們的調用方式變得越來越簡單了,從最開始的指定地址,到后面通過 Eureka 中的服務名稱來調用,再到現在直接通過定義接口來調用。

簡單案例:

首先定義一個公共的api,避免寫swagger接口文檔

1.定義接口:(用途是我將該接口打包,然后分發服務提供方和服務調用方,這樣如果我在這里寫的接口在服務方和調用方就可以實現,然后就不用寫文檔,你也可以知道每個接口的用途(如果你在定義接口時寫了),只是每次需要打包,就比較麻煩,且服服務端和調用方試用版的版本要一致)

public interface UserHandler {

    User getUser();

    Map testMap();

    String putUser(User user);

}

2.install 該接口所在項目

api所在的項目mavenx項目標識

    <groupId>paic.common</groupId>
    <artifactId>user_api</artifactId>
    <version>1.0-SNAPSHOT</version>

3.在服務方和接口調用方引用該jar包

       <dependency>
            <groupId>paic.common</groupId>
            <artifactId>user_api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

服務方 pom:

 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>paic.provider</groupId>
    <artifactId>user_provider</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR3</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>paic.common</groupId>
            <artifactId>user_api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

服務方可以實現這些接口:

@RestController
public class ProviderUserConreoller implements UserHandler {
    private AtomicInteger integer=new AtomicInteger();

    @RequestMapping("/getUser")
    @Override
    public User getUser() {
        integer.incrementAndGet();
        System.out.println("第:"+integer.get()+" 次調用。。。");
        User user=new User();
        user.setAge(integer.get());
        user.setName("baier");
        user.setSchool("黃埔二中");
        try {
            System.out.println("進入睡眠");
            Thread.sleep(4000);
            System.out.println("睡眠結束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return user;
    }
    @RequestMapping("/testMap")
    @Override
    public Map testMap() {
        return Collections.singletonMap("one","hahaha");
    }

    @RequestMapping("/putUser")
    @Override
    public String putUser(@RequestBody  User user) {
        System.out.println(user.getAge());
        System.out.println(user.getName());
        System.out.println(user.getSchool());
        return user.toString();
    }
}

服務調用方pom:

 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>paic.common</groupId>
    <artifactId>user_api</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR3</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

調用方繼承api信息(繼承是因為可以自己定義一部分其他的接口,而不是全部都是公共接口中的東西,其實服務端也可以這樣做)

@FeignClient(name = "user-provider")
public interface UserApi extends UserHandler {
    @RequestMapping("/getUser")
    User getUser();
    @RequestMapping("/testMap")
    Map testMap();
    @RequestMapping("/putUser")
    String putUser(User var1);
}

調用方調用:

@RestController
public class UserConsumer  {
    @Autowired
    private UserApi api;

    @GetMapping("/getUser")
    public User getUser() {
        System.out.println("getUser-------------");
        return api.getUser();
    }

    @GetMapping("/testMap")
    public Map testMap() {
        System.out.println("testMap-------------");
        return api.testMap();
    }

    @PostMapping("/putUser")
    public String putUser(@RequestBody User var1) {
        System.out.println("putUser-------------");
        return api.putUser(var1);
    }
}

使用postman調用:

 

 調用通過

 當有兩個服務提供方的時候,會自定做負載均衡,這樣如果光是查詢的話沒有問題,如果中間做其他操作就會有問題,比如中間向數據庫中插入一條數據,就會插入兩次

 比如啟動兩個項目,其中一個項目睡眠一段時間,會發現程序會掉用另一個服務中的東西,如下:

第一個服務睡眠

  try {
            System.out.println("進入睡眠");
            Thread.sleep(4000);
            System.out.println("睡眠結束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

調用一次,發現兩個服務都輸出了東西

第一個:

 

 第二個沒有睡眠:

 

 其實就是第一次訪問到了第一台機器,但是由於業務調用時間太長(睡眠了),然后會去嘗試調用其他的服務器(之前的調用還在繼續),這樣如果第二台調用成功了,就會返回快速返回結果,但事實上兩個服務器上都執行了

 可以通過配置參數來處理嘗試次數這些,如下:

#連接超時時間(ms)
#ribbon.ConnectTimeout=1000
#業務邏輯超時時間(ms),如果超過會新啟一個請求去調用,有可能繼續打到該台機器上
#ribbon.ReadTimeout=2000
#同一台實例最大重試次數,不包括首次調用
#ribbon.MaxAutoRetries=3
#重試負載均衡其他的實例最大重試次數,不包括首次調用
#ribbon.MaxAutoRetriesNextServer=3
#是否所有操作都重試
#ribbon.OkToRetryOnAllOperations=false

 


免責聲明!

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



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