1. 程式人生 > >springcloud入門系列(2)-Feign、Ribbon實現Rest介面請求和負載均衡

springcloud入門系列(2)-Feign、Ribbon實現Rest介面請求和負載均衡

前面我們介紹了通過springcloud的eureka服務註冊元件實現,並且實現了多機互備的HA,同時也將之前寫的springboot的服務註冊到了eureka上,今天我們主要來介紹下作為服務使用者如何去使用這些服務介面並且實現基於服務化的軟負載均衡。對於之前實現的springboot的rest介面服務,一般來說進行restful介面的接收和拆組包,可以通過apache的httpclient、jdk的URLConnection、okhttp等http請求庫,也可以通過spring提供的resttemplate,這裡我們使用springcloud推薦的feign來進行報文解析來體會下它的優勢,為什麼spring會大力推薦一個新的rest請求元件,比傳統的使用httpclient等有什麼優勢。

1. POM增加Feign和Ribbon相關依賴庫

新建一個springboot工程,並新增cloud的依賴,本文中使用的是1.4.7版本,具體的程式碼可以參見文後的原始碼,這裡需要在pom中新增對ribbon和feign的依賴。還需要加入對Zuul閘道器元件的依賴,如果沒有閘道器元件,在啟動的時候會報Hystrix的錯誤,“Caused by: java.lang.ClassNotFoundException: com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect。“

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zuul</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
        </dependency>

2. Application啟動類增加Feign依賴

在application類中需要增加在類名前面增加@EnableFeignClients的註解,這個註解的意思就是在該應用被啟動的時候,會去所有類中搜索定義為FeignClient的介面,並自動註冊到spring ioc容器中,然後會對所有使用client實現類的物件進行自動依賴注入。這裡還有EurekaClient的註解是因為後面定義FeignClient的時候需要定義介面的服務名而不是ip地址,需要定義EurekaClient才能發現相關服務,並且實現軟負載。

@SpringCloudApplication
@EnableFeignClients
@EnableEurekaClient

public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

3. 建立Feign Client介面

下面來到最重要的一步,就是定義Feign的Client介面,這裡Client介面其實就是對提供服務的restful介面的本地定義,定義好後,在其他類中就可以像使用RPC本地介面類一樣使用遠端服務介面了,想想我們用httpclient、okhttp是怎麼實現報文接收的功能的,首先我們需要定義httpclient物件,按照服務端的介面要求組合json報文,然後定義是get還是post,不同的傳輸方法還需要呼叫不同的方法(doGet、doPost),這才將一個請求發出去,然後從inputstream中迴圈讀取位元組流或者從Reader中讀取字元流,再將字元流反序列化成物件,全部需要編碼實現,整個過程雖然結構很清晰但是很繁瑣,為什麼之前RPC很火,就是因為使用RPC會讓client端呼叫遠端服務介面簡單化,就好像使用本地物件一樣簡單,但是使用RPC的最大的弊端就是每個client都需要維護一個服務端的client jar包,這個jar包中其實就是定義了介面,一旦服務端介面有變化,服務使用方就需要更新jar包,這就使得客戶端對服務端產生了強依賴,feign就很好的解決了這個問題,在client和server端沒有依賴的情況下,讓client使用服務就像使用本地介面一樣簡單,廢話不多少,下面來定義feign client的介面,這裡服務端的介面就使用之前定義的getUser介面。


@FeignClient("usercenter-provider")

public interface UserFeignClient {
    //Feign定義服務提供者介面
    @RequestMapping(value = "/getUser", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8"})
    String getUser(@RequestBody String data);

}

@FeignClient("usercenter-provider")註解的意思是該接口裡定義的方法全部是“usercenter-provider”這個serviceid提供的方法,注意這裡的serviceid需要和eureka server裡定義的服務提供方的名字一致,不然feign是無法找到相關服務的,feign內部集成了Ribbon所以通過serviceid找到服務後,會通過ribbon自動實現服務訪問的負載均衡,可以通過定義ribbon的負載均衡方法來實現自己想要的,預設得是隨機訪問方法也可以定義成順序、訪問權重或者自定義負載演算法,這裡就使用了預設的負載,預設負載已經能夠滿足大部分需求了。在介面的方法前面需要定義該方法訪問路徑和訪問方法和編碼格式等資訊,對於請求引數也可以通過@requestBody或者@RequestParameter的value方法進行設定,這裡就使用和入參名同名的引數設定。是不是很簡單,這樣就定義好了遠端服務端介面。

4. 遠端服務介面使用

上面定義好了Feign Client介面,後面使用就很簡單了,這裡按照日常專案結構定義了service層和controller,將feign的介面呼叫放在了service的實現類中,在controller類中進行service的呼叫,這裡需要注意一個問題,之前在測試的過程中自己遇到了,因為不小心寫錯了,犯了一個很低階的錯誤,找了1個小時才找到原因。。因為feign的client是通過@AutoWired啟動的時候自動注入的,這就使得如果client的呼叫是在service裡的時候需要將service也放在spring的ioc中進行託管,不然就會報你的Service沒有定義,並且在Controller使用這個Service的時候也要通過ioc容器自動注入Service實現,不然會報client空指標的錯誤,如果除錯過程中發現FeignClient呼叫的時候報nullpoint,基本就是由於ioc bean託管導致的問題,我就是因為寫錯了一行程式碼導致了這個問題。
UserServiceImpl.java


@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserFeignClient userFeignClient;
    @Override
    public String getUSer(String data) {
        return "Feign: " + userFeignClient.getUser(data);
    }
}

UserController.java


@RestController
public class UserController {
    @Autowired
    private UserService userService;

    @RequestMapping(value = "/getUser", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8"})
    public String getUser(@RequestBody String data){
        return userService.getUSer(data);
    }
}

5. application.yml引數設定

在配置中主要是要配置eureka server叢集的地址,因為需要定義在哪個eureka叢集中找到feign使用的serviceid。

spring:
  application:
    name: springcloud-feign-ribbon

server:
  port: 8080

eureka:
  client:
    serviceUrl:
      defaultZone: http://node1:8761/eureka/,http://node2:8762/eureka/

小結

最後我們啟動eureka server、usercenter-provider的服務,再啟動本次寫的應用。

image
image
下面通過httprequester來發起請求,位址列輸入http://node1:8080/getUser,body欄輸入{"name":"feiweiwei"},可以看到返回了Feign {"name":"feiweiwei"}
image
image