SpringCloud微服務之Nacos、Feign、GateWay詳解

SpringCloud微服務之Nacos、Feign、GateWay詳解

  • 1、Nacos配置管理
    • 1.1、統一配置管理
      • 1.1.1、在nacos中添加配置文件
      • 1.1.2、從微服務拉取配置
    • 1.2、配置熱更新
      • 1.2.1、方式一
      • 1.2.2、方式二
    • 1.3、配置共享
      • 1.3.1、配置共享的優先級
    • 1.4、搭建nacos集群
      • 1.4.1、初始化數據庫
      • 1.4.2、下載nacos
      • 1.4.3、配置nacos
      • 1.4.4、啟動nacos集群
      • 1.4.5、nginx反向代理
      • 1.4.6、優化
  • 2、Feign遠程調用
    • 2.1、Feign遠程調用替代RestTemplate
    • 2.2、Feign自定義配置
      • 2.2.1、配置文件方式
      • 2.2.2、Java代碼方式
    • 2.3、Feign使用優化
    • 2.4、最佳實踐
      • 2.4.1、抽取Client模塊
  • 3、Gateway服務網關
    • 3.1、為什么需要網關
    • 3.2、gateway快速入門
      • 3.2.1、網關路由的流程圖
      • 3.2.2、總結
    • 3.3、斷言工廠
    • 3.4、過濾器工廠
      • 3.4.1、路由過濾器的種類
      • 3.4.2、請求頭過濾器
      • 3.4.3、默認過濾器
      • 3.4.4、總結
    • 3.5、全局過濾器
      • 3.5.1、自定義全局過濾器
      • 3.5.2、過濾器執行順序
    • 3.6、跨域問題
      • 3.6.1、什么是跨域問題
      • 3.6.2、解決跨域問題

1、Nacos配置管理

Nacos除了可以做注冊中心,同樣可以做配置管理來使用。

1.1、統一配置管理

當微服務部署的實例越來越多,達到數十、數百時,逐個修改微服務配置就會讓人抓狂,而且很容易出錯。我們需要一種統一配置管理方案,可以集中管理所有實例的配置。

在這里插入圖片描述

Nacos一方面可以將配置集中管理,另一方可以在配置變更時,及時通知微服務,實現配置的熱更新。

1.1.1、在nacos中添加配置文件

如何在nacos中管理配置呢?

在這里插入圖片描述

在這里插入圖片描述

注意:項目的核心配置,需要熱更新的配置才有放到nacos管理的必要。基本不會變更的一些配置還是保存在微服務本地比較好。

1.1.2、從微服務拉取配置

微服務要拉取nacos中管理的配置,第二步與本地的application.yml配置合并,才能完成項目啟動。application.yml里面存放的是nacos的地址信息,第二步才讀取,可是第一步在拉取nacos中的配置又需要知道這些信息,那么第一步該如何得知nacos地址呢?

因此spring引入了一種新的配置文件:bootstrap.yaml文件,會在application.yml之前被讀取,流程如下:

在這里插入圖片描述

項目啟動后,先去讀取 bootstrap.yaml 文件,我們將nacos的信息都放在 bootstrap.yaml ,這樣就得知了 nacos 地址,第二步再去讀取本地配置文件 application.yaml ,二者合并,進行后續動作。

  1. 引入nacos-config依賴:首先,在user-service服務中,引入nacos-config的客戶端依賴:
<!--nacos配置管理依賴-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
  1. 添加bootstrap.yaml:然后,在user-service中添加一個bootstrap.yaml文件,內容如下,并且將application.yml里面相同的配置刪掉:
spring:application:name: userservice # 服務名稱profiles:active: dev #開發環境,這里是dev cloud:nacos:server-addr: localhost:8848 # Nacos地址config:file-extension: yaml # 文件后綴名

這里會根據spring.cloud.nacos.server-addr獲取nacos地址,再根據

${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}作為文件id,來讀取配置。

本例中,就是去讀取userservice-dev.yaml

在這里插入圖片描述

  1. 讀取nacos配置:在user-service中的UserController中添加業務邏輯,讀取pattern.dateformat配置:

在這里插入圖片描述

  1. 訪問:http://localhost:8081/user/now

在這里插入圖片描述

1.2、配置熱更新

我們最終的目的,是修改nacos中的配置后,微服務中無需重啟即可讓配置生效,也就是配置熱更新。要實現配置熱更新,可以使用兩種方式:

  • 方式一:在@Value注入的變量所在類上添加注解@RefreshScope
  • 方式二:使用@ConfigurationProperties注解代替@Value注解

1.2.1、方式一

方式一:在@Value注入的變量所在類上添加注解@RefreshScope

在這里插入圖片描述

1.2.2、方式二

方式二:使用@ConfigurationProperties注解代替@Value注解

  1. 在user-service服務中,添加一個類cn/itcast/user/config/PatternProperties,讀取patterrn.dateformat屬性:
@Component
@Data
@ConfigurationProperties(prefix = "pattern")
public class PatternProperties {private String dateformat;
}
  1. 在UserController中使用這個類代替@Value也就是通過對象的方法注入當前時間

在這里插入圖片描述

  1. 測試,在nacos修改統一配置,配置內容更改為如下,然后發布
pattern:dateformat: yyyy年MM月dd日 HH:mm:ss

在這里插入圖片描述

  1. 訪問:http://localhost:8081/user/now

在這里插入圖片描述

1.3、配置共享

其實微服務啟動時,會去nacos讀取多個配置文件,例如:

  • [spring.application.name]-[spring.profiles.active].yaml
    • 例如:userservice-dev.yaml
  • [spring.application.name].yaml
    • 例如:userservice.yaml

[spring.application.name].yaml不包含環境,因此可以被多個環境共享。下面我們通過案例來測試配置共享

  1. 我們在nacos中添加一個環境共享配置userservice.yaml文件:

在這里插入圖片描述

  1. 在user-service中讀取共享配置:在user-service服務中,修改PatternProperties類,讀取新添加的屬性

在這里插入圖片描述

在user-service服務中,修改UserController,添加一個方法:

在這里插入圖片描述

  1. 運行兩個UserApplication,使用不同的profile,修改UserApplication2這個啟動項,改變其profile值:

在這里插入圖片描述

在這里插入圖片描述

這樣,UserApplication(8081)使用的profile是dev,UserApplication2(8082)使用的profile是test。

啟動UserApplication和UserApplication2:

  • 訪問http://localhost:8081/user/prop,結果:

在這里插入圖片描述

  • 訪問http://localhost:8082/user/prop,結果:

在這里插入圖片描述

可以看出來,不管是dev,還是test環境,都讀取到了envSharedValue這個屬性的值。

1.3.1、配置共享的優先級

當nacos、服務本地同時出現相同屬性時,優先級有高低之分:

在這里插入圖片描述

1.4、搭建nacos集群

Nacos生產環境下一定要部署為集群狀態,官方給出的Nacos集群圖:

在這里插入圖片描述

其中包含3個nacos節點,然后一個負載均衡器SLB代理3個Nacos。這里負載均衡器可以使用nginx。我們計劃的集群結構如下更清晰:

在這里插入圖片描述

三個nacos節點的地址:

節點ipport
nacos1192.168.1.2008845
nacos2192.168.1.2008846
nacos3192.168.1.2008847

搭建集群的基本步驟:

  • 搭建數據庫,初始化數據庫表結構
  • 下載nacos安裝包
  • 配置nacos
  • 啟動nacos集群
  • nginx反向代理

1.4.1、初始化數據庫

官方推薦的最佳實踐是使用帶有主從的高可用數據庫集群,這里我們以單點的數據庫為例來講解。首先新建一個數據庫,命名為nacos,而后導入下面的SQL:

CREATE TABLE `config_info` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',`data_id` varchar(255) NOT NULL COMMENT 'data_id',`group_id` varchar(255) DEFAULT NULL,`content` longtext NOT NULL COMMENT 'content',`md5` varchar(32) DEFAULT NULL COMMENT 'md5',`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改時間',`src_user` text COMMENT 'source user',`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',`app_name` varchar(128) DEFAULT NULL,`tenant_id` varchar(128) DEFAULT '' COMMENT '租戶字段',`c_desc` varchar(256) DEFAULT NULL,`c_use` varchar(64) DEFAULT NULL,`effect` varchar(64) DEFAULT NULL,`type` varchar(64) DEFAULT NULL,`c_schema` text,PRIMARY KEY (`id`),UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info';/******************************************/
/*   數據庫全名 = nacos_config   */
/*   表名稱 = config_info_aggr   */
/******************************************/
CREATE TABLE `config_info_aggr` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',`data_id` varchar(255) NOT NULL COMMENT 'data_id',`group_id` varchar(255) NOT NULL COMMENT 'group_id',`datum_id` varchar(255) NOT NULL COMMENT 'datum_id',`content` longtext NOT NULL COMMENT '內容',`gmt_modified` datetime NOT NULL COMMENT '修改時間',`app_name` varchar(128) DEFAULT NULL,`tenant_id` varchar(128) DEFAULT '' COMMENT '租戶字段',PRIMARY KEY (`id`),UNIQUE KEY `uk_configinfoaggr_datagrouptenantdatum` (`data_id`,`group_id`,`tenant_id`,`datum_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='增加租戶字段';/******************************************/
/*   數據庫全名 = nacos_config   */
/*   表名稱 = config_info_beta   */
/******************************************/
CREATE TABLE `config_info_beta` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',`data_id` varchar(255) NOT NULL COMMENT 'data_id',`group_id` varchar(128) NOT NULL COMMENT 'group_id',`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',`content` longtext NOT NULL COMMENT 'content',`beta_ips` varchar(1024) DEFAULT NULL COMMENT 'betaIps',`md5` varchar(32) DEFAULT NULL COMMENT 'md5',`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改時間',`src_user` text COMMENT 'source user',`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',`tenant_id` varchar(128) DEFAULT '' COMMENT '租戶字段',PRIMARY KEY (`id`),UNIQUE KEY `uk_configinfobeta_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_beta';/******************************************/
/*   數據庫全名 = nacos_config   */
/*   表名稱 = config_info_tag   */
/******************************************/
CREATE TABLE `config_info_tag` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',`data_id` varchar(255) NOT NULL COMMENT 'data_id',`group_id` varchar(128) NOT NULL COMMENT 'group_id',`tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',`tag_id` varchar(128) NOT NULL COMMENT 'tag_id',`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',`content` longtext NOT NULL COMMENT 'content',`md5` varchar(32) DEFAULT NULL COMMENT 'md5',`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改時間',`src_user` text COMMENT 'source user',`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',PRIMARY KEY (`id`),UNIQUE KEY `uk_configinfotag_datagrouptenanttag` (`data_id`,`group_id`,`tenant_id`,`tag_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_tag';/******************************************/
/*   數據庫全名 = nacos_config   */
/*   表名稱 = config_tags_relation   */
/******************************************/
CREATE TABLE `config_tags_relation` (`id` bigint(20) NOT NULL COMMENT 'id',`tag_name` varchar(128) NOT NULL COMMENT 'tag_name',`tag_type` varchar(64) DEFAULT NULL COMMENT 'tag_type',`data_id` varchar(255) NOT NULL COMMENT 'data_id',`group_id` varchar(128) NOT NULL COMMENT 'group_id',`tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',`nid` bigint(20) NOT NULL AUTO_INCREMENT,PRIMARY KEY (`nid`),UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`),KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_tag_relation';/******************************************/
/*   數據庫全名 = nacos_config   */
/*   表名稱 = group_capacity   */
/******************************************/
CREATE TABLE `group_capacity` (`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',`group_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整個集群',`quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配額,0表示使用默認值',`usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',`max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '單個配置大小上限,單位為字節,0表示使用默認值',`max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大個數,,0表示使用默認值',`max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '單個聚合數據的子配置大小上限,單位為字節,0表示使用默認值',`max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大變更歷史數量',`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改時間',PRIMARY KEY (`id`),UNIQUE KEY `uk_group_id` (`group_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='集群、各Group容量信息表';/******************************************/
/*   數據庫全名 = nacos_config   */
/*   表名稱 = his_config_info   */
/******************************************/
CREATE TABLE `his_config_info` (`id` bigint(64) unsigned NOT NULL,`nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT,`data_id` varchar(255) NOT NULL,`group_id` varchar(128) NOT NULL,`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',`content` longtext NOT NULL,`md5` varchar(32) DEFAULT NULL,`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,`src_user` text,`src_ip` varchar(50) DEFAULT NULL,`op_type` char(10) DEFAULT NULL,`tenant_id` varchar(128) DEFAULT '' COMMENT '租戶字段',PRIMARY KEY (`nid`),KEY `idx_gmt_create` (`gmt_create`),KEY `idx_gmt_modified` (`gmt_modified`),KEY `idx_did` (`data_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='多租戶改造';/******************************************/
/*   數據庫全名 = nacos_config   */
/*   表名稱 = tenant_capacity   */
/******************************************/
CREATE TABLE `tenant_capacity` (`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',`tenant_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID',`quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配額,0表示使用默認值',`usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',`max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '單個配置大小上限,單位為字節,0表示使用默認值',`max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大個數',`max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '單個聚合數據的子配置大小上限,單位為字節,0表示使用默認值',`max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大變更歷史數量',`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改時間',PRIMARY KEY (`id`),UNIQUE KEY `uk_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='租戶容量信息表';CREATE TABLE `tenant_info` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',`kp` varchar(128) NOT NULL COMMENT 'kp',`tenant_id` varchar(128) default '' COMMENT 'tenant_id',`tenant_name` varchar(128) default '' COMMENT 'tenant_name',`tenant_desc` varchar(256) DEFAULT NULL COMMENT 'tenant_desc',`create_source` varchar(32) DEFAULT NULL COMMENT 'create_source',`gmt_create` bigint(20) NOT NULL COMMENT '創建時間',`gmt_modified` bigint(20) NOT NULL COMMENT '修改時間',PRIMARY KEY (`id`),UNIQUE KEY `uk_tenant_info_kptenantid` (`kp`,`tenant_id`),KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info';CREATE TABLE `users` (`username` varchar(50) NOT NULL PRIMARY KEY,`password` varchar(500) NOT NULL,`enabled` boolean NOT NULL
);CREATE TABLE `roles` (`username` varchar(50) NOT NULL,`role` varchar(50) NOT NULL,UNIQUE INDEX `idx_user_role` (`username` ASC, `role` ASC) USING BTREE
);CREATE TABLE `permissions` (`role` varchar(50) NOT NULL,`resource` varchar(255) NOT NULL,`action` varchar(8) NOT NULL,UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE
);INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');

1.4.2、下載nacos

GitHub地址:https://github.com/alibaba/nacos/tags

1.4.3、配置nacos

  1. 進入nacos的conf目錄,修改配置文件cluster.conf.example,重命名為cluster.conf,然后添加內容
127.0.0.1:8845
127.0.0.1.8846
127.0.0.1.8847
  1. 然后修改application.properties文件,添加數據庫配置
spring.datasource.platform=mysqldb.num=1db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=123456

1.4.4、啟動nacos集群

  1. 將nacos文件夾復制三份,分別命名為:nacos1、nacos2、nacos3
  2. 然后分別修改三個文件夾中的application.properties

nacos1:

server.port=8845

nacos2:

server.port=8846

nacos3:

server.port=8847

然后分別啟動三個nacos節點:

# 集群啟動
startup.cmd

1.4.5、nginx反向代理

  1. 修改nginx的conf/nginx.conf文件,配置如下:
# 配置 nacos-cluster 集群
upstream nacos-cluster {server 127.0.0.1:8845;server 127.0.0.1:8846;server 127.0.0.1:8847;
}# 反向代理
server {# 以后訪問nacos不用8848了,直接是80端口listen       80;server_name  localhost;# 只要訪問了 /nacos 路徑,就代理到nacos集群location /nacos {proxy_pass http://nacos-cluster;}
}
  1. 代碼中application.yml文件配置如下:
spring:cloud:nacos:server-addr: localhost:80 # Nacos地址
  1. 而后在瀏覽器訪問:http://localhost/nacos 即可,這樣會在三個 nacos 作負載均衡

1.4.6、優化

  • 實際部署時,需要給做反向代理的nginx服務器設置一個域名,這樣后續如果有服務器遷移nacos的客戶端也無需更改配置.

  • Nacos的各個節點應該部署到多個不同服務器,做好容災和隔離

2、Feign遠程調用

  • Feign 讀音:糞~

先來看我們以前利用RestTemplate發起遠程調用的代碼:

String url = "http://userservice/user/" + order.getUserId();
User user = restTemplate.getForObject(url,User.class);

存在下面的問題:

  • 代碼可讀性差,編程體驗不統一

  • 參數復雜URL難以維護

Feign是一個聲明式的http客戶端,官方地址:https://github.com/OpenFeign/feign

其作用就是幫助我們優雅的實現http請求的發送,解決上面提到的問題。

2.1、Feign遠程調用替代RestTemplate

  1. 引入依賴:我們在order-service服務的pom文件中引入feign的依賴:
<!--Feign-->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  1. 添加注解:在order-service的啟動類添加注解開啟Feign的功能:

在這里插入圖片描述

  1. 編寫Feign的客戶端:在order-service中新建一個接口roder/client/UserClient,內容如下:
@FeignClient("userservice")
public interface UserClient {@GetMapping("/user/{id}")User findById(@PathVariable("id") Long id);
}

這個客戶端主要是基于SpringMVC的注解來聲明遠程調用的信息,比如:

  • 服務名稱:userservice
  • 請求方式:GET
  • 請求路徑:/user/{id}
  • 請求參數:Long id
  • 返回值類型:User

這樣,Feign就可以幫助我們發送http請求,無需自己使用RestTemplate來發送了。

  1. 測試:修改order-service中的OrderService類中的queryOrderById方法,使用Feign客戶端代替RestTemplate

在這里插入圖片描述

這樣看起來就優雅多了。

  1. 訪問:http://localhost:8080/order/101

在這里插入圖片描述

2.2、Feign自定義配置

Feign可以支持很多的自定義配置,如下表所示:

類型作用說明
feign.Logger.Level修改日志級別包含四種不同的級別:NONE、BASIC、HEADERS、FULL
feign.codec.Decoder響應結果的解析器http遠程調用的結果做解析,例如解析json字符串為java對象
feign.codec.Encoder請求參數編碼將請求參數編碼,便于通過http請求發送
feign. Contract支持的注解格式默認是SpringMVC的注解
feign. Retryer失敗重試機制請求失敗的重試機制,默認是沒有,不過會使用Ribbon的重試

一般情況下,默認值就能滿足我們使用,如果要自定義時,只需要創建自定義的@Bean覆蓋默認Bean即可。下面以日志為例來演示如何自定義配置。

  • 方式一:配置文件方式
  • 方式二:Java代碼方式

2.2.1、配置文件方式

基于配置文件修改feign的日志級別可以針對單個服務:

feign:  client:config: userservice: # 針對某個微服務的配置loggerLevel: FULL #  日志級別 

也可以針對所有服務:

feign:  client:config: default: # 這里用default就是全局配置,如果是寫服務名稱,則是針對某個微服務的配置loggerLevel: BASIC #  日志級別 

而日志的級別分為四種:

  • NONE:不記錄任何日志信息,這是默認值。
  • BASIC:僅記錄請求的方法,URL以及響應狀態碼和執行時間
  • HEADERS:在BASIC的基礎上,額外記錄了請求和響應的頭信息
  • FULL:記錄所有請求和響應的明細,包括頭信息、請求體、元數據。

在這里插入圖片描述

2.2.2、Java代碼方式

也可以基于Java代碼來修改日志級別,先聲明一個類,然后聲明一個Logger.Level的對象:

public class DefaultFeignConfiguration  {@Beanpublic Logger.Level feignLogLevel(){return Logger.Level.BASIC; // 日志級別為BASIC}
}
  • 如果要全局生效,將其放到啟動類的@EnableFeignClients這個注解中:
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration .class) 
  • 如果是局部生效,則把它放到對應的@FeignClient這個注解中:
@FeignClient(value = "userservice", configuration = DefaultFeignConfiguration .class) 

2.3、Feign使用優化

提高Feign的性能主要手段就是使用連接池

  1. 在order-service的pom文件引入依賴
<!--httpClient的依賴 -->
<dependency><groupId>io.github.openfeign</groupId><artifactId>feign-httpclient</artifactId>
</dependency>
  1. 配置連接池:在order-service的application.yml中添加配置:
feign:client:config:default: # default全局的配置loggerLevel: BASIC # 日志級別,BASIC就是基本的請求和響應信息httpclient:enabled: true # 開啟feign對HttpClient的支持max-connections: 200 # 最大的連接數max-connections-per-route: 50 # 每個路徑的最大連接數

總結:

  1. 日志級別盡量用basic
  2. 使用連接池
  3. 真實情況下連接池的參數需要經過壓測才能確定最合適的參數值

2.4、最佳實踐

Feign的客戶端與服務提供者的controller代碼非常相似,有沒有一種辦法簡化這種重復的代碼編寫呢?

在這里插入圖片描述

2.4.1、抽取Client模塊

將Feign的Client抽取為獨立模塊,并且把接口有關的POJO、默認的Feign配置都放到這個模塊中,提供給所有消費者使用。

例如,將UserClient、User、Feign的默認配置都抽取到一個feign-api包中,所有微服務引用該依賴包,即可直接使用。

在這里插入圖片描述

  1. 首先創建一個module,命名為feign-api:

在這里插入圖片描述

  1. 然后,order-service中編寫的UserClient、User都復制到feign-api項目中

  2. 刪除order-service中的UserClient、User等類或接口

  3. 在order-service的pom文件中中引入feign-api的依賴:

<!--feign-api-->
<dependency><groupId>cn.itcast.demo</groupId><artifactId>feign-api</artifactId><version>1.0</version>
</dependency>
  1. 修改order-service中的所有與上述三個組件有關的導包部分,改成導入feign-api中的包
  2. 注意:order-service的@EnableFeignClients注解是在cn.itcast.order包下,而 UserClient 是在 cn.itcast.feign 包下,所以需要解決掃描包問題
// 指定需要加載的Client接口
@EnableFeignClients(clients = {UserClient.class})

3、Gateway服務網關

Spring Cloud Gateway 是 Spring Cloud 的一個全新項目,該項目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等響應式編程和事件流技術開發的網關,它旨在為微服務架構提供一種簡單有效的統一的 API 路由管理方式。

3.1、為什么需要網關

Gateway網關是我們服務的守門神,所有微服務的統一入口。

網關的核心功能特性

  • 請求路由
  • 權限控制
  • 限流

架構圖:

我們有很多個微服務業務,每個微服務有自己的數據庫,每個微服務都可以去Nacos進行服務注冊和配置的管理,各個微服務之間若有相互調用關系,采用Feign遠程調用。

但是若外部想要訪問微服務,則需要先經過網關gateway,進行身份認證,認證成功后通過路由和負載均衡將其轉發到對應微服務服務。

在這里插入圖片描述

  • 權限控制:網關作為微服務入口,需要校驗用戶是是否有請求資格,如果沒有則進行攔截。
  • 路由和負載均衡:一切請求都必須先經過gateway,但網關不處理業務,而是根據某種規則,把請求轉發到某個微服務,這個過程叫做路由。當然路由的目標服務有多個時,還需要做負載均衡。
  • 限流:當請求流量過高時,在網關中按照下流的微服務能夠接受的速度來放行請求,避免服務壓力過大。

在SpringCloud中網關的實現包括兩種:

  • gateway
  • zuul

Zuul是基于Servlet的實現,屬于阻塞式編程。而SpringCloudGateway則是基于Spring5中提供的WebFlux,屬于響應式編程的實現,具備更好的性能。

3.2、gateway快速入門

下面,我們就演示下網關的基本路由功能。基本步驟如下:

  1. 創建SpringBoot工程gateway,引入網關依賴
  2. 編寫啟動類
  3. 編寫基礎配置和路由規則
  4. 啟動網關服務進行測試

我們來實現一下:

  1. 新建Maven模塊,繼承 cloud-demo 工程

在這里插入圖片描述

  1. 引入依賴
<!--網關-->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--nacos服務發現依賴-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
  1. 編寫啟動類:src/main/java/cn/itcast/gateway/GatewayApplication.java
@SpringBootApplication
public class GatewayApplication {public static void main(String[] args) {SpringApplication.run(GatewayApplication.class, args);}
}
  1. 創建application.yml文件,編寫基礎配置和路由規則,內容如下:
server:port: 10010 # 網關端口
spring:application:name: gateway # 服務名稱cloud:nacos:server-addr: localhost:8848 # nacos地址gateway:routes: # 網關路由配置- id: user-service # 路由id,自定義,只要唯一即可# uri: http://127.0.0.1:8081 # 路由的目標地址 http就是固定地址(不推薦)uri: lb://userservice # 路由的目標地址 lb就是負載均衡,后面跟服務名稱predicates: # 路由斷言,也就是判斷請求是否符合路由規則的條件- Path=/user/** # 這個是按照路徑匹配,只要以/user/開頭就符合要求
  • 我們將符合Path 規則的一切請求,都代理到 uri參數指定的地址。

  • 本例中,我們將 /user/**開頭的請求,代理到lb://userservice,lb是負載均衡,根據服務名拉取服務列表,實現負載均衡。

如下,我們也可以將/order/** 開頭的請求,代理到lb://orderservice

server:port: 10010 # 網關端口
spring:application:name: gateway # 服務名稱cloud:nacos:server-addr: localhost:8848 # nacos地址gateway:routes: # 網關路由配置- id: user-service # 路由id,自定義,只要唯一即可# uri: http://127.0.0.1:8081 # 路由的目標地址 http就是固定地址uri: lb://userservice # 路由的目標地址 lb就是負載均衡,后面跟服務名稱predicates: # 路由斷言,也就是判斷請求是否符合路由規則的條件- Path=/user/** # 這個是按照路徑匹配,只要以/user/開頭就符合要求- id: order-serviceuri: lb://orderservicepredicates:- Path=/order/**
  1. 重啟網關,訪問:http://localhost:10010/user/1 ,符合/user/**規則,請求轉發到uri:http://userservice/user/1,得到了結果:

在這里插入圖片描述

3.2.1、網關路由的流程圖

在這里插入圖片描述

用戶發起了http://127.0.0.1:10010/user/1 請求,網關端口也是10010,所以請求會先經過網關,網關基于路由規則判斷,把/user/**請求的路徑代理到userservice,把/order/**請求的路徑代理到orderservice,網關會去Nacos注冊中心拉取微服務列表,找到userservice,負載均衡,發送請求

3.2.2、總結

網關搭建步驟:

  1. 創建項目,引入nacos服務發現和gateway依賴

  2. 配置application.yml,包括服務基本信息、nacos地址、路由

路由配置包括:

  1. 路由id:路由的唯一標示

  2. 路由目標(uri):路由的目標地址,http代表固定地址,lb代表根據服務名負載均衡

  3. 路由斷言(predicates):判斷路由的規則,

  4. 路由過濾器(filters):對請求或響應做處理

接下來,就重點來學習路由斷言和路由過濾器的詳細知識

3.3、斷言工廠

我們在配置文件中寫的斷言規則只是字符串,這些字符串會被Predicate Factory讀取并處理,轉變為路由判斷的條件。例如Path=/user/**是按照路徑匹配,這個規則是由 Path 斷言工廠來處理的,像這樣的斷言工廠在 SpringCloudGateway 還有十幾個:

在這里插入圖片描述

我們只需要掌握Path這種路由工程就可以了。例如其他配置:

spring:application:name: gateway # 服務名稱cloud:nacos:server-addr: localhost:8848 # nacos地址gateway:routes: # 網關路由配置- id: user-service # 路由id,自定義,只要唯一即可# uri: http://127.0.0.1:8081 # 路由的目標地址 http就是固定地址uri: lb://userservice # 路由的目標地址 lb就是負載均衡,后面跟服務名稱predicates: # 路由斷言,也就是判斷請求是否符合路由規則的條件- Path=/user/** # 這個是按照路徑匹配,只要以/user/開頭就符合要求- After=2017-01-20T17:42:47.789-07:00[America/Denver] # 請求的時間必須是 JAN20,2017 年 17:42 山區時間(丹佛) 之后

3.4、過濾器工廠

GatewayFilter是網關中提供的一種過濾器,可以對進入網關的請求和微服務返回的響應做處理:

在這里插入圖片描述

3.4.1、路由過濾器的種類

Spring提供了31種不同的路由過濾器工廠。例如:

名稱說明
AddRequestHeader給當前請求添加一個請求頭
RemoveRequestHeader移除請求中的一個請求頭
AddResponseHeader給響應結果中添加一個響應頭
RemoveResponseHeader從響應結果中移除有一個響應頭
RequestRateLimiter限制請求的流量

3.4.2、請求頭過濾器

下面我們以AddRequestHeader 為例來講解

需求:給所有進入userservice的請求添加一個請求頭:Truth=itcast is freaking awesome!

只需要修改gateway服務的application.yml文件,添加路由過濾即可:

spring:application:name: gateway # 服務名稱cloud:nacos:server-addr: localhost:8848 # nacos地址gateway:routes: # 網關路由配置- id: user-service #   路由id,自定義,只要唯一即可# uri: http://127.0.0.1:8081 # 路由的目標地址 http就是固定地址uri: lb://userservice # 路由的目標地址 lb就是負載均衡,后面跟服務名稱predicates: # 路由斷言,也就是判斷請求是否符合路由規則的條件- Path=/user/** # 這個是按照路徑匹配,只要以/user/開頭就符合要求filters:- AddRequestHeader=name,Augenestern! # 添加請求頭
  • 當前過濾器寫在userservice路由下,因此僅僅對訪問userservice的請求有效。

測試:改造 uservice 的controller下的方法來接收請求頭:

 @GetMapping("/{id}")public User queryById(@PathVariable("id") Long id,@RequestHeader(value = "name",required = false) String name) {System.out.println("name: " + name);return userService.queryById(id);}

重啟服務,訪問:http://localhost:10010/user/1,在控制臺可以看到設置的請求頭

在這里插入圖片描述

3.4.3、默認過濾器

如果要對所有的路由都生效,則可以將過濾器工廠寫到default下。格式如下:

spring:application:name: gateway # 服務名稱cloud:nacos:server-addr: localhost:8848 # nacos地址gateway:routes: # 網關路由配置- id: user-service # 路由id,自定義,只要唯一即可# uri: http://127.0.0.1:8081 # 路由的目標地址 http就是固定地址uri: lb://userservice # 路由的目標地址 lb就是負載均衡,后面跟服務名稱predicates: # 路由斷言,也就是判斷請求是否符合路由規則的條件- Path=/user/** # 這個是按照路徑匹配,只要以/user/開頭就符合要求
#          filters: # 默認過濾項
#            - AddRequestHeader=name,Augenestern!  # 添加請求頭- id: order-serviceuri: lb://orderservicepredicates:- Path=/order/**default-filters: - AddRequestHeader=name,Augenestern!  # 添加請求頭

3.4.4、總結

過濾器的作用是什么?

  1. 對路由的請求或響應做加工處理,比如添加請求頭
  2. 配置在路由下的過濾器只對當前路由的請求生效

defaultFilters的作用是什么?

  1. 對所有路由都生效的過濾器

3.5、全局過濾器

上一節學習的過濾器,網關提供了31種,但每一種過濾器的作用都是固定的。如果我們希望攔截請求,做自己的業務邏輯則沒辦法實現。全局過濾器的作用也是處理一切進入網關的請求和微服務響應,與GatewayFilter的作用一樣。區別如下:

  • GatewayFilter通過配置定義,處理邏輯是固定的
  • GlobalFilter的邏輯需要自己寫代碼實現。定義方式是實現GlobalFilter接口:
public interface GlobalFilter {/***  處理當前請求,有必要的話通過{@link GatewayFilterChain}將請求交給下一個過濾器處理** @param exchange 請求上下文,里面可以獲取Request、Response等信息* @param chain 用來把請求委托給下一個過濾器 * @return {@code Mono<Void>} 返回標示當前過濾器業務結束*/Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}

3.5.1、自定義全局過濾器

需求:定義全局過濾器,攔截請求,判斷請求的參數是否滿足下面條件:

  • 參數中是否有authorization,
  • authorization參數值是否為admin

如果同時滿足則放行,否則攔截。

實現:

  1. 在gateway中定義一個過濾器,加上@Order()注解
// Order順序注解,過濾器的順序,越小優先級越高
@Order(-1)
@Component
public class AuthorizeFilter implements GlobalFilter {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 1.獲取請求參數MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();// 2.獲取第一個匹配的authorization參數String auth = params.getFirst("authorization");// 3.校驗if ("admin".equals(auth)) {// 放行return chain.filter(exchange);}// 4.攔截// 4.1.禁止訪問,設置狀態碼exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);// 4.2.結束處理return exchange.getResponse().setComplete();}
}

測試:

  1. 重啟網關服務,訪問:http://localhost:10010/user/1

在這里插入圖片描述

  1. 訪問:http://localhost:10010/user/1?authorization=admin

在這里插入圖片描述

3.5.2、過濾器執行順序

請求進入網關會碰到三類過濾器:當前路由的過濾器、DefaultFilter默認過濾器、GlobalFilter全局過濾器。請求路由后,會將當前路由過濾器和DefaultFilter、GlobalFilter,合并到一個過濾器鏈(集合)中,排序后依次執行每個過濾器:

在這里插入圖片描述

排序的規則是什么呢?

  • 每一個過濾器都必須指定一個int類型的order值,order值越小,優先級越高,執行順序越靠前
  • GlobalFilter通過實現Ordered接口,或者添加@Order注解來指定order值,由我們自己指定
  • 路由過濾器和defaultFilter的order由Spring指定,默認是按照聲明順序從1遞增。
  • 當過濾器的order值一樣時,會按照 defaultFilter > 路由過濾器 > GlobalFilter的順序執行。

3.6、跨域問題

3.6.1、什么是跨域問題

跨域:域名不一致就是跨域,主要包括:

  • 域名不同: www.taobao.com 和 www.taobao.org 和 www.jd.com
  • 域名相同,端口不同:localhost:8080和localhost8081

跨域問題:瀏覽器禁止請求的發起者與服務端發生跨域ajax請求,請求被瀏覽器攔截的問題

解決方案:CORS,https://www.ruanyifeng.com/blog/2016/04/cors.html

3.6.2、解決跨域問題

網關處理跨域采用的同樣是CORS方案,只需簡單配置即可實現:

server:port: 10010 # 網關端口
spring:application:name: gateway # 服務名稱cloud:nacos:server-addr: localhost:8848 # nacos地址gateway:# 全局的跨域處理globalcors:add-to-simple-url-handler-mapping: true # 解決options請求被攔截問題corsConfigurations:'[/**]':         # 攔截所有的請求allowedOrigins: # 允許哪些網站的跨域請求- "http://localhost:8090"allowedMethods: # 允許的跨域ajax的請求方式- "GET"- "POST"- "DELETE"- "PUT"- "OPTIONS"allowedHeaders: "*" # 允許在請求中攜帶的頭信息allowCredentials: true # 是否允許攜帶cookiemaxAge: 360000 # 這次跨域檢測的有效期

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/15366.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/15366.shtml
英文地址,請注明出處:http://en.pswp.cn/web/15366.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

plt多子圖設置

import matplotlib.pyplot as plt# 使用 subplots 函數創建一個 2x3 的子圖網格 fig, axs plt.subplots(nrows2, ncols3, figsize(16, 10)) # 調整 figsize 來改變圖像大小# 遍歷每個子圖&#xff0c;并繪制一些內容&#xff08;這里只是簡單的示例&#xff09; for ax in ax…

React與Vue的區別?

一、區別: 1. 語法 Vue采用自己特有的模板語法&#xff1b; React是單向的&#xff0c;采用jsx語法創建react元素。 2.監聽數據變化的實現原理不同 Vue2.0 通過Object.defineproperty()方法的getter/setter屬性, 實現數據劫持, 每次修改完數據會觸發diff算法(雙端對比) …

VUE 頁面生命周期基本知識點

在 Vue.js 中&#xff0c;頁面生命周期&#xff08;更準確地說是組件生命周期&#xff09;指的是組件從創建到銷毀的一系列過程。了解這些生命周期鉤子可以幫助我們更好地管理組件的狀態和行為。以下是 Vue 組件的主要生命周期鉤子&#xff1a; beforeCreate 在實例初始化之后&…

vue使用element plus組件上傳服務器

在Vue項目中使用Element Plus組件上傳文件到服務器&#xff0c;你可以使用ElUpload組件。以下是一個簡單的示例&#xff0c;展示了如何使用ElUpload組件來上傳文件&#xff0c;并將其保存到服務器。 首先&#xff0c;確保你已經安裝了Element Plus。 npm install element-plu…

從入門到精通:詳解Linux進程管理

前言 在這篇文章中&#xff0c;我將帶領大家深入學習和理解Linux系統中的進程管理。無論你是初學者還是有一定經驗的開發者&#xff0c;相信這篇文章都會對你有所幫助。我們將詳細講解馮諾依曼體系結構、操作系統概念、進程管理、進程調度、進程狀態、環境變量、內存管理以及其…

C語言之函數和函數庫以及自己制作靜態動態鏈接庫并使用

一&#xff1a;函數的本質 1&#xff1a;C語言為什么會有函數 &#xff08;1&#xff09;整個程序分為多個源文件&#xff0c;一個文件分為多個函數&#xff0c;一個函數分成多個語句&#xff0c;這就是整個程序的組織形式。這樣的組織好處在于&#xff1a;分化問題、、便于程序…

分布式版本控制工具 git

git 是什么 分布式版本控制工具。github 是代碼托管平臺。 git 有什么用 保存文件的所有修改記錄。使用版本號&#xff08;sha1 哈希值&#xff09; 進行區分。隨時可瀏覽歷史版本記錄。可還原到歷史指定版本。對比不同版本的文件差異。 為什么要使用 git 多人協作開發一個大…

SQL 優化

SQL 優化是指通過各種手段提高 SQL 查詢的執行效率,減少資源消耗,提高數據庫的整體性能。以下是一些詳細的 SQL 優化方法,包括索引優化、查詢優化、數據庫設計優化等。 1. 索引優化 創建適當的索引: 單列索引:在查詢中頻繁使用的單個列上創建索引。多列索引(復合索引):…

STM32手寫超頻到128M函數

今天學習了野火的STM32教程學會了如何設置STM32的時鐘頻率&#xff0c;步驟比較詳細&#xff0c;也很容易理解&#xff0c;就是視頻教程不能跳著看&#xff0c;只能一節節的看&#xff0c;不然會知識不連貫&#xff0c;造成有些知識不理解&#xff0c;連續著看還是沒有什么難度…

docker-file 網絡

docker掛載 1.綁定掛載&#xff08;Bind Mounts&#xff09;&#xff1a;綁定掛載是將主機上的文件或目錄掛載到容器中。 docker run -v /host/path:/container/path image_name 2.卷掛載&#xff08;Volume Mounts&#xff09;&#xff1a;卷掛載將 Docker 數據卷掛載到容器中…

【CTF Web】CTFShow web4 Writeup(SQL注入+PHP+字符型注入)

web4 1 管理員阿呆又失敗了&#xff0c;這次一定要堵住漏洞 解法 注意到&#xff1a; <!-- flag in id 1000 -->攔截很多種字符&#xff0c;連 select 也不給用了。 if(preg_match("/or|\-|\\\|\/|\\*|\<|\>|\!|x|hex|\(|\)|\|select/i",$id)){die(&q…

yolov8推理由avi改為mp4

修改\ultralytics-main\ultralytics\engine\predictor.py&#xff0c;即可 # Ultralytics YOLO &#x1f680;, AGPL-3.0 license """ Run prediction on images, videos, directories, globs, YouTube, webcam, streams, etc.Usage - sources:$ yolo modepred…

Android開發-Android開發中的TCP與UDP通信策略的實現

Android 開發中的 TCP 與 UDP 通信策略的實現 1. 前言2. 準備工作3. Kotlin 中 TCP 通信實現客戶端代碼示例&#xff1a;服務器代碼示例&#xff1a; 4. Kotlin 中 UDP 通信實現客戶端代碼示例&#xff1a;服務器代碼示例&#xff1a; 5. TCP 與 UDP 應用場景分析TCP 實現可靠傳…

搭建訪問阿里云百煉大模型環境

最近這波大降價&#xff0c;還有限時免費&#xff0c;還不趕快試試在線大模型&#xff1f;下面整理訪問百煉平臺的千問模型方法。 創建RAM子賬號并授權 創建RAM子賬號 1. “訪問控制RAM”入口&#xff08;控制臺URL&#xff09; 然后點擊進入“RAM管理控制臺” 2. 添加用戶 …

vue 區分多環境打包

需求&#xff1a;區分不同的環境&#xff08;測試、正式環境&#xff09;&#xff0c;接口文檔地址不同&#xff1b; 配置步驟&#xff1a; 1、在根目錄下面新建 .env.xxx 文件&#xff08;xxx 根據環境不同配置&#xff09; 文件中一定要配置的參數項為&#xff1a;NODE_ENV…

【Python搞定車載自動化測試】——Python實現CAN總線Bootloader刷寫(含Python源碼)

系列文章目錄 【Python搞定車載自動化測試】系列文章目錄匯總 文章目錄 系列文章目錄&#x1f4af;&#x1f4af;&#x1f4af; 前言&#x1f4af;&#x1f4af;&#x1f4af;一、環境搭建1.軟件環境2.硬件環境 二、目錄結構三、源碼展示1.診斷基礎函數方法2.診斷業務函數方法…

python 火焰檢測

在日常生活,總是離不開火,有時候我們需要預防火災發生,但是我們又不可能一直盯著,這時候我們就需要一款程序幫我們盯著,一旦發生火災從而告知我們,今天就帶大家編寫這么一款應用。 安裝需要的庫 pip install opencv-python 代碼實現 import cv2 # Library for…

qmt量化教程4----訂閱全推數據

文章鏈接 qmt量化教程4----訂閱全推數據 (qq.com) 上次寫了訂閱單股數據的教程 量化教程3---miniqmt當作第三方庫設置&#xff0c;提供源代碼 全推就主動推送&#xff0c;當行情有變化就會觸發回調函數&#xff0c;推送實時數據&#xff0c;可以理解為數據驅動類型&#xff0…

mysql中使用 mysqldump 實現跨機器備份|數據同步

1.如果同步數據庫&#xff0c;必須先創建數據庫&#xff1a; mysqldump -h 192.168.1.10 --lock-tablesfalse -uroot -proot db_name | mysql -h127.0.0.1 -uroot -proot db_name2.過濾掉不想要的表(沒試過&#xff0c;但是試過轉為sql文件的) mysqldump -h 192.168.1.10 --…

vs2019 c++ 函數的返回值是對象的值傳遞時候,將調用對象的移動構造函數

以前倒沒有注意過這個問題。但編譯器這么處理也符合移動構造的語義。因為本來函數體內的變量也要離開作用域被銷毀回收了。測試如下&#xff1a; 謝謝