Springcloud開發之OpenFeign調用和認證


SpringCloud開發cloud具有巨大的靈活性。

在調用其它服務的時候有多種方式,雖然本質一樣,但是細節還是有所差異。

一、概述

當a服務調用b服務的時候有多種方式進行:

1.通過openFeign接口方式

    優點:我們一般會使用這種方式,因為代碼量相對少一些,安全可以通過openFeign攔截器來實現。

    缺點:無法在請求的時候直接設置請求頭,請求體等(當然可以通過攔截器或者屬性配置解決這個。但在運行時,動態修改並不方便)

    總結:總體而言,還是推薦的。

2.通過RestTemplate的方式指定服務名調用

   優點:靈活度較高,可以自定義處理請求(頭+參數等)和返回的操作,不需要定義openFeign接口

   確定:如果類似的請求比較多,則工作量還是有一些的。

   總結: 如果是最求靈活性,可以考慮。如果不做cloud開發,那么RestTemplate是非常不錯的選擇。

3.通過RestTemplate的方式指定ip方式調用

   第二種方式的變體,一般不那么做,因為這失去了cloud的優勢-集群+負載均衡等等。所以在后文的例子中,有關此部分略。

 

做工程,自然要兼顧效率和可維護性,所以一般我們都推薦使用第1種方式。技術總是要服務於工程和需求。

下面我使用例子來說明。

二、例子

2.1 概述

為了測試,我們構建了四個服務:

a.base(ConfigService) -負責認證 ,返回token

b.McService --管理中心,負責一些管理信息處理

c.CustomerService-負責客戶有關信息

d.ConfigServiceTest-配置測試服務

測試的腳本寫在服務ConfigServiceTest中。

為了解決篇幅,下文例子只是貼出了各個service的接口以及ConfigServiceTest的cloud有關配置。

 2.2 配置代碼

a服務的接口-/api/services/app/Users/Login

    @RequestMapping(value="/api/services/app/Users/Login",method= {RequestMethod.POST,RequestMethod.GET})
    @ResponseBody
    public PublicReturn login(HttpServletRequest request,@RequestBody AuthParam input) {
        AuthParam auth=jwtUtil.parseAuthToken(request, "Authorization");
        
        if (auth!=null) {
            LOG.debug("token Value From header is:"+JSON.toJSONString(auth));
        }
        LOG.debug(JSON.toJSONString(input));
        LOG.debug(input.getCustomerSystemCode());
        try {
            PublicReturn result= this.authService.login(input);
            LOG.debug("result is:"+JSON.toJSONString(result));
            return result;
        }
        catch(Exception e) {
            return PublicReturn.getUnSuccessful("獲取認證信息失敗");
        }
        
    }

 

b 服務的接口-/api/services/app/test

 
         
/**
* 僅用於測試
* @param name
* @return
*/
@RequestMapping("/app/test")
@ResponseBody
public PublicReturn test(@RequestParam(value="name",required = true) String name){
return PublicReturn.getSuccessful(name);
}

 

 c服務的接口-/customer/list

@Controller
@RequestMapping(value="/customer")
public class TestController {
    /**
     * 返回list
     * @return
     */
    @RequestMapping(value = "/list")
    @ResponseBody
    public Object list() { // TODO:
        Map<String,Object> customer=new HashMap<String,Object>();
        customer.put("name","lzf");
        customer.put("sex","男");
        customer.put("age","99");
        customer.put("address","中華");
        return customer;
    }

}

 

以上三個服務的代碼不用特別關注。

 

 d服務(ConfigServiceTest)的測試代碼,分兩大部分:配置和接口

  • 配置-OpenFeign接口和相關配置
  • 用於測試的接口

配置代碼:

1.異常處理-ErrorHandler(ErrorDecoder)

2.openFeign攔截器等配置-ConfigServiceFeignInter,McServiceFeignInter

3.Spring-Mvc配置-CustomWebMvcConfigurer(WebMvcConfigurationSupport),負責定義RestTemplate bean等等

4.openFeign接口-ConfigServiceOpenFeignInterface、McServiceOpenFeignInterface

測試代碼:

1.TestController

--------------------

下面逐一貼出以上代碼(注:為了節約篇幅,部分代碼只選擇緊要部分)

d的兩個攔截器代碼

public class ConfigServiceFeignInter {
    @Bean
    public RequestInterceptor currentUserRequestInterceptor() {
        return (RequestTemplate template) -> {
            //Map<String, Collection<String>> header=template.request().headers();
            //System.out.println(JSON.toJSONString(header, true));
        };
    }

}

public class McServiceFeignInter {

    @Bean
    public ErrorDecoder feignErrorDecoder() {
        return new ErrorHandler();
    }

    @Bean
    public RequestInterceptor currentUserRequestInterceptor() {
        return (RequestTemplate template) -> {
            //Map<String, Collection<String>> header=template.request().headers();
            //System.out.println(JSON.toJSONString(header, true));
            String token = LoginCache.get();
            System.out.println(token);
            template.header("Authorization", token);
        };
    }

}

 

d的兩個openFeign接口

@FeignClient(value = "ConfigService",configuration= ConfigServiceFeignInter.class)
@Component
public interface ConfigServiceOpenFeignInterface {
    @PostMapping("/api/services/app/Users/Login")
    @ResponseBody
    PublicReturn login(@RequestBody AuthParam input);
}

@FeignClient(value = "McService",configuration= McServiceFeignInter.class)
@Component
public interface McServiceOpenFeignInterface {
   @RequestMapping(value = "/api/services/app/test")
   public PublicReturn test(@RequestParam(value="name",required = true) String name);
}

 

d的mvc配置(局部)

@LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        // 把自定義的ClientHttpRequestInterceptor添加到RestTemplate,可添加多個
        //restTemplate.setInterceptors(Collections.singletonList(actionTrackInterceptor));
        return restTemplate;
    }

 

注意:

1.springMvc的RestTemplate功能還是很強大的,可以設定攔截器等等。

2.我們並沒有為服務c(CustomerService)定義標准的OpenFeign接口

 

2.3 測試代碼

@RequestMapping(value = "/login_and_request")
    @ResponseBody
    public Object loginAndRequest() { // TODO:
        AuthParam authParam=new AuthParam();
        //此處略
        PublicReturn loginResult=configService.login(authParam);
        if (loginResult.isSuccessful()){
            String token=loginResult.getDateItem("token").toString();
            LoginCache.set(token);
            try{
                PublicReturn result=mcService.test("this is luzhifei");
                return result;
            }
            catch(Exception e){
                return PublicReturn.getUnSuccessful(e.getMessage());
            }
        }
        else{
            return PublicReturn.getUnSuccessful("登錄失敗");
        }
    }

    /**
     * 使用RestTemplate來調用
     * @return
     */
    @RequestMapping(value = "/request3srv")
    @ResponseBody
    public Object request3srv() { // TODO:
        AuthParam authParam=new AuthParam();
        //此處略...
        PublicReturn loginResult=configService.login(authParam);
        if (loginResult.isSuccessful()){
            String token=loginResult.getDateItem("token").toString();
            String url="http://McService/api/services/app/test";
            MultiValueMap<String, String> header=new LinkedMultiValueMap<>();
            header.add("Authorization",token);
            MultiValueMap<String, String> param = new LinkedMultiValueMap<>();
            param.add("name","lzf");
            HttpEntity<MultiValueMap<String, String>> requst=new HttpEntity<>(param,header);
            try{
                PublicReturn result=restTemplate.postForObject(url,requst,PublicReturn.class); return result;
            }
            catch(Exception e){
                return PublicReturn.getUnSuccessful(e.getMessage());
            }

        }
        else{
            return PublicReturn.getUnSuccessful("登錄失敗");
        }
    }

    @RequestMapping(value = "/list-customer")
    @ResponseBody
    public Object listCustomer() { // TODO:
        String url="http://CustomerService/customer/list";
        MultiValueMap<String, String> header=new LinkedMultiValueMap<>();
        header.add("Authorization","青山遮不住,畢竟東流去!");
        MultiValueMap<String, String> param = new LinkedMultiValueMap<>();
        param.add("name","lzf");
        HttpEntity<MultiValueMap<String, String>> requst=new HttpEntity<>(param,header);
        Object result=restTemplate.postForObject(url,requst,Object.class); return result;
    }

 

2.4測試

編寫完成后,啟動a,b,c,d服務。

之后通過瀏覽器執行:

#通過原生openFeign接口調用
http://localhost:9081/test/login_and_request

#混合調用(原生openFeign+無openFeign方式調用)
http://localhost:9081/test/request3srv

#不需要定義OpenFeign接口的測試
http://localhost:9081/test/list-customer

 

通過上文list-customer的例子可以看到:不需要專門定義OpenFeign接口,也可以直接調用(通過服務名稱而不是ip+url)來實現對其它

服務的調用。

 

測試的時候發現,如果故意停止服務b.McService,那么會等待比較長一段時間才能返回。

如果覺得這個TimeOut時間太長,那么可以設置OpenFeign調用的TimeOut參數

feign:
  client:
    config:
      default:
        connectTimeout: 30000

注意:這影響所有的Client

---

 

關於Timeout可以參閱:SpringCloud開發之OpenFeign timeout和壓縮等問題

 


免責聲明!

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



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