Spring Cloud(三)服務消費

服務消費

Spring Cloud(一)服務注冊與發(fā)現(xiàn)中,我們提供了服務提供者的工程項目份企,既然有服務的提供方赦肋,那么如何去消費所提供的服務第队,SpringCloud中提供了多種方式來實現(xiàn)傅蹂,該模塊主要就介紹了服務消費,內容包含了服務消費(基礎)算凿,服務消費(Ribbon)份蝴,服務消費(Feign)。


服務消費(基礎)

創(chuàng)建一個服務消費者工程氓轰,命名為:service-consumer,并在pom.xml中引入依賴:

    <parent>
        <groupId>com.wkedong.springcloud</groupId>
        <artifactId>parent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-sleuth-zipkin</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-sleuth</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.39</version>
        </dependency>
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.3</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

在消費者的pom文件中婚夫,我們依賴了config配置中心(方便配置文件管理),zipkin和sleuth服務追蹤(后續(xù)文章會進行介紹)署鸡,fastjson(在調用接口時使用的是Json傳參),commons-fileupload(上傳文件所需依賴)

在對工程的配置文件案糙,bootstrap.yml如下:

eureka:
  client:
    healthcheck:
      enabled: true #健康檢查開啟
    serviceUrl:
      defaultZone: http://localhost:6060/eureka/  #注冊中心服務地址
server:
  port: 7010  #當前服務端口
spring:
  ## 從配置中心讀取文件
  cloud:
    config:
      uri: http://localhost:6010/
      label: master
      profile: dev
      name: service-consumer
  application:
    name: service-consumer    #當前服務ID
  zipkin:
    base-url: http://localhost:6040 #zipkin服務地址
  sleuth:
    enabled: true #服務追蹤開啟
    sampler:
      percentage: 1 #zipkin收集率

創(chuàng)建服務應用的主類ServiceConsumerApplication.java如下:

/**
 * @author wkedong
 * 2019/1/5
 * Consumer
 */
@SpringBootApplication
@EnableDiscoveryClient
public class ServiceConsumerApplication {

    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate();
    }

    public static void main(String[] args) {
        new SpringApplicationBuilder(ServiceConsumerApplication.class).web(true).run(args);
    }
}

其實在主類中我們注入了restTemplate限嫌,注解@LoadBalanced已經默認開啟了負載均衡的配置。

主類中我們初始化RestTemplate时捌,用來真正發(fā)起REST請求怒医。關于RestTemplate的介紹可以查看詳解RestTemplate
創(chuàng)建ConsumerController.java來實現(xiàn)/testGet/testPost奢讨,/testFile接口稚叹,如下:

/**
 * @author wkedong
 * 消費者
 * 2019/1/5
 */
@RestController
public class ConsumerController {
    private final Logger logger = Logger.getLogger(getClass());

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping(value = "/testGet")
    public String testGet() {
        logger.info("===<call testGet>===");
        return restTemplate.getForObject("http://service-producer/testGet", String.class);
    }

    @PostMapping(value = "/testPost")
    public String testPost(@RequestParam("name") String name) {
        logger.info("===<call testPost>===");
        JSONObject json = new JSONObject();
        json.put("name", name);
        return restTemplate.postForObject("http://service-producer/testPost", json, String.class);
    }

    @PostMapping(value = "/testFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public String testFile(@RequestParam("file") MultipartFile file) {
        logger.info("===<call testFile>===");
        File path = null;
        if (file != null) {
            try {
                String filePath = "D:\\tempFile";
                path = new File(filePath); //判斷文件路徑下的文件夾是否存在,不存在則創(chuàng)建
                if (!path.exists()) {
                    path.mkdirs();
                }
                File savedFile = new File(filePath + "\\" + file.getOriginalFilename());
                boolean isCreateSuccess = savedFile.createNewFile(); // 是否創(chuàng)建文件成功
                if (isCreateSuccess) {
                    //將文件寫入
                    file.transferTo(savedFile);
                }
                HttpHeaders headers = new HttpHeaders();
                FileSystemResource fileSystemResource = new FileSystemResource(savedFile);
                MediaType type = MediaType.parseMediaType("multipart/form-data");
                headers.setContentType(type);
                MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
                param.add("file", fileSystemResource);
                HttpEntity<MultiValueMap<String, Object>> files = new HttpEntity<>(param, headers);
                return restTemplate.postForObject("http://service-producer/testFile", files, String.class);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (path != null) {
                    path.delete();
                }
            }
        }
        return "文件有誤";
    }
}
  • 這里的請求的地址我們使用了魔法值拿诸,在后續(xù)真正的項目實施時扒袖,可以定義一個常量類來統(tǒng)一管理儲存請求地址。
  • 文件上傳接口不能直接調用服務端接口傳輸MultipartFile文件亩码,所以做了部分處理季率。

工程至此創(chuàng)建完成了,分別啟動注冊中心描沟,服務提供方飒泻,和該工程,并訪問 http://localhost:7010/testGet 啊掏,會出現(xiàn)以下頁面:

Hello, Spring Cloud! My port is 6070 Get info By Mybatis is {"address":"江蘇南京","birthday":"1994-12-21","name":"wkedong"}

服務消費(Ribbon)

Spring Cloud Ribbon

Spring Cloud Ribbon是基于Netflix Ribbon實現(xiàn)的一套客戶端負載均衡的工具蠢络。它是一個基于HTTP和TCP的客戶端負載均衡器。它可以通過在客戶端中配置ribbonServerList來設置服務端列表去輪詢訪問以達到均衡負載的作用迟蜜。

上面是基礎的使用Spring封裝的RestTemplate來進行服務的消費刹孔,在此基礎上實現(xiàn)負載均衡的配置只需要在pom文件中新增依賴:

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

我們將service-consumer工程拷貝一份改名為service-consumer-ribbon,配置文件修改為:

eureka:
  client:
    healthcheck:
      enabled: true #健康檢查開啟
    serviceUrl:
      defaultZone: http://localhost:6060/eureka/  #注冊中心服務地址
server:
  port: 7030  #當前服務端口
spring:
  ## 從配置中心讀取文件
  cloud:
    config:
      uri: http://localhost:6010/
      label: master
      profile: dev
      name: service-consumer-ribbon
  application:
    name: service-consumer-ribbon    #當前服務ID
  zipkin:
    base-url: http://localhost:6040 #zipkin服務地址
  sleuth:
    enabled: true #服務追蹤開啟
    sampler:
      percentage: 1 #zipkin收集率

新建RibbonController.java實現(xiàn)/testRibbon接口:

/**
 * @author wkedong
 * RobbinDemo
 * 2019/1/5
 */
@RestController
public class RibbonController {
    private final Logger logger = Logger.getLogger(getClass());

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping(value = "/testRibbon")
    public String testRibbon() {
        logger.info("===<call testRibbon>===");

        //執(zhí)行http請求Producer服務的provide映射地址娜睛,返回的數據為字符串類型
        //PRODUCER:提供者(Producer服務)的注冊服務ID
        //provide :消費方法
        return restTemplate.getForObject("http://service-producer/testRibbon", String.class);
    }
}

然后啟動eureka髓霞,config,service-producer畦戒,service-consumer-ribbon訪問/testRibbon接口觀察返回數據方库,負載均衡的效果可以通過啟動多個service-producer來進行觀察。
啟動多個服務端之后障斋,調用該接口會發(fā)現(xiàn)端口號在變化纵潦,這就是Ribbon實現(xiàn)的輪值負載均衡機制。


服務消費(Feign)

上面是基礎及實現(xiàn)了負載均衡來使用Spring封裝的RestTemplate來進行服務的消費垃环,下面介紹下利用Feign來進行服務的消費邀层。

Spring Cloud Feign

Spring Cloud Feign是一套基于Netflix Feign實現(xiàn)的聲明式服務調用客戶端。它使得編寫Web服務客戶端變得更加簡單遂庄。
我們只需要通過創(chuàng)建接口并用注解來配置它既可完成對Web服務接口的綁定寥院。它具備可插拔的注解支持,包括Feign注解涛目、JAX-RS注解秸谢。它也支持可插拔的編碼器和解碼器凛澎。
Spring Cloud Feign還擴展了對Spring MVC注解的支持,同時還整合了Ribbon和Eureka來提供均衡負載的HTTP客戶端實現(xiàn)估蹄。

這里繼續(xù)使用之前的服務注冊中心eureka塑煎,配置中心config和服務提供者service-producer,在此基礎上新建工程service-consumer-feign元媚,在pom文件中引用相應依賴:

     <parent>
        <groupId>com.wkedong.springcloud</groupId>
        <artifactId>parent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-sleuth-zipkin</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-sleuth</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.39</version>
        </dependency>
        <dependency>
            <groupId>io.github.openfeign.form</groupId>
            <artifactId>feign-form</artifactId>
            <version>3.0.3</version>
        </dependency>
        <dependency>
            <groupId>io.github.openfeign.form</groupId>
            <artifactId>feign-form-spring</artifactId>
            <version>3.0.3</version>
        </dependency>
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.3</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

主要是多了spring-cloud-starter-feign依賴轧叽,實現(xiàn)feign的操作

在配置文件bootstrap.yml添加如下配置:

eureka:
  client:
    healthcheck:
      enabled: true #健康檢查開啟
    serviceUrl:
      defaultZone: http://localhost:6060/eureka/  #注冊中心服務地址
server:
  port: 7020  #當前服務端口
spring:
  ## 從配置中心讀取文件
  cloud:
    config:
      uri: http://localhost:6010/
      label: master
      profile: dev
      name: service-consumer-feign
  application:
    name: service-consumer-feign    #當前服務ID
  zipkin:
    base-url: http://localhost:6040 #zipkin服務地址
  sleuth:
    enabled: true #服務追蹤開啟
    sampler:
      percentage: 1 #zipkin收集率

工程應用主類中追加@EnableFeignClients注解,聲明為Feign服務應用:

/**
 * @author wkedong
 */
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class ServiceConsumerFeignApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(ServiceConsumerFeignApplication.class).web(true).run(args);
    }

    @Configuration
    class MultipartSupportConfig {
        @Bean
        public Encoder feignFormEncoder() {
            return new SpringFormEncoder();
        }
    }
}

支持文件上傳刊棕,設置feign文件Encoder配置炭晒。

創(chuàng)建一個Feign的客戶端接口定義。使用@FeignClient注解來指定這個接口所要調用的服務名稱甥角,接口中定義的各個函數使用Spring MVC的注解就可以來綁定服務提供方的REST接口网严,這里綁定service-producer中的/testFeign/testFile接口:

/**
 * @author wkedong
 * FeignDemo
 * 2019/1/14
 */
@FeignClient("service-producer")
public interface FeignService {

    @GetMapping(value = "/testFeign")
    String testFeign();

    @PostMapping(value = "/testFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    String testFeignFile(@RequestPart(value = "file") MultipartFile file);
}

創(chuàng)建一個Controller來調用該客戶端:

/**
 * @author wkedong
 * FeignDemo
 * 2019/1/14
 */
@RestController
public class FeignController {

    private final Logger logger = Logger.getLogger(getClass());

    @Autowired
    FeignService feignService;

    @GetMapping("/testFeign")
    public String testFeign() {
        logger.info("===<call testFeign>===");
        return feignService.testFeign();
    }

    @PostMapping(value = "/testFeignFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public String testFeignFile(@RequestParam("file") MultipartFile file) {
        logger.info("===<call testFeignFile>===");
        File path = null;
        if (file != null) {
            try {
                String filePath = "D:\\tempFile";
                //判斷文件路徑下的文件夾是否存在,不存在則創(chuàng)建
                path = new File(filePath);
                if (!path.exists()) {
                    path.mkdirs();
                }
                File tempFile = new File(filePath + "\\" + file.getOriginalFilename());
                // 是否創(chuàng)建文件成功
                boolean isCreateSuccess = tempFile.createNewFile();
                if (isCreateSuccess) {
                    //將文件寫入
                    file.transferTo(tempFile);
                }
                DiskFileItem fileItem = (DiskFileItem) new DiskFileItemFactory().createItem("file",
                        MediaType.MULTIPART_FORM_DATA_VALUE, true, file.getOriginalFilename());

                try (InputStream input = new FileInputStream(tempFile); OutputStream os = fileItem.getOutputStream()) {
                    IOUtils.copy(input, os);
                } catch (Exception e) {
                    throw new IllegalArgumentException("Invalid file: " + e, e);
                }
                MultipartFile multi = new CommonsMultipartFile(fileItem);
                return feignService.testFeignFile(multi);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (path != null) {
                    path.delete();
                }
            }
        }
        return "文件有誤";
    }

}

通過Spring Cloud Feign來實現(xiàn)服務調用的方式更加簡單了嗤无,通過@FeignClient定義的接口聲明我們需要依賴的微服務接口震束。具體使用的時候就跟調用本地方法一樣的進行調用。
由于Feign是基于Ribbon實現(xiàn)的当犯,所以它自帶了客戶端負載均衡功能垢村,也可以通過Ribbon的IRule進行策略擴展。另外嚎卫,F(xiàn)eign還整合的Hystrix來實現(xiàn)服務的容錯保護嘉栓,后續(xù)文章中再對Hysrix進行介紹。
至此代碼編寫結束拓诸,啟動eureka侵佃,config,service-producer奠支,service-consumer-feign訪問/testFeign接口觀察返回數據馋辈,負載均衡的效果可以通過啟動多個service-producer來進行觀察。


文章目錄:

整體demo的GitHub地址:Spring-Cloud

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末倍谜,一起剝皮案震驚了整個濱河市迈螟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌尔崔,老刑警劉巖井联,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異您旁,居然都是意外死亡,警方通過查閱死者的電腦和手機轴捎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進店門鹤盒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蚕脏,“玉大人,你說我怎么就攤上這事侦锯⊥毡蓿” “怎么了?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵尺碰,是天一觀的道長挣棕。 經常有香客問我,道長亲桥,這世上最難降的妖魔是什么洛心? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮题篷,結果婚禮上词身,老公的妹妹穿的比我還像新娘。我一直安慰自己番枚,他們只是感情好法严,可當我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著葫笼,像睡著了一般深啤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上路星,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天溯街,我揣著相機與錄音,去河邊找鬼奥额。 笑死苫幢,一個胖子當著我的面吹牛,可吹牛的內容都是我干的垫挨。 我是一名探鬼主播韩肝,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼九榔!你這毒婦竟也來了哀峻?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤哲泊,失蹤者是張志新(化名)和其女友劉穎剩蟀,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體切威,經...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡育特,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缰冤。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡犬缨,死狀恐怖,靈堂內的尸體忽然破棺而出棉浸,到底是詐尸還是另有隱情怀薛,我是刑警寧澤,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布迷郑,位于F島的核電站枝恋,受9級特大地震影響,放射性物質發(fā)生泄漏嗡害。R本人自食惡果不足惜焚碌,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望就漾。 院中可真熱鬧呐能,春花似錦、人聲如沸抑堡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽首妖。三九已至偎漫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間有缆,已是汗流浹背象踊。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留棚壁,地道東北人杯矩。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像袖外,于是被迫代替她去往敵國和親史隆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,955評論 2 355

推薦閱讀更多精彩內容