微服務網關 Spring Cloud Gateway

来源:https://www.cnblogs.com/cjsblog/archive/2019/07/05/11099234.html
-Advertisement-
Play Games

1. 為什麼是Spring Cloud Gateway 一句話,Spring Cloud已經放棄Netflix Zuul了。現在Spring Cloud中引用的還是Zuul 1.x版本,而這個版本是基於過濾器的,是阻塞IO,不支持長連接。Zuul 2.x版本跟1.x的架構大一樣,性能也有所提升。既然 ...


1.  為什麼是Spring Cloud Gateway

一句話,Spring Cloud已經放棄Netflix Zuul了。現在Spring Cloud中引用的還是Zuul 1.x版本,而這個版本是基於過濾器的,是阻塞IO,不支持長連接。Zuul 2.x版本跟1.x的架構大一樣,性能也有所提升。既然Spring Cloud已經不再集成Zuul 2.x了,那麼是時候瞭解一下Spring Cloud Gateway了。

可以看到,最新的Spring Cloud中的Zuul還是1.3.1版本

而且,官網中也明確說了不再維護Zuul了

(PS:順便補充幾個名詞: 服務發現(Eureka),斷路器(Hystrix),智能路由(Zuul),客戶端負載均衡(Ribbon))

2.  API網關

API網關是一個伺服器,是系統的唯一入口。從面向對象設計的角度看,它與外觀模式類似。API網關封裝了系統內部架構,為每個客戶端提供一個定製的API。它可能還具有其它職責,如身份驗證、監控、負載均衡、緩存、請求分片與管理、靜態響應處理。API網關方式的核心要點是,所有的客戶端和消費端都通過統一的網關接入微服務,在網關層處理所有的非業務功能。通常,網關也是提供REST/HTTP的訪問API。

網關應當具備以下功能:

  • 性能:API高可用,負載均衡,容錯機制。
  • 安全:許可權身份認證、脫敏,流量清洗,後端簽名(保證全鏈路可信調用),黑名單(非法調用的限制)。
  • 日誌:日誌記錄(spainid,traceid)一旦涉及分散式,全鏈路跟蹤必不可少。
  • 緩存:數據緩存。
  • 監控:記錄請求響應數據,api耗時分析,性能監控。
  • 限流:流量控制,錯峰流控,可以定義多種限流規則。
  • 灰度:線上灰度部署,可以減小風險。
  • 路由:動態路由規則。

目前,比較流行的網關有:Nginx 、 Kong 、Orange等等,還有微服務網關Zuul 、Spring Cloud Gateway等等

對於 API Gateway,常見的選型有基於 Openresty 的 Kong、基於 Go 的 Tyk 和基於 Java 的 Zuul。這三個選型本身沒有什麼明顯的區別,主要還是看技術棧是否能滿足快速應用和二次開發。

以上說的這些功能,這些開源的網關組件都有,或者藉助Lua也能實現,比如:Nginx + Lua

那要Spring Cloud Gateway還有什麼用呢?

其實,我個人理解是這樣的:

  • 像Nginx這類網關,性能肯定是沒得說,它適合做那種門戶網關,是作為整個全局的網關,是對外的,處於最外層的;而Gateway這種,更像是業務網關,主要用來對應不同的客戶端提供服務的,用於聚合業務的。各個微服務獨立部署,職責單一,對外提供服務的時候需要有一個東西把業務聚合起來。
  • 像Nginx這類網關,都是用不同的語言編寫的,不易於擴展;而Gateway就不同,它是用Java寫的,易於擴展和維護
  • Gateway這類網關可以實現熔斷、重試等功能,這是Nginx不具備的

所以,你看到的網關可能是這樣的:

 

2.1.  Netflix Zuul 1.x  VS  Netflix Zuul 2.x

  

3.  Spring Cloud Gateway

3.1.  特性

  • 基於Spring Framework 5、Project Reactor和Spring Boot 2.0構建
  • 能夠在任意請求屬性上匹配路由
  • predicates(謂詞) 和 filters(過濾器)是特定於路由的
  • 集成了Hystrix斷路器
  • 集成了Spring Cloud DiscoveryClient
  • 易於編寫謂詞和過濾器
  • 請求速率限制
  • 路徑重寫

3.2.  術語

Route : 路由是網關的基本組件。它由ID、目標URI、謂詞集合和過濾器集合定義。如果聚合謂詞為true,則匹配路由

Predicate : This is a Java 8 Function Predicate

Filter : 是GatewayFilter的一個實例,在這裡,可以在發送下游請求之前或之後修改請求和響應

3.3.  原理

(PS:看到這張圖是不是很熟悉,沒錯,很像SpringMVC的請求處理過程)

客戶端向Spring Cloud Gateway發出請求。如果Gateway Handler Mapping確定請求與路由匹配,則將其發送給Gateway Web Handler。這個Handler運行通過特定於請求的過濾器鏈發送請求。過濾器可以在發送代理請求之前或之後執行邏輯。執行所有的“pre”過濾邏輯,然後發出代理請求,最後執行“post”過濾邏輯。

3.4.  Route Predicate Factories

  • Spring Cloud Gateway 包含許多內置的 Route Predicate Factories
  • 所有這些predicates用於匹配HTTP請求的不同屬性
  • 多個 Route Predicate Factories 可以通過邏輯與(and)結合起來一起使用

3.4.1.  After Route Predicate Factory

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]

這個路由匹配“美國丹佛時間2017-01-20 17:42”之後的任意請求

3.4.2.  Header Route Predicate Factory

spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: https://example.org
        predicates:
        - Header=X-Request-Id, \d+

這個路由匹配“請求頭包含X-Request-Id並且其值匹配正則表達式\d+”的任意請求

3.4.3.  Method Route Predicate Factory

spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: https://example.org
        predicates:
        - Method=GET

這個路由匹配任意GET請求

3.4.4.  Path Route Predicate Factory

spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: https://example.org
        predicates:
        - Path=/foo/{segment},/bar/{segment}

這個路由匹配這樣路徑的請求,比如:/foo/1 或 /foo/bar 或 /bar/baz

3.4.5.  Query Route Predicate Factory

這個Predicate有兩個參數:一個必須的參數名和一個可選的正則表達式

spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: https://example.org
        predicates:
        - Query=baz

這個路由匹配“查詢參數中包含baz”的請求

spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: https://example.org
        predicates:
        - Query=foo, ba.

這個路由匹配“查詢參數中包含foo,並且其參數值滿足正則表達式ba.”的請求,比如:bar,baz

3.4.6.  RemoteAddr Route Predicate Factory

這個路由接受一個IP(IPv4或IPv6)地址字元串。例如:192.168.0.1/16,其中192.168.0.1,16是子網掩碼

spring:
  cloud:
    gateway:
      routes:
      - id: remoteaddr_route
        uri: https://example.org
        predicates:
        - RemoteAddr=192.168.1.1/24

這裡路由匹配遠程地址是這樣的請求,例如:192.168.1.10

3.5.  GatewayFilter Factories(網關過濾器)

路由過濾器允許以某種方式修改傳入的HTTP請求或傳出HTTP響應。路由過濾器的作用域是特定的路由。Spring Cloud Gateway包含許多內置的網關過濾器工廠。

3.5.1.  AddRequestHeader GatewayFilter Factory

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: https://example.org
        filters:
        - AddRequestHeader=X-Request-Foo, Bar

對於所有匹配的請求,將會給傳給下游的請求添加一個請求頭 X-Request-Foo:Bar

3.5.2.  AddRequestParameter GatewayFilter Factory

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_parameter_route
        uri: https://example.org
        filters:
        - AddRequestParameter=foo, bar

對於所有匹配的請求,將給傳給下游的請求添加一個查詢參數 foo=bar

3.5.3.  AddResponseHeader GatewayFilter Factory

spring:
  cloud:
    gateway:
      routes:
      - id: add_response_header_route
        uri: https://example.org
        filters:
        - AddResponseHeader=X-Response-Foo, Bar

對於所有匹配的請求,添加一個響應頭 X-Response-Foo:Bar

3.5.4.  Hystrix GatewayFilter Factory

Hystrix網關過濾器允許你將斷路器引入網關路由,保護你的服務免受級聯失敗的影響,併在下游發生故障時提供預備響應。

為了啟用Hystrix網關過濾器,你需要引入 spring-cloud-starter-netflix-hystrix

Hystrix網關過濾器需要一個name參數,這個nameHystrixCommand的名字

spring:
  cloud:
    gateway:
      routes:
      - id: hystrix_route
        uri: https://example.org
        filters:
        - Hystrix=myCommandName

給這個過濾器包裝一個名字叫myCommandNameHystrixCommand

Hystrix網關過濾器也接受一個可選的參數fallbackUri,但是目前只支持forward:首碼的URL。也就是說,如果這個fallback被調用,請求將被重定向到匹配的這個URL。

spring:
  cloud:
    gateway:
      routes:
      - id: hystrix_route
        uri: lb://backing-service:8088
        predicates:
        - Path=/consumingserviceendpoint
        filters:
        - name: Hystrix
          args:
            name: fallbackcmd
            fallbackUri: forward:/incaseoffailureusethis
        - RewritePath=/consumingserviceendpoint, /backingserviceendpoint

當fallback被調用的時候,請求將被重定向到/incaseoffailureusethis

spring:
  cloud:
    gateway:
      routes:
      - id: ingredients
        uri: lb://ingredients
        predicates:
        - Path=//ingredients/**
        filters:
        - name: Hystrix
          args:
            name: fetchIngredients
            fallbackUri: forward:/fallback
      - id: ingredients-fallback
        uri: http://localhost:9994
        predicates:
        - Path=/fallback

在這個例子中,專門定義了一個端點來處理/fallback請求,它在localhost:9994上。也就是說,當fallback被調用的時候將重定向到http://localhost:9994/fallback

3.5.5.  PrefixPath GatewayFilter Factory

spring:
  cloud:
    gateway:
      routes:
      - id: prefixpath_route
        uri: https://example.org
        filters:
        - PrefixPath=/mypath

所有匹配的請求都將加上首碼/mypath。例如,如果請求是/hello,那麼經過這個過濾器後,發出去的請求變成/mypath/hello

3.5.6.  RequestRateLimiter GatewayFilter Factory

RequestRateLimiter網關過濾器使用一個RateLimiter實現來決定是否當前請求可以繼續往下走。如果不能,預設將返回HTTP 429 - Too Many Requests

這個過濾器接受一個可選的參數keyResolver,這個參數是一個特定的rate limiter

keyResolver是實現了KeyResolver介面的一個Bean。

在配置的時候,使用SpEL按名稱引用Bean。#{@myKeyResolver}是一個SpEL表達式,表示引用名字叫myKeyResolver的Bean。

KeyResolver.java 

1 public interface KeyResolver {
2 	Mono<String> resolve(ServerWebExchange exchange);
3 }

KeyResolver預設的實現是PrincipalNameKeyResolver,它從ServerWebExchange中檢索Principal,並調用Principal.getName()方法。 

預設情況下,如果KeyResolver沒有找到一個key,那麼請求將會被denied(譯:否認,拒絕)。這種行為可以通過spring.cloud.gateway.filter.request-rate-limiter.deny-empty-key (true or false) 和 spring.cloud.gateway.filter.request-rate-limiter.empty-key-status-code 屬性來進行調整. 

Redis RateLimiter

需要引用 spring-boot-starter-data-redis-reactive

這個邏輯使用令牌桶演算法

  • redis-rate-limiter.replenishRate : 允許用戶每秒處理多少個請求。這是令牌桶被填充的速率。
  • redis-rate-limiter.burstCapacity : 用戶在一秒鐘內允許執行的最大請求數。這是令牌桶可以容納的令牌數量。將此值設置為0將阻塞所有請求。

一個穩定的速率是通過將replenishRate 和 burstCapacity設為相同的值來實現的。也可以將burstCapacity設得比replenishRate大,以應對臨時爆發的流量。在這種情況下,需要允許速率限制器在突發事件之間間隔一段時間,因為連續兩次突發事件將導致丟棄請求(HTTP 429 - Too Many Requests)

application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: requestratelimiter_route
        uri: https://example.org
        filters:
        - name: RequestRateLimiter
          args:
            redis-rate-limiter.replenishRate: 10
            redis-rate-limiter.burstCapacity: 20

Config.java

1 @Bean
2 KeyResolver userKeyResolver() {
3     return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
4 }

這裡定義了每個用戶的請求速率限製為10。允許使用20個請求,但是在接下來的一秒中,只有10個請求可用。

這個例子中只是簡單地從請求參數中獲取"user",在實際生產環境中不建議這麼做。

我們也可以通過實現RateLimiter介面來自定義,這個時候,在配置中我們就需要引用這個Bean,例如:#{@myRateLimiter} 

spring:
  cloud:
    gateway:
      routes:
      - id: requestratelimiter_route
        uri: https://example.org
        filters:
        - name: RequestRateLimiter
          args:
            rate-limiter: "#{@myRateLimiter}"
            key-resolver: "#{@userKeyResolver}"

3.5.7.  Default Filters

如果你想要添加一個過濾器並且把它應用於所有路由的話,你可以用spring.cloud.gateway.default-filters。這個屬性接受一個過濾器列表。

spring:
  cloud:
    gateway:
      default-filters:
      - AddResponseHeader=X-Response-Default-Foo, Default-Bar
      - PrefixPath=/httpbin

3.6.  Global Filters(全局過濾器)

GlobalFilter介面的方法簽名和GatewayFilter相同。這些是有條件地應用於所有路由的特殊過濾器。

3.6.1.  GlobalFilter和GatewayFilter的順序

當一個請求過來的時候,將會添加所有的GatewayFilter實例和所有特定的GatewayFilter實例到過濾器鏈上。過濾器鏈按照org.springframework.core.Ordered介面對該鏈路上的過濾器進行排序。你可以通過實現介面中的getOrder()方法或者使用@Order註解。

Spring Cloud Gateway將過濾器執行邏輯分為“pre”和“post”階段。優先順序最高的過濾器將會是“pre”階段中的第一個過濾器,同時它也將是“post”階段中的最後一個過濾器。

ExampleConfiguration.java 

 1 @Bean
 2 @Order(-1)
 3 public GlobalFilter a() {
 4     return (exchange, chain) -> {
 5         log.info("first pre filter");
 6         return chain.filter(exchange).then(Mono.fromRunnable(() -> {
 7             log.info("third post filter");
 8         }));
 9     };
10 }
11 
12 @Bean
13 @Order(0)
14 public GlobalFilter b() {
15     return (exchange, chain) -> {
16         log.info("second pre filter");
17         return chain.filter(exchange).then(Mono.fromRunnable(() -> {
18             log.info("second post filter");
19         }));
20     };
21 }
22 
23 @Bean
24 @Order(1)
25 public GlobalFilter c() {
26     return (exchange, chain) -> {
27         log.info("third pre filter");
28         return chain.filter(exchange).then(Mono.fromRunnable(() -> {
29             log.info("first post filter");
30         }));
31     };
32 }

3.6.2.  LoadBalancerClient Filter

LoadBalancerClientFilter查找exchange屬性中查找ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR一個URI。如果url符合lb schema(例如:lb://myservice),那麼它將使用Spring Cloud LoadBalancerClient 來解析這個名字到一個實際的主機和埠,並替換URI中相同的屬性。原始url中未被修改的部分被附加到ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR屬性列表中。

application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: myRoute
        uri: lb://service
        predicates:
        - Path=/service/**

預設情況下,當一個服務實例在LoadBalancer中沒有找到時,將返回503。你可以通過配置spring.cloud.gateway.loadbalancer.use404=true來讓它返回404。

3.7.  配置

RouteDefinitionLocator.java 

1 public interface RouteDefinitionLocator {
2 	Flux<RouteDefinition> getRouteDefinitions();
3 }

預設情況下,PropertiesRouteDefinitionLocator通過@ConfigurationProperties機制載入屬性

下麵兩段配置是等價的

spring:
  cloud:
    gateway:
      routes:
      - id: setstatus_route
        uri: https://example.org
        filters:
        - name: SetStatus
          args:
            status: 401
      - id: setstatusshortcut_route
        uri: https://example.org
        filters:
        - SetStatus=401

下麵用Java配置

GatewaySampleApplication.java 

 1 // static imports from GatewayFilters and RoutePredicates
 2 @Bean
 3 public RouteLocator customRouteLocator(RouteLocatorBuilder builder, ThrottleGatewayFilterFactory throttle) {
 4     return builder.routes()
 5             .route(r -> r.host("**.abc.org").and().path("/image/png")
 6                 .filters(f ->
 7                         f.addResponseHeader("X-TestHeader", "foobar"))
 8                 .uri("http://httpbin.org:80")
 9             )
10             .route(r -> r.path("/image/webp")
11                 .filters(f ->
12                         f.addResponseHeader("X-AnotherHeader", "baz"))
13                 .uri("http://httpbin.org:80")
14             )
15             .route(r -> r.order(-1)
16                 .host("**.throttle.org").and().path("/get")
17                 .filters(f -> f.filter(throttle.apply(1,
18                         1,
19                         10,
20                         TimeUnit.SECONDS)))
21                 .uri("http://httpbin.org:80")
22             )
23             .build();
24 }

這種風格允許自定義更多的謂詞斷言,預設是邏輯與(and)。你也可以用and() , or() , negate() 

再來一個例子

 1 @SpringBootApplication
 2 public class DemogatewayApplication {
 3 	@Bean
 4 	public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
 5 		return builder.routes()
 6 			.route("path_route", r -> r.path("/get")
 7 				.uri("http://httpbin.org"))
 8 			.route("host_route", r -> r.host("*.myhost.org")
 9 				.uri("http://httpbin.org"))
10 			.route("hystrix_route", r -> r.host("*.hystrix.org")
11 				.filters(f -> f.hystrix(c -> c.setName("slowcmd")))
12 				.uri("http://httpbin.org"))
13 			.route("hystrix_fallback_route", r -> r.host("*.hystrixfallback.org")
14 				.filters(f -> f.hystrix(c -> c.setName("slowcmd").setFallbackUri("forward:/hystrixfallback")))
15 				.uri("http://httpbin.org"))
16 			.route("limit_route", r -> r
17 				.host("*.limited.org").and().path("/anything/**")
18 				.filters(f -> f.requestRateLimiter(c -> c.setRateLimiter(redisRateLimiter())))
19 				.uri("http://httpbin.org"))
20 			.build();
21 	}
22 }

3.8.  CORS配置

spring:
  cloud:
    gateway:
      globalcors:
        corsConfigurations:
          '[/**]':
            allowedOrigins: "https://docs.spring.io"
            allowedMethods:
            - GET

上面的例子中,所有原始為docs.spring.io的GET請求均被允許跨域請求。

4.  示例

 

本例中又4個項目,如下圖:

4.1.  cjs-eureka-server

pom.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 4     <modelVersion>4.0.0</modelVersion>
 5     <parent>
 6         <groupId>org.springframework.boot</groupId>
 7         <artifactId>spring-boot-starter-parent</artifactId>
 8         <version>2.1.6.RELEASE</version>
 9         <relativePath/> <!-- lookup parent from repository -->
10     </parent>
11     <groupId>com.cjs.example</groupId>
12     <artifactId>cjs-eureka-server</artifactId>
13     <version>0.0.1-SNAPSHOT</version>
14     <name>cjs-eureka-server</name>
15 
16     <properties>
17         <java.version>1.8</java.version>
18         <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
19     </properties>
20 
21     <dependencies>
22         <dependency>
23             <groupId>org.springframework.cloud</groupId>
24             <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
25         </dependency>
26         <dependency>
27             <groupId>ch.qos.logback</groupId>
28             <artifactId>logback-classic</artifactId>
29             <version>1.2.3</version>
30         </dependency>
31     </dependencies>
32 
33     <dependencyManagement>
34         <dependencies>
35             <dependency>
36                 <groupId>org.springframework.cloud</groupId>
37                 <artifactId>spring-cloud-dependencies</artifactId>
38                 <version>${spring-cloud.version}</version>
39                 <type>pom</type>
40                 <scope>import</scope>
41             </dependency>
42         </dependencies>
43     </dependencyManagement>
44 
45     <build>
46         <plugins>
47             <plugin>
48                 <groupId>org.springframework.boot</groupId>
49                 <artifactId>spring-boot-maven-plugin</artifactId>
50             </plugin>
51         </plugins>
52     </build>
53 
54 </project>

application.yml

 1 server:
 2   port: 8761
 3 
 4 spring:
 5   application:
 6     name: cjs-eureka-server
 7 
 8 eureka:
 9   client:
10     service-url:
11       defaultZone: http://10.0.29.92:8761/eureka/,http://10.0.29.232:8761/eureka/
12 
13 logging:
14   file: ${spring.application.name}.log

Application.java

 1 package com.cjs.example;
 2 
 3 import org.springframework.boot.SpringApplication;
 4 import org.springframework.boot.autoconfigure.SpringBootApplication;
 5 import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
 6 
 7 /**
 8  * @author chengjiansheng
 9  * @date 2019-06-26
10  */
11 @EnableEurekaServer
12 @SpringBootApplication
13 public class CjsEurekaServerApplication {
14 
15     public static void main(String[] args) {
16         SpringApplication.run(CjsEurekaServerApplication.class, args);
17     }
18 
19 } 

4.2.  cjs-gateway-server

pom.xml

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

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • [2019.07.04 學習筆記2] 1.行級元素(inline element),與其他元素位於同一行。 1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>行內元素</title> 6 < ...
  • Webpack已經流行好久了,但很多同學使用webpack時還是一頭霧水,一下看到那麼多文檔、各種配置、各種loader、plugin立馬就暈頭轉向了。我也不例外,以至於很長一段時間對webpack都是一知半解的狀態。但是想要繼續做好前端,webpack是必須得跨過的一道坎,其實掌握webpack並... ...
  • 如果第二次看到我的文章,歡迎右側掃碼訂閱我喲~ 👉 每周五早8點 按時送達。當然了,也會時不時加個餐~ 在一個分散式系統的開發團隊中,有一些問題是很容易產生程式員之間矛盾的。 其中之一就是「業務歸屬」,就是當新加/修改一個業務的時候,代碼變更應該放到你負責的系統還是我負責的系統里? 一些業務輪廓很 ...
  • SpringCloud系列教程 | 第五篇:熔斷監控Hystrix Dashboard和Turbine Springboot: 2.1.6.RELEASE SpringCloud: Greenwich.SR1 如無特殊說明,本系列教程全採用以上版本 Hystrix dashboard是一款針對Hys ...
  • 郵箱核心業務場景: 發郵件 收郵件 查看郵件 郵箱業務我們關註的核心信息 草稿箱 收件箱 已發送郵件 未讀郵件 重要郵件 垃圾郵件 已刪除郵件 核心領域模型文字版 共三個模型,如下: 草稿郵件(DraftMail,聚合根): ID 標題 內容 所屬Owner郵箱地址 創建時間 支持場景:創建郵件(但 ...
  • 一個類如何表示 1. 第一格為類名 2. 第二格為類中欄位屬性 格式: 許可權:private、public 、protected、default,它們分別對應 、+、 、~ 3. 第三格為類的方法 格式: 返回類型可選 類之間的關係 多看幾次上圖,對比如下簡短說明,再結合實踐,相信你很快就可以搞清楚 ...
  • Web Service技術在我第一次接觸,又沒有實際使用時完全不理解這是什麼。以為是一種類似Spring,Shiro的編程框架。後來漸漸理解,WS(即Web Service縮寫)是一種通用的介面規範,並按照該規範編寫介面對外提供服務。 ...
  • 一、簡介 在使用mybatis時我們需要重覆的去創建pojo類、mapper文件以及dao類並且需要配置它們之間的依賴關係,比較麻煩且做了大量的重覆工作,mybatis官方也發現了這個問題, 因此給我們提供了mybatis generator工具來幫我們自動創建pojo類、mapper文件以及dao ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...