深入講解SpringCloud Alibaba 微服務(wù)調(diào)用,還不懂微服務(wù)的一定得看看!

概述

在微服務(wù)架構(gòu)中,最常見(jiàn)的場(chǎng)景就是微服務(wù)間的相互調(diào)用。微服務(wù)間的相互調(diào)用方式主要有RestTemplate、Feign 、和OpenFeign 。

RestTemplate

  • RestTemplate是遠(yuǎn)程調(diào)用Http的工具,是對(duì)java底層http的封裝,使用RestTemplata用戶可以不再關(guān)注底層的連接建立;
  • RestTemplate是Spring提供的用于訪問(wèn)Rest服務(wù)的客戶端,RestTemplate提供了多種便捷訪問(wèn)遠(yuǎn)程Http服務(wù)的方法,能夠大大提高客戶端的編寫(xiě)效率
  • RestTemplata不僅支持RESTful規(guī)范,還可以定義返回值對(duì)象類型。
  • RestTemplate 支持本地客戶端負(fù)載均衡,是對(duì)Ribbon的封裝。
    個(gè)人整理了一些資料,有需要的朋友可以直接點(diǎn)擊領(lǐng)取。

Java基礎(chǔ)知識(shí)大全

百本Java架構(gòu)師核心書(shū)籍

對(duì)標(biāo)阿里P8的Java學(xué)習(xí)路線和資料

2021年最新java面試題合集

Feign

  • Feign是Spring Cloud組件中的一個(gè)聲明式的輕量級(jí)RESTful的HTTP服務(wù)客戶端 ;
  • Feign內(nèi)置了Ribbon,用來(lái)做客戶端負(fù)載均衡,去調(diào)用服務(wù)注冊(cè)中心的服務(wù);
  • Feign的使用方式是:使用Feign的注解定義接口,然后調(diào)用這個(gè)接口,就可以調(diào)用服務(wù)注冊(cè)中心的服務(wù) ,用起來(lái)就好像調(diào)用本地方法一樣,完全感覺(jué)不到是調(diào)用的遠(yuǎn)程方法;
  • Feign本身不支持SpringMVC的注解,它有一套自己的注解;

OpenFeign

  • OpenFeign是Spring Cloud 在Feign的基礎(chǔ)上支持SpringMVC的注解,如@RequesMapping等等。
  • OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通過(guò)動(dòng)態(tài)代理的方式產(chǎn)生實(shí)現(xiàn)類,實(shí)現(xiàn)類中做負(fù)載均衡并調(diào)用其他服務(wù)。

創(chuàng)建一個(gè)微服務(wù)消費(fèi)者子模塊

在 alibaba-server 子工程下創(chuàng)建一個(gè)微服務(wù)消費(fèi)者子模塊 springboot 項(xiàng)目alibaba-server-consumer,最終文件目錄如下:


在微服務(wù)消費(fèi)者子模塊 alibaba-server-consumer 的pom文件中添加依賴:

<dependencies>
    <!-- web 應(yīng)用 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- 必須包含spring-boot-starter-actuator包,不然啟動(dòng)會(huì)報(bào)錯(cuò)。 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!-- 整合nacos配置中心所需jar包 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        <version>2.2.1.RELEASE</version>
    </dependency>
    <!-- 整合nacos服務(wù)注冊(cè) -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        <version>2.2.1.RELEASE</version>
    </dependency>
    <!-- sentinel -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>
        <!-- lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.2</version>
        <optional>true</optional>
    </dependency>
</dependencies>

將消費(fèi)者微服務(wù) alibaba-server-consumer 注冊(cè)到 nacos上

其中配置如下:

調(diào)用提供者微服務(wù)

使用 RestTemplate

提供者 alibaba-server-helloworld 微服務(wù)的 controller 如下:

@RestController
@RequestMapping("/lhj")
public class HelloWorlsController {
    @RequestMapping("/hello")
    public String hello(){
        return "hello world!";
    }
}

端口號(hào)是8006,上下文是hello

添加一個(gè) RestTemplate 的配置類

@Component
public class RestTemplateConfig {
    @Bean
    @LoadBalanced  // 負(fù)載均衡,使得loadBalancerClient可以通過(guò)應(yīng)用名獲取對(duì)應(yīng)的url
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

啟動(dòng)類

@SpringBootApplication
@EnableDiscoveryClient
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }
}

controller 中調(diào)用遠(yuǎn)程服務(wù)

注意:使用 @Autowired 注入 restTemplate 只能通過(guò)第三種方式來(lái)調(diào)用服務(wù),@Autowired 注入的 restTemplate 因?yàn)椴荒苤苯釉L問(wèn)地址,只能通過(guò)注冊(cè)的服務(wù)應(yīng)用名來(lái)訪問(wèn)。

@RestController

public class ConsumerController {
    @Autowired
    private LoadBalancerClient loadBalancerClient; // 注入 LoadBalancerClient
    @Autowired
    private RestTemplate restTemplate; // 注入 RestTemplate 

    @GetMapping("/diaoyong")
    public  ResponseEntity<String> msg(){
    
        //1.第一種方式(直接使用restTemplate,url寫(xiě)死)
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> response = restTemplate.getForEntity("http://localhost:8006/hello/lhj/hello", String.class);
        
        //2.弟二種方式(利用loadBalancerClient通過(guò)應(yīng)用名獲取url,然后再使用直接使用restTemplate)
        ServiceInstance serviceInstance = loadBalancerClient.choose("alibaba-server-helloworld");
        String url = String.format("http://%s:%s",serviceInstance.getHost(),serviceInstance.getPort())+"/hello/lhj/hello";
        RestTemplate restTemplate = new RestTemplate();
        String response = restTemplate.getForObject(url, String.class);

        //3.弟二種方式(利用@LoadBalanced,可在restTemplate里使用應(yīng)用名字)
        RestTemplate restTemplate = new RestTemplate();
        String response = restTemplate.getForObject("http://PRODUCT/msg", String.class);
        log.info("response={}",response);
        return response;
        
        System.out.println(response.getStatusCode());
        System.out.println(response.getBody());
        System.out.println(response.getHeaders());
        return response;
    }
}

RestTemplate 調(diào)用的三種方式

第一種方式:直接使用被調(diào)用服務(wù)的訪問(wèn)地址,url寫(xiě)死(必須new一個(gè)restTemplate,不能使用注入的)

RestTemplate restTemplate = new RestTemplate();  
String response = restTemplate.getForObject("http://localhost:8006/hello/lhj/hello",String.class)
return response;

第二種方式:利用loadBalancerClient通過(guò)應(yīng)用名獲取url,然后再使用restTemplate(必須new一個(gè)restTemplate,不能使用注入的)

ServiceInstance serviceInstance = loadBalancerClient.choose("alibaba-server-helloworld"); 
String url = String.format("http://%s:%s",serviceInstance.getHost(),serviceInstance.getPort())+"/hello/lhj/hello";
RestTemplate restTemplate = new RestTemplate(); 
String response = restTemplate.getForObject(url, String.class); 
return response;

第三種方式:利用配置類中的@LoadBalanced,可在restTemplate里直接使用服務(wù)應(yīng)用名字(可以使用注入的restTemplate)

String response = restTemplate.getForObject("http://alibaba-server-helloworld/hello/lhj/hello", String.class); 
return response;

注意:用 @Autowired 注入 restTemplate 的不能直接訪問(wèn)地址,只能通過(guò)注冊(cè)的服務(wù)應(yīng)用名來(lái)訪問(wèn)

restTemplate 的返回值類型

返回值類型一共有兩類,getForEntity 和 getForObject,每一類有三個(gè)重載方法。


① getForEntity
既然 RestTemplate 發(fā)送的是 HTTP 請(qǐng)求,那么在響應(yīng)的數(shù)據(jù)中必然也有響應(yīng)頭,如果開(kāi)發(fā)者需要獲取響應(yīng)頭的話,那么就需要使用 getForEntity 來(lái)發(fā)送 HTTP 請(qǐng)求,此時(shí)返回的對(duì)象是一個(gè) ResponseEntity 的實(shí)例。這個(gè)實(shí)例中包含了響應(yīng)數(shù)據(jù)以及響應(yīng)頭。
② getForObject
getForObject 方法的參數(shù)和 getForEntity 一樣,getForObject 的返回值就是服務(wù)提供者返回的數(shù)據(jù),使用 getForObject 無(wú)法獲取到響應(yīng)頭。

瀏覽器訪問(wèn)

使用 Feign

消費(fèi)者 alibaba-server-consumer 微服務(wù)的文件結(jié)構(gòu)


提供者 alibaba-server-hellocloud 微服務(wù)的 controller 如下:

@RestController
@RequestMapping("zj")
public class HelloCloudController {
    @RequestMapping("hello")
    public String hello(String name){
        return "hello cloud,"+name;
    }
}

端口號(hào)是8007,上下文是hello

Feign 原理

● 啟動(dòng)時(shí),程序會(huì)進(jìn)行包掃描,掃描所有包下所有@FeignClient注解的類,并將這些類注入到spring的IOC容器中。當(dāng)定義的Feign中的接口被調(diào)用時(shí),通過(guò)JDK的動(dòng)態(tài)代理來(lái)生成RequestTemplate。
● RequestTemplate中包含請(qǐng)求的所有信息,如請(qǐng)求參數(shù),請(qǐng)求URL等。
● RequestTemplate聲場(chǎng)Request,然后將Request交給client處理,這個(gè)client默認(rèn)是JDK的HTTPUrlConnection,也可以是OKhttp、Apache的HTTPClient等。
● 最后client封裝成LoadBaLanceClient,結(jié)合ribbon負(fù)載均衡地發(fā)起調(diào)用。

(1) pom 文件中添加 Feign 依賴

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

(2)啟動(dòng)類上添加注解 @EnableFeignClients

在服務(wù)的啟動(dòng)類上添加注解 @EnableFeignClients 以開(kāi)啟 Spring Cloud Feign 的支持。

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ConsumerApplication {

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

(3)聲明服務(wù)接口

@Service @FeignClient("alibaba-server-hellocloud") // 服務(wù)提供者的名稱spring.application.name public interface FeignService {
    @RequestMapping(value="/hello/zj/hello") // 服務(wù)提供者方法的訪問(wèn)地址
    String getService(@RequestParam(value="name") String name); 
    } 

① 接口中的方法可以不用加public,方法名任意取。
② 在該接口中,使用 @FeignClient 注解指定要調(diào)用的服務(wù)名來(lái)綁定服務(wù),然后再使用Spring MVC的注解 @RequestMapping 來(lái)綁定具體該服務(wù)提供的REST接口。
③@RequestParam 注解必須要加上 value 屬性。

(4)controller 中調(diào)用服務(wù)接口

@RestController
public class ConsumerController {

    @Autowired
    private FeignService feignService;

    @GetMapping("/diaoyong")
    public  String msg(){
        return feignService.getService("lhj");
    }
}

(5)瀏覽器訪問(wèn)


(6)Feign 開(kāi)啟日志

如果我們想追蹤Feign客戶端發(fā)送的數(shù)據(jù),就要啟用 Feign 的日志。

Feign 在構(gòu)建被 @FeignClient 注解修飾的服務(wù)客戶端時(shí),會(huì)為每一個(gè)客戶端都創(chuàng)建一個(gè)feign.Logger實(shí)例,這樣就可以利用該日志對(duì)象的DEBUG模式來(lái)幫助分析Feign的請(qǐng)求細(xì)節(jié)。

開(kāi)啟方法:

① 配置文件開(kāi)啟:在application.yml 中使用 logging.level.{Feign客戶端對(duì)應(yīng)的接口的全限定名} 的參數(shù)配置格式來(lái)開(kāi)啟指定客戶端日志

logging:   
    level:
        {Feign客戶端對(duì)應(yīng)的接口的全限定名}: debug 

② java bean 的方式開(kāi)啟:@EnableFeignClients 注解上有個(gè) defaultConfiguration 屬性,可以指定默認(rèn)Feign Client的一些配置。

@EnableFeignClients(defaultConfiguration =DefaultFeignConfiguration.class) 
@SpringBootApplication 
public class ProductApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProductApplication.class, args);
    } 
 } 

@Configuration 
public class DefaultFeignConfiguration {
    @Bean
    public Logger.Level feignLoggerLevel(){
        return Logger.Level.FULL;
     }
 }

使用 Feign 的注意事項(xiàng)

① 在 Feign 的服務(wù)聲明接口使用對(duì)象作為參數(shù)時(shí)必須用 @RequestBody注解,讓其以json方式接收

    void insert(@RequestBody User user);  

② 在 Feign 的服務(wù)聲明接口中使用 @RequestParam 一定要加上value屬性

    void delete(@RequestParam("accountCode") String accountCode); 

③ 在 Feign 的服務(wù)聲明接口中使用 @PathVariable 一定要跟上面一樣加上value屬性

     ResultData<AccountDTO> getByCode(@PathVariable(value = "accountCode") String accountCode);  

④ 在消費(fèi)者模塊啟動(dòng)類上使用@EnableFeignClients注解后指明Feign接口所在的包路徑

     @EnableFeignClients(basePackages = "com.javadaily.feign.*")

最后

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容