Spring Cloud微服務教程-第一篇

Spring Cloud微服務教程-第一篇

1.教程大綱
2.統一開發環境
3.微服務架構
4.Spring Cloud簡介
5.使用 Spring Boot 實現微服務
6.Spring Cloud快速入門

1.教程大綱
統一開發環境
瞭解微服務架構
瞭解Spring Cloud
Spring Cloud快速入門
Eureka服務註冊中心
使用Ribbon實現負載均衡
使用Hystrix實現容錯
2.統一開發環境
JDK: 1.8
IDE: IntelliJ IDEA
Maven:3.3.9
OS: Windows 10 10.0
Springboot版本:2.0+
3.微服務架構
目前微服務是非常火的架構或者說概念,也是在構建大型互聯網項目時採用的架構方式。

3.1.單體架構
單體架構,是指將開發好的項目打成war包,然後發佈到tomcat等容器中的應用。

假設你正準備開發一款與Uber和滴滴競爭的出租車調度軟件,經過初步會議和需求分析,你可能會手動或者使用基於Spring Boot、Play或者Maven的生成器開始這個新項目,它的六邊形架構是模塊化的
應用核心是業務邏輯,由定義服務、領域對象和事件的模塊完成。圍繞着核心的是與外界打交道的適配器。適配器包括數據庫訪問組件、生產和處理消息的消息組件,以及提供API或者UI訪問支持的web模塊等。

儘管也是模塊化邏輯,但是最終它還是會打包並部署爲單體式應用。具體的格式依賴於應用語言和框架。例如,許多Java應用會被打包爲WAR格式,部署在Tomcat或者Jetty上,而另外一些Java應用會被打包成自包含的JAR格式,類似的,Rails和Node.js會被打包成層級目錄。

這種應用開發風格很常見,因爲IDE和其它工具都擅長開發一個簡單應用,這類應用也很易於調試,只需要簡單運行此應用,用Selenium鏈接UI就可以完成端到端測試。單體式應用也易於部署,只需要把打包應用拷貝到服務器端,通過在負載均衡器後端運行多個拷貝就可以輕鬆實現應用擴展。在早期這類應用運行的很好。

3.2.單體架構存在的問題
在這裏插入圖片描述
在這裏插入圖片描述
如何解決以上問題呢? – 使用微服務架構。使得應用由重變輕。

3.3.什麼是微服務?
在這裏插入圖片描述
微服務架構的特徵
在這裏插入圖片描述
3.5.微服務架構示例
在這裏插入圖片描述
每一個應用功能區都使用微服務完成。

4.Spring Cloud簡介
4.1.簡介
Spring Cloud項目的官方網址:
http://projects.spring.io/spring-cloud/
4.2.Spring Cloud子項目
在這裏插入圖片描述
4.3.版本說明
在這裏插入圖片描述
4.4.Spring Cloud框架特點
在這裏插入圖片描述
5.使用 Spring Boot 實現微服務
在正式學習Spring Cloud之前我們先使用Spring Boot實現一個微服務。

業務非常簡單:
1、商品微服務:通過商品id查詢商品的服務;
2、訂單微服務:創建訂單時通時,通過調用商品的微服務進行查詢商品數據;

圖示:在這裏插入圖片描述
說明:
1、對於商品微服務而言,商品微服務是服務的提供者,訂單微服務是服務的消費者;
2、對於訂單微服務而言,訂單微服務是服務的提供者,人是服務的消費者。

5.1.實現商品微服務
5.1.1.創建maven工程
地址git:
5.1.2.導入依賴
重點是導入Spring Boot的依賴:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zpc.microservice</groupId>
    <artifactId>microservice-item</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <!-- 資源文件拷貝插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <configuration>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <!-- java編譯插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
    </project>
5.1.3.創建實體Item
package com.zpc.item.entity;

public class Item {

    private Long id;

    private String title;

    private String pic;

    private String desc;

    private Long price;

    public Item(){}

    public Item(long id, String title, String pic, String desc, Long price) {
        this.id=id;
        this.title=title;
        this.pic=pic;
        this.desc=desc;
        this.price=price;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getPic() {
        return pic;
    }

    public void setPic(String pic) {
        this.pic = pic;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public Long getPrice() {
        return price;
    }

    public void setPrice(Long price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Item [id=" + id + ", title=" + title + ", pic=" + pic + ", desc=" + desc + ", price=" + price + "]";
    }

}

5.1.4.編寫ItemService
編寫ItemService用於實現具體的商品查詢邏輯,爲了演示方便,我們並不真正的連接數據庫,而是做模擬實現。

package com.zpc.item.service;
import com.zpc.item.entity.Item;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;

@Service
public class ItemService {

    private static final Map<Long, Item> ITEM_MAP = new HashMap<Long, Item>();

    static {// 準備一些靜態數據,模擬數據庫
        ITEM_MAP.put(1L, new Item(1L, "商品1", "http://圖片1", "商品描述1", 1000L));
        ITEM_MAP.put(2L, new Item(2L, "商品2", "http://圖片2", "商品描述2", 2000L));
        ITEM_MAP.put(3L, new Item(3L, "商品3", "http://圖片3", "商品描述3", 3000L));
        ITEM_MAP.put(4L, new Item(4L, "商品4", "http://圖片4", "商品描述4", 4000L));
        ITEM_MAP.put(5L, new Item(5L, "商品5", "http://圖片5", "商品描述5", 5000L));
        ITEM_MAP.put(6L, new Item(6L, "商品6", "http://圖片6", "商品描述6", 6000L));
        ITEM_MAP.put(7L, new Item(7L, "商品7", "http://圖片7", "商品描述7", 7000L));
        ITEM_MAP.put(8L, new Item(8L, "商品8", "http://圖片8", "商品描述8", 8000L));
        ITEM_MAP.put(8L, new Item(9L, "商品9", "http://圖片9", "商品描述9", 9000L));
        ITEM_MAP.put(8L, new Item(10L, "商品10", "http://圖片10", "商品描述10", 10000L));
    }

    /**
     * 模擬實現商品查詢
     *
     * @param id
     * @return
     */
    public Item queryItemById(Long id) {
        return ITEM_MAP.get(id);
    }

}

5.1.5.編寫ItemController

package com.zpc.item.controller;

import com.zpc.item.entity.Item;
import com.zpc.item.service.ItemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ItemController {

    @Autowired
    private ItemService itemService;

    /**
     * 對外提供接口服務,查詢商品信息
     *
     * @param id
     * @return
     */
    @GetMapping(value = "item/{id}")
    public Item queryItemById(@PathVariable("id") Long id) {
        return this.itemService.queryItemById(id);
    }

}

@RestController註解的說明:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {

   /**
    * The value may indicate a suggestion for a logical component name,
    * to be turned into a Spring bean in case of an autodetected component.
    * @return the suggested component name, if any (or empty String otherwise)
    * @since 4.0.1
    */
   @AliasFor(annotation = Controller.class)
   String value() default "";

}

從源碼可以看出,這是一個組合註解,組合了@Controller和@Response註解。相當於我們同時寫了這2個註解。
@GetMapping註解是@RequestMapping(method = RequestMethod.GET)簡寫方式。其功能都是一樣的。

同理還有其它註解:@PostMapping @PutMapping @ResttMapping等
5.1.6.編寫程序入口

package com.zpc.item.runner;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

/**
 * @author Evan
 */

@SpringBootApplication//申明這是一個Spring Boot項目
@ComponentScan(basePackages = {"com.zpc.item.controller","com.zpc.item.service"})//手動指定bean組件掃描範圍
public class ItemApp {
    public static void main(String[] args) {
        SpringApplication.run(ItemApp.class, args);
    }
}

5.1.7.創建application.yml配置文件
Spring Boot以及Spring Cloud項目支持yml和properties格式的配置文件。

yml格式是YAML(Yet Another Markup Language)編寫的格式,YAML和properties格式的文件是可以相互轉化的。如:

server:
  port: 8081 #服務端口

等價於properties文件的配置:
server.port=8081

配置文件的示例:

server:
  port: 8081 #服務端口

5.1.8.啓動程序測試
在這裏插入圖片描述
5.2.實現訂單微服務
5.2.1.創建工程
地址git:
5.2.2.導入依賴

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zpc.microservice</groupId>
    <artifactId>microservice-item</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <!-- 資源文件拷貝插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <configuration>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <!-- java編譯插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
    </project>

5.2.3.創建訂單Order實體

package com.zpc.order.entity;

import java.util.Date;
import java.util.List;

public class Order {

    private String orderId;

    private Long userId;

    private Date createDate;

    private Date updateDate;

    private List<OrderDetail> orderDetails;

    public Order() {

    }

    public Order(String orderId, Long userId, Date createDate, Date updateDate) {
        this.orderId = orderId;
        this.userId = userId;
        this.createDate = createDate;
        this.updateDate = updateDate;
    }

    public String getOrderId() {
        return orderId;
    }

    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    public Date getUpdateDate() {
        return updateDate;
    }

    public void setUpdateDate(Date updateDate) {
        this.updateDate = updateDate;
    }


    public List<OrderDetail> getOrderDetails() {
        return orderDetails;
    }

    public void setOrderDetails(List<OrderDetail> orderDetails) {
        this.orderDetails = orderDetails;
    }

    @Override
    public String toString() {
        return "Order [orderId=" + orderId + ", userId=" + userId
                + ", createDate=" + createDate + ", updateDate=" + updateDate
                + "]";
    }}

5.2.4.創建訂單詳情OrderDetail實體
訂單與訂單詳情是一對多的關係。

package com.zpc.order.entity;

public class OrderDetail {

    private String orderId;

    private Item item = new Item();

    public OrderDetail() {

    }

    public OrderDetail(String orderId, Item item) {
        this.orderId = orderId;
        this.item = item;
    }

    public String getOrderId() {
        return orderId;
    }

    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }

    public Item getItem() {
        return item;
    }

    public void setItem(Item item) {
        this.item = item;
    }

    @Override
    public String toString() {
        return "OrderDetail [orderId=" + orderId + ", item=" + item + "]";
    }

}

5.2.5.將商品微服務項目中的Item類拷貝到當前工程
在這裏插入圖片描述
5.2.6.編寫OrderService
該Service實現的根據訂單Id查詢訂單的服務,爲了方便測試,我們將構造數據實現,不採用查詢數據庫的方式。

package com.zpc.order.service;

import com.zpc.order.entity.Item;
import com.zpc.order.entity.Order;
import com.zpc.order.entity.OrderDetail;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class OrderService {

    private static final Map<String, Order> ORDER_DATA = new HashMap<String, Order>();

    static {
        // 模擬數據庫,構造測試數據
        Order order = new Order();
        order.setOrderId("201810300001");
        order.setCreateDate(new Date());
        order.setUpdateDate(order.getCreateDate());
        order.setUserId(1L);
        List<OrderDetail> orderDetails = new ArrayList<OrderDetail>();

        Item item = new Item();// 此處並沒有商品的數據,只是保存了商品ID,需要調用商品微服務獲取
        item.setId(1L);
        orderDetails.add(new OrderDetail(order.getOrderId(), item));

        item = new Item(); // 構造第二個商品數據
        item.setId(2L);
        orderDetails.add(new OrderDetail(order.getOrderId(), item));

        order.setOrderDetails(orderDetails);

        ORDER_DATA.put(order.getOrderId(), order);
    }

    @Autowired
    private ItemService itemService;

    /**
     * 根據訂單id查詢訂單數據
     *
     * @param orderId
     * @return
     */
    public Order queryOrderById(String orderId) {
        Order order = ORDER_DATA.get(orderId);
        if (null == order) {
            return null;
        }
        List<OrderDetail> orderDetails = order.getOrderDetails();
        for (OrderDetail orderDetail : orderDetails) {
            // 通過商品微服務查詢商品詳細數據
            Item item = this.itemService.queryItemById(orderDetail.getItem()
                    .getId());
            if (null == item) {
                continue;
            }
            orderDetail.setItem(item);
        }

        return order;
    }

}

5.2.7.實現ItemService

package com.zpc.order.service;


import com.zpc.order.entity.Item;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class ItemService {

    // Spring框架對RESTful方式的http請求做了封裝,來簡化操作
    @Autowired
    private RestTemplate restTemplate;

    public Item queryItemById(Long id) {
        return this.restTemplate.getForObject("http://127.0.0.1:8081/item/"
                + id, Item.class);
    }

}
5.2.8.編寫OrderController
package com.zpc.order.controller;

import com.zpc.order.entity.Order;
import com.zpc.order.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {
    @Autowired
    private OrderService orderService;

    @GetMapping(value = "order/{orderId}")
    public Order queryOrderById(@PathVariable("orderId") String orderId) {
        return this.orderService.queryOrderById(orderId);
    }
}

5.2.9.編寫程序入口

package com.zpc.order.runner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.client.RestTemplate;

/**
 * @author Evan
 */
@SpringBootApplication//申明這是一個Spring Boot項目
@ComponentScan(basePackages = {"com.zpc.order.controller", "com.zpc.order.service"})//手動指定bean掃描範圍
public class OrderApp {
    public static void main(String[] args) {
        SpringApplication.run(OrderApp.class, args);
    }

    /**
     * 向Spring容器中定義RestTemplate對象
     * @return
     */
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

5.2.10.編寫application.yml配置文件

server:
  port: 8082 #服務端口

5.2.11.啓動測試
在這裏插入圖片描述
5.3.添加okHttp的支持
okhttp是一個封裝URL,比HttpClient更友好易用的工具。看個人習慣,HttpClient用的人也很多。
RestTemplate底層默認使用的jdk的標準實現,如果我們想讓RestTemplate的底層使用okhttp,非常簡單:
1、添加okhttp依賴

<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>3.9.0</version>
</dependency>

2、設置requestFactory
return new RestTemplate(new OkHttp3ClientHttpRequestFactory());

@Bean
public RestTemplate restTemplate() {
    return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}

在這裏插入圖片描述
查看 http://localhost:8082/order/201810300001 測試結果是一樣的。
5.4.解決訂單系統中的url硬編碼問題

  1. 通過以上的測試我們發現,在訂單系統中要調用商品微服務中的查詢接口來獲取數據,在訂單微服務中將url硬編碼到代碼中,這樣顯然不好,因爲,運行環境一旦發生變化這個url地址將不可用。
  2. 如何解決呢?
    解決方案:將url地址寫入到application.yml配置文件中。

實現:
修改application.yml文件:

server:
  port: 8082 #服務端口
myspcloud:
  item:
    url: http://127.0.0.1:8081/item/

修改ItemService中的實現:

@Service
public class ItemService {

    // Spring框架對RESTful方式的http請求做了封裝,來簡化操作
    @Autowired
    private RestTemplate restTemplate;

    @Value("${myspcloud.item.url}")
    private String itemUrl;

    public Item queryItemById(Long id) {
        return this.restTemplate.getForObject(itemUrl
                + id, Item.class);
    }

}

5.5.繼續優化解決硬編碼的問題
在SpringBoot中使用@ConfigurationProperties註解可以非常簡單的將配置文件中的值映射成對象。
第一步,創建ItemProperties類:

package com.zpc.order.properties;

public class ItemProperties {
    private String url;

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

}

第二步,創建OrderProperties類:

package com.zpc.order.properties;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * 以myspcloud開頭的配置被匹配到
 * @author Evan
 */
@Component
@ConfigurationProperties(prefix="myspcloud")
public class OrderProperties {

    private ItemProperties item = new ItemProperties();

    public ItemProperties getItem() {
        return item;
    }

    public void setItem(ItemProperties item) {
        this.item = item;
    }
}

第三步,在Itemservice中注入該對象:

@Service
public class ItemService {

    // Spring框架對RESTful方式的http請求做了封裝,來簡化操作
    @Autowired
    private RestTemplate restTemplate;

    //@Value("${myspcloud.item.url}")
    //private String itemUrl;

    @Autowired
    OrderProperties orderProperties;

    public Item queryItemById(Long id) {
        return this.restTemplate.getForObject(orderProperties.getItem().getUrl()
                + id, Item.class);
    }

第四步,啓動類中增加bean掃描路徑

@ComponentScan(basePackages = {"com.zpc.order.controller","com.zpc.order.service","com.zpc.order.properties"})//
手動指定bean掃描範圍

可以看出,這種解決方案比第一種好很多,更加的方便的。
6.Spring Cloud快速入門
6.1.分析硬編碼的問題
通過前面5.4、5.5的實現,我們視乎已經解決了url硬編碼的問題,但是我們想想:
1、如果商品微服務的ip地址發生了變更,訂單微服務中的配置文件也需要跟着修改
2、如果商品微服務有多個,那麼在訂單微服務中又該如何寫地址?

那應該怎麼解決呢? – 通過服務註冊、發現的機制來完成。

6.2.微服務註冊與發現
在這裏插入圖片描述
由上圖可以看出:
1、服務提供者將服務註冊到註冊中心
2、服務消費者通過註冊中心查找服務
3、查找到服務後進行調用(這裏就是無需硬編碼url的解決方案)
4、服務的消費者與服務註冊中心保持心跳連接,一旦服務提供者的地址發生變更時,註冊中心會通知服務消費者

6.3.註冊中心:Eureka
Spring Cloud提供了多種註冊中心的支持,如:Eureka、consul、ZooKeeper等。Eureka已經閉源了。本教程第二篇也會介紹使用其它兩種方式作爲註冊中心。ureka由兩個組件組成:Eureka服務器和Eureka客戶端。Eureka服務器用作服務註冊服務器。Eureka客戶端是一個java客戶端,用來簡化與服務器的交互、作爲輪詢負載均衡器,並提供服務的故障切換支持。Netflix在其生產環境中使用的是另外的客戶端,它提供基於流量、資源利用率以及出錯狀態的加權負載均衡。
6.3.1.原理
在這裏插入圖片描述
Eureka包含兩個組件:Eureka Server和Eureka Client。
Eureka Server提供服務註冊服務,各個節點啓動後,會在Eureka Server中進行註冊,這樣EurekaServer中的服務註冊表中將會存儲所有可用服務節點的信息,服務節點的信息可以在界面中直觀的看到。
Eureka Client是一個java客戶端,用於簡化與Eureka Server的交互,客戶端同時也就別一個內置的、使用輪詢(round-robin)負載算法的負載均衡器。
在應用啓動後,將會向Eureka Server發送心跳,默認週期爲30秒,如果Eureka Server在多個心跳週期內沒有接收到某個節點的心跳,Eureka Server將會從服務註冊表中把這個服務節點移除(默認90秒)。
Eureka Server之間通過複製的方式完成數據的同步,Eureka還提供了客戶端緩存機制,即使所有的Eureka Server都掛掉,客戶端依然可以利用緩存中的信息消費其他服務的API。綜上,Eureka通過心跳檢查、客戶端緩存等機制,確保了系統的高可用性、靈活性和可伸縮性。
6.3.2.編寫Eureka Server
第一步:創建Maven工程:
git代碼地址:
第二步,導入依賴:
這裏需要導入Spring Cloud的管理依賴。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zpc.springcloud.eureka</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6.RELEASE</version>
    </parent>
    <dependencyManagement>
        <dependencies>
            <!-- 導入Spring Cloud的依賴管理 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
    	<!--springboot 整合eureka服務端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

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

第三步,編寫程序啓動類:

package com.zpc.springcloud.eureka;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

/**
 * Eureka註冊中心
 */
@SpringBootApplication
@EnableEurekaServer //申明這是一個Eureka服務
public class AppEureka {

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

第四步,編寫application.yml配置文件:

###服務端口號
server:
  port: 8100

###服務名稱
spring:
  application:
    name: app-eureka-center

eureka:
  instance:
    #註冊中心地址
    hostname: 127.0.0.1
###客戶端調用地址
  client:
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:8100/eureka/
###是否將自己註冊到Eureka服務中,因爲該應用本身就是註冊中心,不需要再註冊自己(集羣的時候爲true)
    register-with-eureka: false
###是否從Eureka中獲取註冊信息,因爲自己爲註冊中心,不會在該應用中的檢索服務信息
    fetch-registry: false

第五步,啓動程序做測試:
在這裏插入圖片描述
6.4.將商品微服務註冊到Eureka
接下來,我們需要將商品的微服務註冊到Eureka服務中。

第一步:修改pom文件,引入Spring Cloud的管理依賴以及eureka服務依賴。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zpc.microservice</groupId>
    <artifactId>microservice-item</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6.RELEASE</version>
    </parent>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--springboot 整合eureka客戶端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <!-- 資源文件拷貝插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <configuration>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <!-- java編譯插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

第二步,修改application.yml配置文件:

###服務端口號(本身是一個web項目)
server:
  port: 8081
###起個名字作爲服務名稱(該服務註冊到eureka註冊中心的名稱,比如商品服務)
spring:
    application:
        name: app-item
###服務註冊到eureka註冊中心的地址
eureka:
  client:
    service-url:
           defaultZone: http://127.0.0.1:8100/eureka
###因爲該應用爲服務提供者,是eureka的一個客戶端,需要註冊到註冊中心
    register-with-eureka: true
###是否需要從eureka上檢索服務
    fetch-registry: true

第三步,修改啓動類,增加@EnableEurekaClient 註解:

package com.zpc.item.runner;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.ComponentScan;

/**
 * @author Evan
 */
//申明這是一個Spring Boot項目
@SpringBootApplication
@EnableEurekaClient
@ComponentScan(basePackages = {"com.zpc.item.controller","com.zpc.item.service"})
public class ItemApp {
    public static void main(String[] args) {
        SpringApplication.run(ItemApp.class, args);
    }

}

第四步,啓動測試:
在這裏插入圖片描述
至此,我們已經將自己的微服務註冊到Eureka server中了。

6.5.訂單系統從Eureka中發現商品服務
之前我們在訂單系統中是將商品微服務的地址進行了硬編碼,現在,由於已經將商品服務註冊到Eureka中,所以,只需要從Eureka中發現服務即可。

第一步,在訂單系統中添加依賴:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zpc.microservice</groupId>
    <artifactId>microservice-order</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6.RELEASE</version>
    </parent>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--springboot 整合eureka客戶端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>3.9.0</version>
        </dependency>

    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <!-- 資源文件拷貝插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <configuration>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <!-- java編譯插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

第二步,修改application.yml配置文件:

server:
  port: 8082 #服務端口
myspcloud:
  item:
    url: http://127.0.0.1:8081/item/
###起個名字作爲服務名稱(該服務註冊到eureka註冊中心的名稱,比如訂單服務)
spring:
    application:
        name: app-order
###服務註冊到eureka註冊中心的地址
eureka:
  client:
    service-url:
           defaultZone: http://127.0.0.1:8100/eureka
###因爲該應用爲服務提供者,是eureka的一個客戶端,需要註冊到註冊中心
    register-with-eureka: true
###是否需要從eureka上檢索服務
    fetch-registry: true

第三步,修改ItemService的實現邏輯:

package com.zpc.order.service;
import com.zpc.order.entity.Item;
import com.zpc.order.properties.OrderProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class ItemService {

    // Spring框架對RESTful方式的http請求做了封裝,來簡化操作
    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    OrderProperties orderProperties;

    public Item queryItemById(Long id) {

        // 該方法走eureka註冊中心調用(去註冊中心根據app-item查找服務,這種方式必須先開啓負載均衡@LoadBalanced)
        String itemUrl = "http://app-item/item/{id}";
        Item result = restTemplate.getForObject(itemUrl, Item.class, id);
        System.out.println("訂單系統調用商品服務,result:" + result);
        return result;
    }
}

第四步,在啓動類中添加@EnableEurekaClient註解 ,獲取RestTemplate的方法上加 @LoadBalanced註解

package com.zpc.order.runner;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

/**
 * @author Evan
 */

@SpringBootApplication//申明這是一個Spring Boot項目
@EnableEurekaClient
@ComponentScan(basePackages = {"com.zpc.order.controller", "com.zpc.order.service","com.zpc.order.properties"})//手動指定bean掃描範圍
public class OrderApp {
    public static void main(String[] args) {
        SpringApplication.run(OrderApp.class, args);
    }

    /**
     * 向Spring容器中定義RestTemplate對象
     * @return
     */
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
    }

}

第五步,啓動測試(此時有3個應用:Eureka註冊中心、Item服務、order服務)
在這裏插入圖片描述
在註冊中心http://localhost:8100看到有2個客戶端。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章