SpringCloudAlibaba註冊中心與配置中心之利器Nacos實戰與原始碼分析(中)

itxiaoshen發表於2022-04-11

Nacos配置中心示例

配置SpringBoot日誌

日誌我們使用SpringBoot預設的logback,在庫存模組的根目錄下建立conf資料夾,將logback.xml放在下面,logback.xml內容如下

<?xml version="1.0" encoding="UTF-8" ?>
<configuration debug="false">
    <!--定義日誌檔案的儲存地址 勿在 LogBack 的配置中使用相對路徑-->
    <springProperty scope="context" name="APP_HOME" source="spring.application.name"/>
    <property name="LOG_HOME" value="${LOG_PATH:-.}" />
    <!-- 控制檯輸出設定 -->
    <!-- 彩色日誌格式,magenta:洋紅,boldMagenta:粗紅,yan:青色,·⊱══> -->
    <property name="CONSOLE_LOG_PATTERN" value="%boldMagenta([%d{yyyy-MM-dd HH:mm:ss.SSS}]) %cyan([%X{requestId}]) %boldMagenta(%-5level) %blue(%logger{15}) %red([%thread]) %magenta(·⊱══>) %cyan(%msg%n)"/>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>
    <!-- 按天輸出日誌設定 -->
    <appender name="DAY_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日誌檔案輸出的檔名 -->
            <FileNamePattern>${LOG_HOME}/%d{yyyy-MM-dd}_${APP_HOME}.%i.log</FileNamePattern>
            <!-- 日誌檔案保留天數 -->
            <MaxHistory>7</MaxHistory>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>50MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>             <!-- 設定攔截的物件為INFO級別日誌 -->
            <onMatch>ACCEPT</onMatch>       <!-- 當遇到了INFO級別時,啟用改段配置 -->
            <onMismatch>DENY</onMismatch>   <!-- 沒有遇到INFO級別日誌時,遮蔽改段配置 -->
        </filter>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!-- 格式化輸出:%d表示日期,%thread表示執行緒名,%-5level:級別從左顯示5個字元寬度%msg:日誌訊息,%n是換行符 -->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>
    <!-- 按天輸出WARN級別日誌設定 -->
    <appender name="DAY_WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日誌檔案輸出的檔名 -->
            <FileNamePattern>${LOG_HOME}/%d{yyyy-MM-dd}_${APP_HOME}_warn.%i.log</FileNamePattern>
            <!-- 日誌檔案保留天數 -->
            <MaxHistory>7</MaxHistory>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>50MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>             <!-- 設定攔截的物件為INFO級別日誌 -->
            <onMatch>ACCEPT</onMatch>       <!-- 當遇到了INFO級別時,啟用改段配置 -->
            <onMismatch>DENY</onMismatch>   <!-- 沒有遇到INFO級別日誌時,遮蔽改段配置 -->
        </filter>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!-- 格式化輸出:%d表示日期,%thread表示執行緒名,%-5level:級別從左顯示5個字元寬度%msg:日誌訊息,%n是換行符 -->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>
    <!-- 按天輸出ERROR級別日誌設定 -->
    <appender name="DAY_ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日誌檔案輸出的檔名 -->
            <FileNamePattern>${LOG_HOME}/%d{yyyy-MM-dd}_${APP_HOME}_error.%i.log</FileNamePattern>
            <!-- 日誌檔案保留天數 -->
            <MaxHistory>7</MaxHistory>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>50MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>            <!-- 設定攔截的物件為ERROR級別日誌 -->
            <onMatch>ACCEPT</onMatch>       <!-- 當遇到了ERROR級別時,啟用改段配置 -->
            <onMismatch>DENY</onMismatch>   <!-- 沒有遇到ERROR級別日誌時,遮蔽改段配置 -->
        </filter>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!-- 格式化輸出:%d表示日期,%thread表示執行緒名,%-5level:級別從左顯示5個字元寬度%msg:日誌訊息,%n是換行符 -->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 日誌輸出級別,OFF level > FATAL > ERROR > WARN > INFO > DEBUG > ALL level -->
    <logger name="com.sand" level="INFO"/>
    <logger name="com.apache.ibatis" level="INFO"/>
    <logger name="java.sql.Statement" level="INFO"/>
    <logger name="java.sql.Connection" level="INFO"/>
    <logger name="java.sql.PreparedStatement" level="INFO"/>
    <logger name="org.springframework" level="WARN"/>
    <logger name="com.baomidou.mybatisplus" level="WARN"/>

    <!-- 開發環境:列印控制檯和輸出到檔案 -->
    <springProfile name="dev">
        <root level="INFO">
            <appender-ref ref="CONSOLE"/>
            <appender-ref ref="DAY_FILE"/>
            <appender-ref ref="DAY_WARN_FILE"/>
            <appender-ref ref="DAY_ERROR_FILE"/>
        </root>
    </springProfile>

    <!-- 生產環境:列印控制檯和輸出到檔案 -->
    <springProfile name="pro">
        <root level="INFO">
            <appender-ref ref="CONSOLE"/>
            <appender-ref ref="DAY_FILE"/>
            <appender-ref ref="DAY_WARN_FILE"/>
            <appender-ref ref="DAY_ERROR_FILE"/>
        </root>
    </springProfile>
</configuration>

配置使用

image-20220410175322070

建立配置

建立庫存微服務的Nacos配置,點選發布

image-20220410015643754

編輯配置,增加庫存微服務資料庫的配置和日誌配置檔案路徑和儲存路徑image-20220410021728439

ecom-storage-service-dev.yaml的配置記憶體如下

server:
  port: 4080
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.50.95:3308/storage?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull
    username: root
    password: 123456
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      max-active: 1000
      min-idle: 5
      initial-size: 10
mybatis-plus:
  global-config:
    db-config:
      id-type: auto
      logic-delete-field: deleted
      logic-delete-value: 1
      logic-not-delete-value: 0
  configuration:
    map-underscore-to-camel-case: on
    call-setters-on-nulls: on
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
logging:
  file:
    path: ./ecom_storage/logs
  config: ./ecom_storage/conf/logback.xml

如果配置中心和redis是共用的,所有服務都放在一個ecom-group組下,commons-dev.yaml的內容如下

spring:
  cloud:
    nacos:
      discovery:
        server-addr: ${spring.cloud.nacos.server-addr}
        group: ecom-group
        namespace: a2b1a5b7-d0bc-48e8-ab65-04695e61db01      
        username: itsx
        password: itxs123
  redis:
    cluster:
      nodes: 192.168.50.196:5001,192.168.50.196:5002,192.168.50.196:5003,192.168.50.196:5004,192.168.50.196:5005,192.168.50.196:5006
      max-redirects: 6
    password: PushBz28

建立extension-priority-dev.yaml(組為extension-group)和shared-priority-dev.yaml(組為shared-priority-dev.yaml)來演示讀取多配置檔案及配置的優先順序。

image-20220410110158986

image-20220410110038533

簡單配置檔案以配置完畢

image-20220410110442779

讀取配置示例

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>

客戶端spring-cloud-starter-alibaba-nacos-config在載入配置的時候,不僅僅載入了以 dataid 為 ${spring.application.name}.${file-extension:properties} 為字首的基礎配置,還載入了dataid為 ${spring.application.name}-${profile}.${file-extension:properties} 的基礎配置。在日常開發中如果遇到多套環境下的不同配置,可以通過Spring 提供的 ${spring.profiles.active} 這個配置項來配置,這裡使用spring.profiles.active=dev,自定義 namespace 的配置和支援自定義 Group 的配置,此外可以通過使用extension-configs或shared-configs支援讀取多個 Data Id 的配置場景。

庫存模組的bootstrap.yml檔案內容如下

spring:
  application:
    name: ecom-storage-service
  profiles:
    active: dev
  main:
    allow-circular-references: true
  cloud:
    nacos:
      # 註冊中心資訊放在配置中心上,每個程式一般只配置配置中心的資訊
      server-addr: 192.168.50.95:8848
      config:
        server-addr: ${spring.cloud.nacos.server-addr}
        file-extension: yaml
        namespace: a2b1a5b7-d0bc-48e8-ab65-04695e61db01
        group: storage-group
        extension-configs:
          - dataId: extension-priority-dev.yaml
            group: extension-group
            refresh: true
          - dataId: commons-dev.yaml
            group: commons-group
            refresh: true
        shared-configs:
          - dataId: shared-priority-dev.yaml
            group: shared-group
            refresh: true
        username: itsx
        password: itxs123
        enabled: true # 預設為true,設定false 來完全關閉 Spring Cloud Nacos Config
        refresh-enabled: true # 預設為true,當變更配置時,應用程式中能夠獲取到最新的值,設定false來關閉動態重新整理,我們使用註冊中心場景大部分就是動態感知,因此基本使用預設的

建立讀取配置NacosConfigDemoComtroller.java

package cn.itxs.ecom.storage.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Name :NacosConfigDemoComtroller
 * @Description :Nacos配置讀取示例控制器
 * @Author :itxs
 * @Date :2022/4/10 11:06
 * @Version :1.0
 * @History :
 */
@RestController
@RefreshScope
public class NacosConfigDemoComtroller {
    //直接通過@Value註解就能獲取nacos配置中心的資料,但這種寫法不能實現動態更新,需要配合@RefreshScope註解來使@Value註解的內容動態重新整理
    @Value(value = "${user.name}")
    private String userName;
    @Value(value = "${user.age}")
    private String userAge;

    @Autowired
    private ConfigurableApplicationContext applicationContext;

    @RequestMapping("/read_config")
    public String readConfig(){
        return "ApplicationContext get userName=" + applicationContext.getEnvironment().getProperty("user.name")
                + ",ApplicationContext get userAge=" + applicationContext.getEnvironment().getProperty("user.age")
                + ",Value get userName=" + userName + ",Value get userAge="+userAge;
    }
}

庫存模組讀取配置示例框架如下

image-20220410113150230

訪問http://localhost:4080/read_config

image-20220410112709081

首先ecom-storage-service-dev.yaml微服務主配置讀取到了,服務的埠為我們配置4080,其次目前user的值獲取到的是extension-priority-dev.yaml裡的

修改Nacos中extension-priority-dev.yaml的值,繼續訪問

image-20220410113050460

在 Nacos Spring Cloud 中,dataId 的完整格式如下:\({prefix}-\){spring.profiles.active}.${file-extension}

  • prefix 預設為 spring.application.name 的值,也可以通過配置項 spring.cloud.nacos.config.prefix來配置。
  • spring.profiles.active 即為當前環境對應的 profile,當 spring.profiles.active 為空時,對應的連線符 - 也將不存在,dataId 的拼接格式變成 ${prefix}.${file-extension}
  • file-exetension 為配置內容的資料格式,可以通過配置項 spring.cloud.nacos.config.file-extension 來配置。目前只支援 propertiesyaml 型別。

直接通過@Value註解就能獲取nacos配置中心的資料,但這種寫法不能實現動態更新,通過 Spring Cloud 原生註解 @RefreshScope來使@Value註解的內容動態重新整理,至此配置都可以動態更新。

配置優先順序

從上小節之後繼續進行多個示例,包括在主配置檔案增加user的值,在extension-priority-dev.yaml和shared-priority-dev.yaml內部多個檔案順序的,驗證 Spring Cloud Alibaba Nacos Config三種配置能力從 Nacos 拉取相關的配置的優先順序。

  • A: 通過 spring.cloud.nacos.config.shared-configs[n].data-id 支援多個共享 Data Id 的配置
  • B: 通過 spring.cloud.nacos.config.extension-configs[n].data-id 的方式支援多個擴充套件 Data Id 的配置
  • C: 通過內部相關規則(應用名、應用名+ Profile )自動生成相關的 Data Id 配置

優先順序關係是:shared-configs < extension-configs < 主配置(應用名、應用名+ Profile )自動生成相關的 Data Id 配置)

而extension-configs內部配置多個 Data Id 時,優先順序關係是 spring.cloud.nacos.config.extension-configs[n].data-id 其中 n 的值越大,優先順序越高。

Nacos Spring Boot

使用 @NacosPropertySource 載入 指定dataId 的配置源,並開啟自動更新,並通過 Nacos 的 @NacosValue 註解設定屬性值。

image-20220410171047171

OpenAPI

Nacos也提供了OpenAPI供開發者進行靈活定製開發

image-20220410120726051

通過官網提供介面定義測試獲取配置

curl -X GET 'http://192.168.50.95:8848/nacos/v1/cs/configs?tenant=a2b1a5b7-d0bc-48e8-ab65-04695e61db01&dataId=ecom-storage-service-dev.yaml&group=storage-group'

image-20220410120854856

監聽配置變化

如果需要感知配置的變化,可以新增一個監聽器來監聽配置的變化

image-20220410164720350

Nacos註冊中心示例

概述

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-vgbqH7Bo-1649608118605)(F:\creation\markdown\article\SpringCloudAlibaba註冊中心與配置中心之利器Nacos實戰與原始碼分析\SpringCloudAlibaba註冊中心與配置中心之利器Nacos實戰與原始碼分析.assets\image-20220410175620221.png)]

進行遠端呼叫首先需要知道遠端服務的地址,spring-cloud-starter-alibaba-nacos-discovery客戶端提供服務名的方式去呼叫遠端的服務的功能,首先解決找服務的問題,其次也提供客戶端軟體負載均衡器,支援設定負載均衡演算法和自定義擴充套件負載均衡演算法,目前最新版本的負載均衡器已不再使用Spring Cloud Netflix Ribbon,而是使用spring-cloud-loadbalancer,關於spring-cloud-loadbalancer的使用詳細可以查閱官網,spring-cloud-loadbalancer文件歸在spring-cloud-commons裡,https://docs.spring.io/spring-cloud-commons/docs/3.1.1/reference/html/#spring-cloud-loadbalancer。呼叫HTTP 服務Spring Boot提供RestTemplate方式,但是這種使用我們需要拼接url和引數,顯然不太符合方法呼叫的思維,這時我們再使用Spring Cloud OpenFeign(以宣告式REST客戶端:Feign建立了一個動態實現的介面,該介面用JAX-RS或Spring MVC註解裝飾),以Spring MVC使用方式進行服務呼叫。

image-20220410172545160

服務註冊

將spring-cloud-loadbalancer加入到commons的pom檔案裡,服務註冊與發現客戶端依賴spring-cloud-starter-alibaba-nacos-discovery前面已加入了

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency> 

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

前面在Nacos中的commons-dev.yaml中已包含Nacos註冊中心的配置,接著先初始化庫存資料,新建commodityCode為1001的庫存資料999,在commons模組建立庫存實體和庫存介面

package cn.itxs.ecom.commons.entity;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

@Data
@TableName("storage_tbl")
public class Storage {
    private Integer id;
    private String commodityCode;
    private Integer count;
}
package cn.itxs.ecom.commons.service;

public interface StorageService {
    /**
     * 扣除儲存數量
     */
    String deduct(String commodityCode, int count);
}

image-20220410235721356

庫存微服務中建立StorageMapper.java

package cn.itxs.ecom.storage.dao;

import cn.itxs.ecom.commons.entity.Storage;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

public interface StorageMapper extends BaseMapper<Storage> {
}

增加MyBatis-Plus配置類MyBatisPlusConfig.java,配置Mapper的掃描目錄

package cn.itxs.ecom.storage.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@MapperScan("cn.itxs.ecom.storage.dao")
@EnableTransactionManagement
@Configuration
public class MyBatisPlusConfig {

    /**
     * 配置新版樂觀鎖外掛,新版分頁外掛
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        //樂觀鎖外掛
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        //分頁外掛
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return mybatisPlusInterceptor;
    }
}

增加庫存服務實現類

package cn.itxs.ecom.storage.service.impl;

import cn.itxs.ecom.commons.service.StorageService;
import cn.itxs.ecom.storage.dao.StorageMapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class StorageServiceImpl implements StorageService {

    @Autowired
    StorageMapper storageMapper;

    @Override
    public String deduct(String commodityCode, int count) {
        UpdateWrapper updateWrapper = new UpdateWrapper();
        updateWrapper.setSql("count = count - " + count);
        updateWrapper.eq("commodity_code", commodityCode);
        storageMapper.update(null,updateWrapper);
        return "1";
    }
}

最後建立庫存控制器,提供扣減庫存方法

package cn.itxs.ecom.storage.controller;

import cn.itxs.ecom.commons.service.StorageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class StorageController {

    @Autowired
    StorageService storageService;

    @RequestMapping("/deduct/{commodityCode}/{count}")
    public String deduct(@PathVariable("commodityCode") String commodityCode, @PathVariable("count") int count){
        return storageService.deduct(commodityCode,count);
    }
}

啟動庫存微服務,在Nacos控制檯中檢視庫存服務模組已註冊到Nacos

image-20220411000934430

訪問扣減庫存的介面,http://localhost:4080/deduct/1001/1 ,檢視資料庫庫存表1001商品庫存已減1,至此庫存服務註冊已完成

image-20220411000719176

服務發現

建立訂單微服務模組,同樣在conf目錄下複製前面logback.xml檔案,pom檔案內容和存庫微服務一樣

<?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">
    <parent>
        <artifactId>simple_ecommerce</artifactId>
        <groupId>cn.itxs</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>ecom_order</artifactId>
    <packaging>jar</packaging>
    <version>1.0</version>
    <name>ecom_order</name>
    <description>a simple electronic commerce platform demo tutorial for order service</description>

    <dependencies>
        <dependency>
            <groupId>cn.itxs</groupId>
            <artifactId>ecom_commons</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <!-- 指定該Main Class為全域性的唯一入口 -->
                    <mainClass>com.aotain.cu.underly.infra.xx1.Xx1ServiceApplication</mainClass>
                    <layout>ZIP</layout>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal><!--可以把依賴的包都打包到生成的Jar包中-->
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

bootstrap.yml內容如下

spring:
  application:
    name: ecom-order-service
  profiles:
    active: dev
  main:
    allow-circular-references: true
  cloud:
    nacos:
      # 註冊中心資訊放在配置中心上,每個程式一般只配置配置中心的資訊
      server-addr: 192.168.50.95:8848
      config:
        server-addr: ${spring.cloud.nacos.server-addr}
        file-extension: yaml
        namespace: a2b1a5b7-d0bc-48e8-ab65-04695e61db01
        group: order-group
        username: itsx
        password: itxs123
        extension-configs:
          - dataId: commons-dev.yaml
            group: commons-group
            refresh: true
        enabled: true # 預設為true,設定false 來完全關閉 Spring Cloud Nacos Config
        refresh-enabled: true # 預設為true,當變更配置時,應用程式中能夠獲取到最新的值,設定false來關閉動態重新整理,我們使用註冊中心場景大部分就是動態感知,因此基本使用預設的

在commons模組建立訂單實體和訂單介面和庫存OpenFeign介面

package cn.itxs.ecom.commons.entity;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

@Data
@TableName("order_tbl")
public class Order {
    private Integer id;
    private String userId;
    private String commodityCode;
    private Integer count;
    private Integer money;
}
package cn.itxs.ecom.commons.service;

import cn.itxs.ecom.commons.entity.Order;

public interface OrderService {
    /**
     * 建立訂單
     */
    Order create(String userId, String commodityCode, int orderCount);
}

package cn.itxs.ecom.commons.service.openfeign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@FeignClient("ecom-storage-service")
public interface StorageFeignService {
    /**
     * deduct 方法在 Spring MVC 請求對映的方式與 nacos-discovery-provider 中的 ServiceController 基本相同,
     * 唯一區別在於 @PathVariable 註解指定了 value 屬性 commodityCode和count,
     * 這是因為預設情況,Java 編譯器不會講介面方法引數名新增到 Java 位元組碼中。
     */

    @RequestMapping("/deduct/{commodityCode}/{count}")
    String deduct(@PathVariable("commodityCode") String commodityCode, @PathVariable("count") int count);
}

同樣在訂單建立Mapper介面和配置類

package cn.itxs.ecom.order.dao;

import cn.itxs.ecom.commons.entity.Order;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

public interface OrderMapper extends BaseMapper<Order> {
}
package cn.itxs.ecom.order.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@MapperScan("cn.itxs.ecom.order.dao")
@EnableTransactionManagement
@Configuration
public class MyBatisPlusConfig {

    /**
     * 配置新版樂觀鎖外掛,新版分頁外掛
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        //樂觀鎖外掛
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        //分頁外掛
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return mybatisPlusInterceptor;
    }
}

增加訂單介面實現類

package cn.itxs.ecom.order.service.impl;

import cn.itxs.ecom.commons.entity.Order;
import cn.itxs.ecom.commons.service.OrderService;
import cn.itxs.ecom.order.dao.OrderMapper;
import cn.itxs.ecom.commons.service.openfeign.StorageFeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private StorageFeignService storageFeignService;

    @Autowired
    OrderMapper orderMapper;

    @Override
    public Order create(String userId, String commodityCode, int orderCount) {
        storageFeignService.deduct(commodityCode,orderCount);

        Order order = new Order();
        order.setUserId(userId);
        order.setCommodityCode(commodityCode);
        order.setCount(orderCount);
        order.setMoney(orderCount*10);
        orderMapper.insert(order);
        return order;
    }
}

最後增加訂單控制器和訂單微服務啟動類,注意需要@EnableFeignClients和配置掃描Feign介面

package cn.itxs.ecom.order.controller;

import cn.itxs.ecom.commons.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {

    @Autowired
    OrderService orderService;

    @RequestMapping("/create/{userId}/{commodityCode}/{count}")
    public String create(@PathVariable("userId") String userId,@PathVariable("commodityCode") String commodityCode, @PathVariable("count") int count){
        return orderService.create(userId,commodityCode,count).toString();
    }
}
package cn.itxs.ecom.order;

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

@SpringBootApplication
@EnableFeignClients(basePackages = {"cn.itxs.ecom.commons.service.openfeign"}) // 啟用 @FeignClient
@ComponentScan(basePackages = {"cn.itxs.ecom.order","cn.itxs.ecom.commons.config","cn.itxs.ecom.commons.utils"})
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

image-20220411002556730

啟動訂單的微服務,檢視庫存和訂單微服務都已註冊到Nacos中

image-20220410205122299

執行建立訂單服務,http://localhost:4070/create/a1001/1001/3 ,成功返回結果

image-20220410204621980

資料庫中訂單和庫存資料表記錄也已經正確更新

image-20220410204906766

**本人部落格網站 **IT小神 www.itxiaoshen.com

相關文章