主頁 > 後端開發 > 深入Java微服務之網關系列3: SpringCloudalibaba gateway詳解(史上最全)

深入Java微服務之網關系列3: SpringCloudalibaba gateway詳解(史上最全)

2022-03-05 06:11:14 後端開發

九、服務網關:Gateway

image-20210507115602441

9.1、網關簡介

大家都都知道在微服務架構中,一個系統會被拆分為很多個微服務,那么作為客戶端要如何去呼叫這么多的微服務呢?如果沒有網關的存在,我們只能在客戶端記錄每個微服務的地址,然后分別去呼叫,

image-20210507115048632

這樣的架構會存在許多的問題:

  1. 客戶端多次請求不同的微服務,增加客戶端代碼或配置撰寫的復雜性,
  2. 認證復雜,每個服務都需要獨立認證,
  3. 存在跨域請求,在一定場景下處理相對復雜,

網關就是為了解決這些問題而生的,所謂的API網關,就是指系統的統一入口,它封裝了應用程式的內部結構,為客戶端提供統一服務,一些與業務本身功能無關的公共邏輯可以在這里實作,諸如認證、鑒權、監控、路由轉發等等,

image-20210507115517531

9.2、常用的網關

9.2.1、Ngnix+lua

使用nginx的反向代理和負載均衡可實作對api服務器的負載均衡及高可用,

lua是一種腳本語言,可以來撰寫一些簡單的邏輯, nginx支持lua腳本

9.2.2、Kong

基于Nginx+Lua開發,性能高,穩定,有多個可用的插件(限流、鑒權等等)可以開箱即用,

他的缺點:

  1. 只支持Http協議,
  2. 二次開發,自由擴展困難,
  3. 提供管理API,缺乏更易用的管控、配置方式,

9.2.3、Zuul

Netflix開源的網關,功能豐富,使用JAVA開發,易于二次開發,

他的缺點:

  1. 缺乏管控,無法動態配置,
  2. 依賴組件較多,
  3. 處理Http請求依賴的是Web容器,性能不如Nginx,

9.2.4、Spring Cloud Gateway

Spring公司為了替換Zuul而開發的網關服務,SpringCloud alibaba技術堆疊中并沒有提供自己的網關,我們可以采用Spring Cloud Gateway來做網關

9.3、Gateway簡介

Spring Cloud Gateway是Spring公司基于Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技術開發的網關,它旨在為微服務架構提供一種簡單有效的統一的 API 路由管理方式,它的目標是替代Netflix Zuul,其不僅提供統一的路由方式,并且基于 Filter 鏈的方式提供了網關基本的功能,例如:安全,監控和限流,

他的主要功能是:

  1. 進行轉發重定向,
  2. 在開始的時候,所有類都需要做的初始化操作,
  3. 進行網路隔離,

9.4、快速入門

需求:通過瀏覽器訪問api網關,然后通過網關將請求轉發到商品微服務,

9.4.1、基礎版

創建一個api-gateway 模塊,并且匯入下面的依賴,

<?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>
    Shop-parent
    <groupId>cn.linstudy</groupId>
    <version>1.0.0</version>
  </parent>
  <modelVersion>4.0.0</modelVersion>
  api-gateway

  <properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  </properties>

  <dependencies>
    <!--gateway網關-->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      spring-cloud-starter-gateway
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      lombok
    </dependency>
  </dependencies>

</project>
復制代碼

撰寫組態檔

server:
  port: 9000 # 指定網關服務的埠
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes: # 路由陣列[路由 就是指定當請求滿足什么條件的時候轉到哪個微服務]
        - id: product_route # 當前路由的標識, 要求唯一
          uri: http://localhost:8081 # 請求要轉發到的地址
          order: 1 # 路由的優先級,數字越小級別越高
          predicates: # 斷言(就是路由轉發要滿足的條件)
            - Path=/product-serv/** # 當請求路徑滿足Path指定的規則時,才進行路由轉發
          filters: # 過濾器,請求在傳遞程序中可以通過過濾器對其進行一定的修改
            - StripPrefix=1 # 轉發之前去掉1層路徑
復制代碼

測驗

image-20210507171142491

9.4.2、升級版

我們發現升級版有一個很大的問題,那就是在組態檔中寫死了轉發路徑的地址,我們需要在注冊中心來獲取地址,

加入nacos依賴

<?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>
    Shop-parent
    <groupId>cn.linstudy</groupId>
    <version>1.0.0</version>
  </parent>
  <modelVersion>4.0.0</modelVersion>
  api-gateway

  <properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  </properties>

  <dependencies>
    <!--gateway網關-->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      spring-cloud-starter-gateway
    </dependency>
    <!--nacos客戶端-->
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      spring-cloud-starter-alibaba-nacos-discovery
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      lombok
    </dependency>
  </dependencies>

</project>
復制代碼

在主類上添加注解

@SpringBootApplication
@EnableDiscoveryClient
public class GateWayServerApp {
  public static void main(String[] args) {
    SpringApplication.run(GateWayServerApp.class,args);
  }
}
復制代碼

修改組態檔

server:
  port: 9000
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          enabled: true # 讓gateway可以發現nacos中的微服務
      routes:
        - id: product_route # 路由的名字
          uri: lb://product-service # lb指的是從nacos中按照名稱獲取微服務,并遵循負載均衡策略
          predicates:
            - Path=/product-serv/** # 符合這個規定的才進行1轉發
          filters:
            - StripPrefix=1 # 將第一層去掉
復制代碼

我們還可以自定義多個路由規則,

spring:
  application:
    gateway:
      routes:
        - id: product_route
          uri: lb://product-service 
          predicates:
            - Path=/product-serv/**
          filters:
            - StripPrefix=1
        - id: order_route
          uri: lb://order-service 
          predicates:
            - Path=/order-serv/**
          filters:
            - StripPrefix=1
復制代碼

9.4.3、簡寫版

我們的組態檔無需寫的1那么復雜就可以實作功能,有一個簡寫版,

server:
  port: 9000
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      discovery:
        locator:
          enabled: true # 讓gateway可以發現nacos中的微服務
復制代碼

image-20210507201625163

我們發現,就發現只要按照網關地址/微服務名稱/介面的格式去訪問,就可以得到成功回應,

9.5、Gateway核心架構

9.5.1、基本概念

路由(Route) 是 gateway 中最基本的組件之一,表示一個具體的路由資訊載體,主要定義了下面的幾個資訊:

  1. id:路由識別符號,區別于其他 Route,
  2. uri:路由指向的目的地 uri,即客戶端請求最終被轉發到的微服務,
  3. order:用于多個 Route 之間的排序,數值越小排序越靠前,匹配優先級越高,
  4. predicate:斷言的作用是進行條件判斷,只有斷言都回傳真,才會真正的執行路由,
  5. filter:過濾器用于修改請求和回應資訊,
  6. predicate:斷言,用于進行條件判斷,只有斷言都回傳真,才會真正的執行路由,

9.5.2、執行原理

image-20201030161652819

  1. 接收用戶的請求,請求處理器交給處理器映射器,回傳執行鏈,
  2. 請求處理器去呼叫web處理器,在web處理器里面對我們的路徑1進行處理,假設1我們的路徑1是:http://localhost:9000/product-serv/get?id=1 ,根據配置的路由規則,上本地找對應的服務資訊:product-service對應的主機ip是192.168.10.130,
  3. 根據1ribbon的負載均衡策略去選擇一個節點,然后拼接好,將路徑中的product-serv替換成192.168.10.130:8081,如果你配置了filter,那么他還會走filter,
  4. 如果你沒有自定義路由的話,默認Gateway會幫你把第一層去掉,網關埠從此一個/開始到第二個/開始算第一層,

image-20210507203258953

9.6、過濾器

Gateway的過濾器的作用是:是在請求的傳遞程序中,對請求和回應做一些手腳,

Gateway的過濾器的生命周期:

  1. PRE:這種過濾器在請求被路由之前呼叫,我們可利用這種過濾器實作身份驗證、在集群中選擇 請求的微服務、記錄除錯資訊等,
  2. POST:這種過濾器在路由到微服務以后執行,這種過濾器可用來為回應添加標準的HTTP Header、收集統計資訊和指標、將回應從微服務發送給客戶端等,

Gateway 的Filter從作用范圍可分為兩種: GatewayFilter與GlobalFilter:

  1. GatewayFilter:應用到單個路由或者一個分組的路由上,
  2. GlobalFilter:應用到所有的路由上,

9.6.1、區域過濾器

區域過濾器是針對單個路由的過濾器,他分為內置過濾器和自定義過濾器,

9.6.1.1、內置過濾器

在SpringCloud Gateway中內置了很多不同型別的網關路由過濾器,

9.6.1.1.1、區域過濾器內容
過濾器工廠 作用 引數
AddRequestHeader 為原始請求添加Header Header的名稱及值
AddRequestParameter 為原始請求添加請求引數 引數名稱及值
AddResponseHeader 為原始回應添加Header Header的名稱及值
DedupeResponseHeader 剔除回應頭中重復的值 需要去重的Header名稱及去重策略
Hystrix 為路由引入Hystrix的斷路器保護 HystrixCommand 的名稱
FallbackHeaders 為fallbackUri的請求頭中添加具體的例外資訊 Header的名稱
PrefixPath 為原始請求路徑添加前綴 前綴路徑
PreserveHostHeader 為請求添加一個preserveHostHeader=true的屬性,路由過濾器會檢查該屬性以決定是否要發送原始的Host
RequestRateLimiter 用于對請求限流,限流演算法為令牌桶 keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus
RedirectTo 將原始請求重定向到指定的URL http狀態碼及重定向的url
RemoveHopByHopHeadersFilter 為原始請求洗掉IETF組織規定的一系列Header 默認就會啟用,可以通過配置指定僅洗掉哪些Header
RemoveRequestHeader 為原始請求洗掉某個Header Header名稱
RemoveResponseHeader 為原始回應洗掉某個Header Header名稱
RewritePath 重寫原始的請求路徑 原始路徑正則運算式以及重寫后路徑的正則運算式
RewriteResponseHeader 重寫原始回應中的某個Header Header名稱,值的正則運算式,重寫后的值
SaveSession 在轉發請求之前,強制執行WebSession::save操作
secureHeaders 為原始回應添加一系列起安全作用的回應頭 無,支持修改這些安全回應頭的值
SetPath 修改原始的請求路徑 修改后的路徑
SetResponseHeader 修改原始回應中某個Header的值 Header名稱,修改后的值
SetStatus 修改原始回應的狀態碼 HTTP 狀態碼,可以是數字,也可以是字串
StripPrefix 用于截斷原始請求的路徑 使用數字表示要截斷的路徑的數量
Retry 針對不同的回應進行重試 retries、statuses、methods、series
RequestSize 設定允許接收最大請求包的大小,如果請求包大小超過設定的值,則回傳 413 Payload Too Large 請求包大小,單位為位元組,默認值為5M
ModifyRequestBody 在轉發請求之前修改原始請求體內容 修改后的請求體內容
ModifyResponseBody 修改原始回應體的內容 修改后的回應體內容
9.6.1.1.2、區域過濾器的使用
server:
  port: 9000
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          enabled: true # 讓gateway可以發現nacos中的微服務
      routes:
        - id: product_route # 路由的名字
          uri: lb://product-service # lb指的是從nacos中按照名稱獲取微服務,并遵循負載均衡策略
          predicates:
            - Path=/product-serv/** # 符合這個規定的才進行1轉發
          filters:
            - StripPrefix=1 # 將第一層去掉
            - SetStatus=2000 # 這里使用內置的過濾器,修改回傳狀態
復制代碼

9.6.1.2、自定義區域過濾器

很多的時候,內置過濾器沒辦法滿足我們的需求,這個時候就必須自定義區域過濾器,我們假定一個需求是:統計訂單服務呼叫耗時,

撰寫一個類,用于實作邏輯

名稱是有固定格式xxxGatewayFilterFactory

@Component
public class TimeGatewayFilterFactory extends AbstractGatewayFilterFactory<TimeGatewayFilterFactory.Config> {

  private static final String BEGIN_TIME = "beginTime";

  //建構式
  public TimeGatewayFilterFactory() {
    super(TimeGatewayFilterFactory.Config.class);
  }

  //讀取組態檔中的引數 賦值到 配置類中
  @Override
  public List<String> shortcutFieldOrder() {
    return Arrays.asList("show");
  }

  @Override
  public GatewayFilter apply(Config config) {
    return new GatewayFilter() {
      @Override
      public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        if (!config.show){
          // 如果配置類中的show為false,表示放行
          return chain.filter(exchange);
        }
        exchange.getAttributes().put(BEGIN_TIME, System.currentTimeMillis());
        /**
         *  pre的邏輯
         * chain.filter().then(Mono.fromRunable(()->{
         *     post的邏輯
         * }))
         */
        return chain.filter(exchange).then(Mono.fromRunnable(()->{
          Long startTime = exchange.getAttribute(BEGIN_TIME);
          if (startTime != null) {
            System.out.println(exchange.getRequest().getURI() + "請求耗時: " + (System.currentTimeMillis() - startTime) + "ms");
          }
        }));
      }
    };
  }

  @Setter
  @Getter
  static class Config{
    private boolean show;
  }

}
復制代碼

撰寫application.xml

server:
  port: 9000
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          enabled: true # 讓gateway可以發現nacos中的微服務
      routes:
        - id: product_route # 路由的名字
          uri: lb://product-service # lb指的是從nacos中按照名稱獲取微服務,并遵循負載均衡策略
          predicates:
            - Path=/product-serv/** # 符合這個規定的才進行1轉發
          filters:
            - StripPrefix=1 # 將第一層去掉
        - id: order_route
          uri: lb://order-service
          predicates:
            - Path=/order-serv/**
          filters:
            - StripPrefix=1
            - Time=true
復制代碼

訪問路徑:http://localhost:9000/order-serv/getById?o=1&pid=1

在這里插入圖片描述

9.6.2、全域過濾器

全域過濾器作用于所有路由, 無需配置,通過全域過濾器可以實作對權限的統一校驗,安全性驗證等功能,SpringCloud Gateway內部也是通過一系列的內置全域過濾器對整個路由轉發進行處理,

網關全域過濾器

開發中的鑒權邏輯:

  • 當客戶端第一次請求服務時,服務端對用戶進行資訊認證(登錄),
  • 認證通過,將用戶資訊進行加密形成token,回傳給客戶端,作為登錄憑證,
  • 以后每次請求,客戶端都攜帶認證的token,
  • 服務端對token進行解密,判斷是否有效,

image-20210507220009473

我們來模擬一個需求:實作統一鑒權的功能,我們需要在網關判斷請求中是否包含token且,如果沒有則不轉發路由,有則執行正常邏輯,

撰寫全域過濾器

@Component
public class AuthGlobalFilter implements GlobalFilter {

  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    String token = exchange.getRequest().getQueryParams().getFirst("token");
    if (StringUtils.isBlank(token)) {
      System.out.println("鑒權失敗");
      exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
      return exchange.getResponse().setComplete();
    }
    return chain.filter(exchange);
  }
}
復制代碼

9.6.3、網關限流

網關是所有請求的公共入口,所以可以在網關進行限流,而且限流的方式也很多,我們本次采用前面學過的Sentinel組件來實作網關的限流,Sentinel支持對SpringCloud Gateway、Zuul等主流網關進行限流,

image-20210507220048921

從1.6.0版本開始,Sentinel提供了SpringCloud Gateway的適配模塊,可以提供兩種資源維度的限流:

  • route維度:即在Spring組態檔中配置的路由條目,資源名為對應的routeId
  • 自定義API維度:用戶可以利用Sentinel提供的API來自定義一些API分組

9.6.3.1、網關集成Sentinel

添加依賴

<dependency>
	<groupId>com.alibaba.csp</groupId>
	sentinel-spring-cloud-gateway-adapter
</dependency>
復制代碼

撰寫配置類進行限流

配置類的本質是用代碼替代nacos圖形化界面限流,

@Configuration
public class GatewayConfiguration {
    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }
    // 配置限流的例外處理器
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        // Register the block exception handler for Spring Cloud Gateway.
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }
    // 初始化一個限流的過濾器
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }
    //增加對商品微服務的限流
     @PostConstruct
    private void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("product_route")
                .setCount(3) // 三次
                .setIntervalSec(1) // 一秒,表示一秒鐘1超過了三次就會限流
        );
        GatewayRuleManager.loadRules(rules);
    }
}
復制代碼

修改限流默認回傳格式

如果我們不想在限流的時候回傳默認的錯誤,那么就需要自定義錯誤,指定自定義的回傳格式,我們只需在類中添加一段配置即可,

@PostConstruct
public void initBlockHandlers() {
	BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
	public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
		Map map = new HashMap<>();
		map.put("code", 0);
		map.put("message", "介面被限流了");
			return ServerResponse.status(HttpStatus.OK).
				contentType(MediaType.APPLICATION_JSON).
				body(BodyInserters.fromValue(map));
            }
};
	GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
復制代碼

測驗

image-20210507221350190

9.6.3.2、自定義API分組

我們可以發現,上面的這種定義,對整個服務進行了限流,粒度不夠細,自定義API分組是一種更細粒度的限流規則定義,它可以實作某個方法的細粒度限流,

在Shop-order-server專案中添加ApiController

@RestController
@RequestMapping("/api")
public class ApiController {
    @RequestMapping("/hello")
    public String api1(){
        return "api";
    }
}
復制代碼

在GatewayConfiguration中添加配置

@PostConstruct
private void initCustomizedApis() {
	Set<ApiDefinition> definitions = new HashSet<>();
	ApiDefinition api1 = new ApiDefinition("order_api")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    add(new ApiPathPredicateItem().setPattern("/order-serv/api/**").                 setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});
	definitions.add(api1);
	GatewayApiDefinitionManager.loadApiDefinitions(definitions);
}
@PostConstruct
private void initGatewayRules() {
	Set<GatewayFlowRule> rules = new HashSet<>();
	rules.add(new GatewayFlowRule("product_route")
                .setCount(3)
                .setIntervalSec(1)
	);
	rules.add(new GatewayFlowRule("order_api").
                setCount(1).
                setIntervalSec(1));
    GatewayRuleManager.loadRules(rules);
}
復制代碼

測驗

直接訪問http://localhost:8082/api/hello 是不會發生限流的,訪問http://localhost:9000/order-serv/api/hello 就會出現限流了,

作者:XiaoLin_Java
鏈接:https://juejin.cn/post/7001816849826447397
來源:稀土掘金
著作權歸作者所有,商業轉載請聯系作者獲得授權,非商業轉載請注明出處,

微信公眾號【程式員黃小斜】作者是前螞蟻金服Java工程師,專注分享Java技術干貨和求職成長心得,不限于BAT面試,演算法、計算機基礎、資料庫、分布式、spring全家桶、微服務、高并發、JVM、Docker容器,ELK、大資料等,關注后回復【book】領取精選20本Java面試必備精品電子書,

?

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/437872.html

標籤:Java

上一篇:吃透SpringBoo的這些t知識,你就已經超過90%的Java面試者了

下一篇:【JavaWeb】互聯網通信流程 --- 互聯網通信模型;B/S 通信模型;共享資源檔案;開發人員在互聯網通信流程中擔負的職責

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more