Spring Cloud (1)

SpringCloud

微服務定義

  1. 是一種架構風格
  2. 一系列微小的服務共同組成
  3. 每一個服務跑在本身的進程裏
  4. 每一個服務爲獨立的業務開發
  5. 獨立部署
  6. 分佈式的管理

image-20180920185214111

架構形態

單體架構

image-20180920185310014

特色:
  1. 打包成一個war包,沒有外部依賴
  2. 共用一個db
  3. 容易測試
  4. 容易部署
  5. 開發效率低
  6. 代碼維護困難
  7. 部署不夠靈活html

    • 構建時間特別長
  8. 穩定性不高
  9. 擴展性不夠,不能知足高併發需求

基於ajax的先後端分離

image-20180920185619575

分佈式架構

定義:支持應用程序和服務的開發,能夠利用物理架構和多個自制的處理元素(多節點),不共享主內存,但經過網絡發送消息前端

簡單的微服務

image-20180920185838902

基礎框架組件

服務註冊發現

服務網關

  • 鏈接內外大門
  • 屏蔽後臺細節,讓用戶無感知
  • 路由功能,外部請求反向路由到內部某個服務
  • 網關功能,控制流量,監控和日誌java

    • 用戶認證
    • 受權
    • 服務網關

前端服務(邊緣服務)

聚合

把兩個接口聚合在一塊兒返回出去mysql

裁剪

經過不一樣需求返回不一樣數據,pc和手機端淘寶返回詳情數據不一致git

後端通用服務(中間層服務)

阿里系

Dubboweb

Zookeeperajax

Spring MVC or SpringBootspring

Spring Cloud

定義

  • 是一個開發工具集
  • 利用spring boot的開發遍歷
  • 基於對netflix 開源組件的進一步封裝
  • 簡化了分佈式開發

來源

基於Netflix Eureka作了二次封裝sql

文檔

http://projects.spring.io/spr...docker

版本查看

image-20180920192026017

Eureka Server(註冊中心)

-Dserver.port=

pom文件

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>

配置方式

image-20180920204856378

image-20180920204912734

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8080/eureka/
    # 不把本身註冊到註冊中心
    register-with-eureka: false
# 設置應用名
spring:
  application:
    name: eureka

打包

mvn clean package install

Eureka Client(服務註冊)

pom文件

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

yml配置方式

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
    # 不把本身註冊到註冊中心
    register-with-eureka: false
    #關閉心跳監測
  server:
    enable-self-preservation: false
# 設置應用名
spring:
  application:
    name: eureka
server:
  port: 8761

image-20180920231533665

開啓註解

@EnableDiscoveryClient@EnableEurekaClient區別

若是註冊中心是eureka那麼推薦使用EnableEurekaClient`

若是是其餘註冊中心那麼推薦使用Enable

Eureka的高可用

image-20180920232137448

兩個eureka都能看到client的信息

若是一個client掛掉了那麼兩個eureka都沒有信息

解決:

image-20180920232310374

多臺

image-20180920232433410

分佈式中爲何須要服務發現

image-20180920232826038

a和b溝通徹底經過註冊中心

服務端發現

  • Nginx
  • Zookeeper
  • Kubernetes

客戶端發現

  • Eureka

微服務的特色

  1. 異構
  2. 不一樣類型的數據庫
  3. Spring Cloud經過rest方式實現服務註冊
  4. Node.js的eureka-js-client

服務拆分方法

  1. 分清楚起點和終點
  2. 瞭解現有架構是否支持微服務架構
  3. 架構是不斷引進的

不適合微服務的系統

  1. 系統中包含不少強事物場景的
  2. 業務相對穩定,迭代週期長
  3. 訪問壓力不大,可用性要求不高

康威定律

任何組織在設計一套系統時,所交互的設計方案在結構上都與該組織的溝通結構保持一直

微服務特色

  • 一系列微小的服務共同組成
  • 單獨部署,跑在本身的進程裏
  • 每一個服務爲獨立的業務開發
  • 分佈式的管理

image-20180921113617844

擴展立方模型

image-20180921113854873

  • X軸 水平復制
  • Y軸功能解耦

    • 單一職責,每一個服務負責單一功能
    • 相關功能彙集在一個服務內
    • 關注點分離

      • 職責分離
      • 通用性分離

        • 基礎組件劃分
        • 消息服務
        • 用戶服務
        • 公共組件拆分
      • 粒度分離

        • 不是越小越好
  • Z軸 數據分區

點餐業務拆分

拆分服務

image-20180921114336491

服務拆分方法論

  • 每一個微服務都有單獨的數據存儲
  • 依據服務特色選擇不一樣結構的數據庫類型

    • 搜索->elasticsearch
    • 訂單類-》mysql
  • 難點在難以肯定邊界

    • 針對邊界設置api
    • 依據邊界權衡數據冗餘

應用通訊

調用的三種方式

  1. 直接使用RestTemplate

    • String s = restTemplate.getForObject("http://localhost:8083/hello", String.class);
  2. 使用LoadbalanceClient

    • ServiceInstance serviceInstance = loadBalancerClient.choose("PRODUCT");
      String url = String.format("http://%s:%s", serviceInstance.getHost(), serviceInstance.getPort() + "/hello");
      String result = restTemplate.getForObject(url, String.class);
  3. 使用註解

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

客戶端負載均衡器(Ribbon)

  • RestTemplate
  • Feign
  • Zuul

發現方式

  • 服務發現
  • 服務選擇規則
  • 服務監聽

主要組件

  • ServerList
  • IRule
  • ServerListFilter

源碼解析

  1. image-20180925174557136
  2. image-20180925174625714
  3. image-20180925174655200
  4. 用來獲取全部列表image-20180925174741878
  5. 輪循策略默認是roundRobinRuleimage-20180925174826179

    image-20180925175053677

    修改默認輪詢策略

    1. http://cloud.spring.io/spring...

      users:
        ribbon:
          NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule
    2. 輪循策略image-20180925175858487

Feign使用(內部也使用了rabbion作負載均衡)

  1. Pom文件

    1. <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-feign</artifactId>
         </dependency>
  2. 啓動主類增長註解@FeignClient
  3. 編寫接口

    1. @FeignClient("product")
      public interface FeignConfig {
          @GetMapping("hello")
          String hello();
      }
  4. 注入和調用

    1. @Autowired
          private FeignConfig feignConfig;
          
          @RequestMapping("/feign")
          public String feign() {
              String result = feignConfig.hello();
              return result;
          }

多模塊拆分服務

問題

  1. 商品和訂單dto重複定義
  2. 同一個對象屢次重複定義
  3. 訂單服務裏面不該該知道商品服務的uri

模塊拆分

  • product-server

    • 業務邏輯
  • product-client

    • 對外暴露的接口
  • Product-common

    • 公用的對象
  • 依賴關係

    • image-20180926111404689

打包命令

  • mvn -Dmaven.test.skip=true -U clean install

同步仍是異步

  1. image-20180926171455253

    1. 消費者經過消息中間件進行解耦
    2. 用戶調用短信服務,積分服務,其餘服務,服務耦合過大,用戶登陸成功須要多個服務同步響應後才告訴成功
  2. image-20180926171800592

    1. 商品服務庫存變化發佈消息,訂單服務訂閱消息,好比商品信息

      1. 訂單不須要查詢商品服務而是查詢本身服務中的信息
      2. 保證數據最終一致性,只須要訂閱對應服務就能保證
  3. 常見消息隊列

    • RabbitMQ
    • Kafka
    • ActiveMQ

RabbitMQ安裝

5672->默認RabbitMQServer端口

15672->RabbitMQ管理頁面端口,頁面只須要配置這個

docker run -d --hostname my-rabbit -p 5672:5672 -p 15672:15672 rabbitmq:3.7.3-management

默認密碼:guest

Docker和DevOps

  1. docker可以解決不一樣環境下應用程序都能運行
  2. 輕量
  3. 進程隔離和管理
  4. 可複用,版本化(tag機制)
  5. 微服務架構師核心,devops和docker是手段

統一配置中心(Spring Cloud Config)

緣由

  1. 配置方式不方便維護
  2. 配置內容與權限(針對線上)
  3. 每更新一個配置都要重啓項目

ConfigServer使用

image-20180926175911819

configServer從遠端git拉下配置放到本地git若是遠端不可用使用本地的

  • pom
  1. <dependency>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-config-server</artifactId>
     </dependency>
     <dependency>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
     </dependency>
  • 開啓註解

    @EnableDiscoveryClient
    @EnableConfigServer
  • 沒有配置對應的gituriimage-20180926181337220
  • 在git上新建項目

    • image-20180926182910218
  • 在配置中填寫對應git uri,username,password
  • image-20180926183121728
  • 訪問對應配置文件

    1. .properties展現爲properties ,.yml爲yml
    2. /{label}/{name}-{profiles}

      1. label->分支(不寫默認master)
      2. name->文件名
      3. profiles->環境
  • 設置配置文件基礎目錄

    • spring.cloud.config.server.git.basedir

ConfigClient使用

導入依賴

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-client</artifactId>
</dependency>

更改配置

spring:
  application:
    name: order
  cloud:
    config:
      discovery:
        enabled: true
        service-id: config
      profile: dev

修改application.yml

修改成bootstrap.yml這樣會優先啓動

注意

配置中心會讀取拼接order.yml+order-xx.yml的內容

Spring Cloud Bus

經過git hook訪問配置文件變動,同步信息到消息隊列,(/bus/refresh)

Config-server 經過消息隊列同步到其餘服務

使用

更新版本

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.BUILD-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.BUILD-SNAPSHOT</spring-cloud.version>
    </properties>

導入jar包

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
            <version>2.0.0.RELEASE</version>
        </dependency>

yml開放端口用來刷新對應變動

management:
  endpoints:
    web:
      exposure:
        include: bus-refresh

訪問端口刷新配置

curl -v -X POST "http://localhost:8991/actuator/bus-refresh"

須要變動的類上面加上註解@RefreshScope

@RestController
@RequestMapping("/env")
@RefreshScope
public class EnvController {
    @Value("${env}")
    public String env;

    @GetMapping("/profile")
    public String getenv() {
        return env;
    }
}

經過 git HOOK自動訪問地址

image-20180928005701388

有bug

異步和消息

定義

  1. 異步:客戶端請求不會阻塞進程,服務端的響應能夠是非及時的

    1. 通知
    2. 請求異步響應
    3. 客戶端不會阻塞
    4. 經過消息實現一對多

      1. 客戶端發送請求消息等待服務端響應

MQ

應用場景

  1. 異步處理

    1. 用戶註冊後須要發短信和加積分
  2. 流量削鋒

    1. 秒殺場景,丟棄請求,控制活動人數
  3. 日誌處理(kafka)

    1. 經過日誌採集定時寫入隊列
  4. 應用解耦

    1. 用戶下單後須要調用商品服務,使用mq用戶下單後把消息寫入商品服務,商品使用拉或者推的方式,訂單服務寫入訂單後就能夠不關注後續流程了

使用

導入依賴

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

yml配置

spring:
  application:
    name: order
  cloud:
    config:
      discovery:
        enabled: true
        service-id: config
      profile: test
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    guest: guest

發送方式

@Autowired
    private AmqpTemplate amqpTemplate;

    @Test
    public void send() {
        amqpTemplate.convertAndSend("myQueue", "now time:" + System.currentTimeMillis());
    }

接收方式

第一種(須要手動建立隊列)

image-20180928173139793

@Slf4j
@Component
public class MqReceiver {
    @RabbitListener(queues = "myQueue")
    public void process(String message) {
        log.info("MqReceiver:{}", message);
    }
}
第二種 自動建立隊列
/**
     * 自動建立隊列
     *
     * @param message
     */
    @RabbitListener(queuesToDeclare = @Queue("myQueue2"))
    public void autoCreateQueue(String message) {
        log.info("autoCreateQueue:{}", message);
    }

第三種 Exchange和Queue綁定

@RabbitListener(bindings = {@QueueBinding(
            value = @Queue("myQueueExchange"),
            exchange = @Exchange("myExchange")
    )})
    public void exchange(String message) {
        log.info("myExchange:{}", message);
    }

image-20180928174927568

消息分組使用第三種方式

@RabbitListener(bindings = @QueueBinding(
        exchange = @Exchange("myOrder"),
        key = "computer",
        value = @Queue("computerOrder")
))
public void computerOrder(String message) {
    log.info("computerOrder:{}", message);
}

@RabbitListener(bindings = @QueueBinding(
        exchange = @Exchange("myOrder"),
        key = "fruit",
        value = @Queue("fruitOrder")
))
public void fruitOrder(String message) {
    log.info("fruitOrder:{}", message);
}
@Test
    public void sendToProduct() {
        amqpTemplate.convertAndSend("myOrder", "computer", "now time:" + System.currentTimeMillis());
    }

Spring Cloud Stream

爲微服務應用構建消息能力的應用,對於消息中間件的封裝,代碼對於中間件的無感知,可是隻支持rabbitMQ 和Kafka

image-20180929181444018

使用

導入pom
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
        </dependency>
定義接口
public interface StreamClient {
    @Input("myMessage")
    SubscribableChannel input();

    @Output("myMessage")
    MessageChannel output();
}
接受
@Component
@EnableBinding(StreamClient.class)
@Slf4j
public class StreamReceiver {
    @StreamListener("myMessage")
    public void process(Object message) {
        log.info("StreamReceiver:{}",message);
    }
}
發送
@RestController
public class SendMessageController {
    @Autowired
    private StreamClient streamClient;

    @GetMapping("/sendMessage")
    public void process() {
        String message = "now " + new Date();
        streamClient.output().send(MessageBuilder.withPayload(message).build());
    }
}

參考:https://coding.imooc.com/learn/list/187.html