Nacos + Gateway網關搭建微服務

来源:https://www.cnblogs.com/Tom-shushu/archive/2022/08/06/16557185.html
-Advertisement-
Play Games

閑來無事,總結一下公司使用的 微服務框架,文章所有代碼GtiHub:https://github.com/Tom-shushu/work-study 裡面的gateway-server和server1項目 1、Docker 部署 Nacos 資料庫準備 新建 "nacos_config" 資料庫 # ...


閑來無事,總結一下公司使用的 微服務框架,文章所有代碼GtiHub:https://github.com/Tom-shushu/work-study 裡面的gateway-server和server1項目

1、Docker 部署 Nacos

資料庫準備

新建 "nacos_config" 資料庫

在https://github.com/alibaba/nacos/blob/develop/distribution/conf/nacos-mysql.sql 獲取最新的SQL腳本並執行(註意版本一定要對應),執行成功後如下圖所示:

2、安裝Nacos

a.下載鏡像

docker pull nacos/nacos-server

b.新建 application.properties文件(註意鑒權那個一定要加-不然會出現許可權繞過漏洞 CVE-2021-29441)前幾天的護網行動可把人忙壞了!

### 開啟鑒權
nacos.core.auth.enabled=true
### 關閉使用user-agent判斷服務端請求並放行鑒權的功能
nacos.core.auth.enable.userAgentAuthWhite=false
### 配置自定義身份識別的key(不可為空)和value(不可為空)
nacos.core.auth.server.identity.key=my-nacos
nacos.core.auth.server.identity.value=my-nacos

server.contextPath=/nacos
server.servlet.contextPath=/nacos
server.port=8848

spring.datasource.platform=mysql
db.num=1
## 修改為自己的資料庫連接和密碼
db.url.0=jdbc:mysql://xxxxxxxxxxx.mysql.rds.aliyuncs.com:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=xxxxxxx
db.password=xxxxxxx

nacos.cmdb.dumpTaskInterval=3600
nacos.cmdb.eventTaskInterval=10
nacos.cmdb.labelTaskInterval=300
nacos.cmdb.loadDataAtStart=false
management.metrics.export.elastic.enabled=false
management.metrics.export.influx.enabled=false
server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %{User-Agent}i
nacos.security.ignore.urls=/,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/v1/auth/login,/v1/console/health/**,/v1/cs/**,/v1/ns/**,/v1/cmdb/**,/actuator/**,/v1/console/server/**
nacos.naming.distro.taskDispatchThreadCount=1
nacos.naming.distro.taskDispatchPeriod=200
nacos.naming.distro.batchSyncKeyCount=1000
nacos.naming.distro.initDataRatio=0.9
nacos.naming.distro.syncRetryDelay=5000
nacos.naming.data.warmup=true
nacos.naming.expireInstance=true

c.直接啟動

docker  run --name nacos-server -p 8848:8848 --privileged=true --restart=always -e MODE=standalone -e PREFER_HOST_MODE=hostname -v /root/nacos/logs:/home/nacos/logs -v /root/application.properties:/home/nacos/conf/application.properties -d nacos/nacos-server

d.測試,註意打開8848安全組

瀏覽器輸入: IP:8848/nacos 預設賬號密碼: nacos nacos

3、Nginx 代理 介面功能變數名稱(沒有功能變數名稱的可以跳過)

功能變數名稱有ssl證書的修改nginx.conf文件,網關我們使用8000埠,現在一次配好

# HTTPS server
    server {
        listen       443 ssl;
        server_name  api.zhouhong.icu;

        charset utf-8;
        ssl_certificate   /usr/local/nginx/ssl/8247790_api.zhouhong.icu.pem;
        ssl_certificate_key /usr/local/nginx/ssl/8247790_api.zhouhong.icu.key;
        ssl_session_timeout 5m;
    	ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
    	ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    	ssl_prefer_server_ciphers on;  
    	## Nacos
        location /nacos {
             proxy_pass   http://127.0.0.1:8848;
        }
    	## 網關
    	location / {
                 proxy_pass   http://127.0.0.1:8000;
        }
    }

沒有ssl安全證書的可以如下配置,網關我們使用8000埠,現在一次配好

    server {
        listen       80;
        server_name  api.zhouhong.icu;

        ## Nacos
        location /nacos {
             proxy_pass   http://127.0.0.1:8848;
        }
        ## 網關
        location / {
             proxy_pass   http://127.0.0.1:8000;
        }
    }

測試

瀏覽器輸入: https://api.zhouhong.icu/nacos 或者 http://api.zhouhong.icu/nacos

4、網關服務搭建(代碼參考:https://github.com/Tom-shushu/work-study)

1.pom文件(只展示部分,詳細信息請到github看代碼~)

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>fastjson</artifactId>
                    <groupId>com.alibaba</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>fastjson</artifactId>
                    <groupId>com.alibaba</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

2.配置文件 application.yml

#請求和響應GZIP壓縮支持
feign:
  httpclient:
    enabled: false
  okhttp:
    enabled: true
  compression:
    request:
      enabled: true
      mime-types: text/xml,application/xml,application/json
      min-request-size: 2048
    response:
      enabled: true
      
spring:
  cloud:
    gateway:
      enabled: true
      discovery:
        locator:
          lowerCaseServiceId: true
          enabled: true
          ## 配置下麵這個預設會匹配以服務名為首碼的路由,就不需要在nacos裡面配置路由了
          filters:
            - name: RewritePath
              args:
                regexp: "'/' + serviceId + '/(?<remaining>.*)'"
                replacement: "'/' + serviceId + '/${remaining}'"
      default-filters:
        - DedupeResponseHeader=Access-Control-Allow-Origin
      globalcors:
        add-to-simple-url-handler-mapping: true
        corsConfigurations:
          '[/**]':
            allowed-origins: "*"
            allowed-methods: "*"
            allowed-headers: "*"
            allow-credentials: true
      httpclient:
        response-timeout: 60000
        connect-timeout: 60000
    loadbalancer:
      ribbon:
        enabled: false
  application:
    name: gateway
  sleuth:
    enabled: false
    http:
      legacy:
        enabled: true
  main:
    allow-bean-definition-overriding: true
  codec:
    max-in-memory-size: 20MB
management:
  endpoints:
    web:
      exposure:
        include: '*'
        exclude: heapdump,dump,threaddump,configprops,env
  security:
    enabled: false

gate:
  ignore:
    startWith: /auth/jwt,/auth/captcha

ribbon:
  eureka:
    enabled: true
  ReadTimeout: 60000
  ConnectTimeout: 60000
  MaxAutoRetries: 0
  MaxAutoRetriesNextServer: 1
  OkToRetryOnAllOperations: false

hystrix:
  threadpool:
    default:
      coreSize: 1000 ##併發執行的最大線程數,預設10
      maxQueueSize: 1000 ##BlockingQueue的最大隊列數
      queueSizeRejectionThreshold: 500 ##即使maxQueueSize沒有達到,達到queueSizeRejectionThreshold該值後,請求也會被拒絕
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 10000
            timerDelayInMilliseconds: 10000

3.bootstrap.yml 配置文件

#服務配置
server:
  port: 8000
  max-http-header-size: 10240

spring:
  application:
    name: gateway
  profiles:
    active: @spring.active@
  servlet:
    multipart:
      max-request-size: 100MB
      max-file-size: 100MB

logging:
  file:
    path: logs/
---
spring:
  profiles: local
  cloud:
    nacos:
      config:
        server-addr: https://api.zhouhong.icu
        namespace: 4d7c95f1-21d5-43ad-a420-5eebb2473751
        file-extension: yml
        username: nacos
        password: nacos
      discovery:
        server-addr: https://api.zhouhong.icu
        namespace: 4d7c95f1-21d5-43ad-a420-5eebb2473751
        username: nacos
        password: nacos
---
spring:
  profiles: dev
  cloud:
    nacos:
      config:
        server-addr: https://api.zhouhong.icu
        namespace: 07610e11-85ae-49cb-aa46-b8f0802543bd
        file-extension: yml
        username: nacos
        password: nacos
      discovery:
        server-addr: https://api.zhouhong.icu
        namespace: 07610e11-85ae-49cb-aa46-b8f0802543bd
        username: nacos
        password: nacos
---
spring:
  profiles: prod
  cloud:
    nacos:
      config:
        server-addr: https://api.zhouhong.icu
        namespace: 01940467-9f25-45e3-b9b8-1ad25c937544
        file-extension: yml
        extension-configs:
      discovery:
        server-addr: https://api.zhouhong.icu
        namespace: 01940467-9f25-45e3-b9b8-1ad25c937544

4.網關配置類GatewayConfig

@Configuration
public class GatewayConfig {
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @Bean
    public Decoder feignDecoder() {
        return new ResponseEntityDecoder(new SpringDecoder(feignHttpMessageConverter()));
    }

    public ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
        final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(new GateWayMappingJackson2HttpMessageConverter());
        return new ObjectFactory<HttpMessageConverters>() {
            @Override
            public HttpMessageConverters getObject() throws BeansException {
                return httpMessageConverters;
            }
        };
    }

    public class GateWayMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
        GateWayMappingJackson2HttpMessageConverter() {
            List<MediaType> mediaTypes = new ArrayList<>();
            mediaTypes.add(MediaType.valueOf(MediaType.TEXT_HTML_VALUE + ";charset=UTF-8"));
            setSupportedMediaTypes(mediaTypes);
        }
    }
    @Bean
    @LoadBalanced
    public WebClient.Builder loadBalancedWebClientBuilder() {

        return WebClient.builder().exchangeStrategies(ExchangeStrategies.builder().codecs(
                clientCodecConfigurer -> clientCodecConfigurer.defaultCodecs().maxInMemorySize(10 * 1024 * 1024)).build());
    }
}

5.過濾器

package com.zhouhong.gatewayserver.core.filter;
/**
 * 全局請求轉換
 **/
@Log4j2
@Component
public class RequestGlobalFilter implements GlobalFilter, Ordered {

    @Resource
    ServerCodecConfigurer codecConfigurer;
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        if (Objects.equals(exchange.getRequest().getMethod(), HttpMethod.POST)) {
            // 表單傳輸
            MediaType mediaType = exchange.getRequest().getHeaders().getContentType();
            if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(mediaType)) {
                return returnMono(chain, exchange);
            }

            ServerHttpRequest request = exchange.getRequest();
            String path = request.getURI().getPath();
            //放開不進行安全過濾的介面
            boolean checkSign = true;
            for (String notAuthResource : ReleaseConstant.NONE_SECURITY_URL_PATTERNS) {
                AntPathMatcher antPathMatcher = new AntPathMatcher();
                if (antPathMatcher.match(notAuthResource, path)) {
                    checkSign = false;
                }
            }
            ServerRequest serverRequest = ServerRequest.create(exchange, codecConfigurer.getReaders());
            Mono modifiedBody = serverRequest.bodyToMono(String.class).flatMap(body -> {
                //因為約定了終端傳參的格式,所以只考慮json的情況,如果是表單傳參,請自行發揮
                if (MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)) {
                    JSONObject jsonObject = JSON.parseObject(body);
                    String newBody = null;
                    try {
                        // 如果是1.0版本的請求(body經過base64轉碼),要將body解碼
                        if (jsonObject.containsKey("sign") || jsonObject.containsKey("object")) {
                            newBody = verifySignature(jsonObject.getString("object"));
                        } else {
                            newBody = body;
                        }
                    } catch (Exception e) {
                        return Mono.error(e);
                    }
                    log.info("請求:" + path + " " + body);
                    return Mono.just(newBody);
                }
                return Mono.just(body);
            });
            BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
            HttpHeaders headers = new HttpHeaders();
            headers.putAll(exchange.getRequest().getHeaders());
            headers.remove("Content-Length");
            // 請求中添加TOKEN用戶信息
            addTokenInfo(headers, serverRequest);
            CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
            Mono mono = bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {
                ServerHttpRequest decorator = this.decorate(exchange, headers, outputMessage);
                return returnMono(chain, exchange.mutate().request(decorator).build());
            }));
            return mono;
        } else {
            //GET 驗簽
            return returnMono(chain, exchange);
        }
    }

    @Override
    public int getOrder() {
        return 1;
    }

    private Mono<Void> returnMono(GatewayFilterChain chain, ServerWebExchange exchange) {
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
        }));
    }

    private String verifySignature(String paramStr) {
        return new String(Base64Utils.decodeFromString(paramStr));
    }


    private ServerHttpRequestDecorator decorate(ServerWebExchange exchange, HttpHeaders headers, CachedBodyOutputMessage outputMessage) {
        return new ServerHttpRequestDecorator(exchange.getRequest()) {
            @Override
            public HttpHeaders getHeaders() {
                long contentLength = headers.getContentLength();
                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.putAll(headers);
                if (contentLength > 0L) {
                    httpHeaders.setContentLength(contentLength);
                } else {
                    httpHeaders.set("Transfer-Encoding", "chunked");
                }
                return httpHeaders;
            }

            @Override
            public Flux<DataBuffer> getBody() {
                return outputMessage.getBody();
            }
        };
    }

    private void addTokenInfo(HttpHeaders headers, ServerRequest request) {
        return;
    }
}
其他代碼見github源碼~

4、普通業務服務(服務名稱:server1)

1.bootstrap.yml 配置文件

server:
  port: 8080
  max-http-header-size: 10240
spring:
  jackson:
    time-zone: GMT+8
  main:
    allow-bean-definition-overriding: true
  profiles:
    active: @spring.active@
  application:
    name: server1

logging:
  file:
    path: ./logs/

log:
  path: ./logs/

---
spring:
  profiles: local
  cloud:
    nacos:
      config:
        server-addr: https://api.zhouhong.icu
        namespace: 4d7c95f1-21d5-43ad-a420-5eebb2473751
        file-extension: yml
        username: nacos
        password: nacos
      discovery:
        server-addr: https://api.zhouhong.icu
        namespace: 4d7c95f1-21d5-43ad-a420-5eebb2473751
        username: nacos
        password: nacos
---
spring:
  profiles: dev
  cloud:
    nacos:
      config:
        server-addr: https://api.zhouhong.icu
        namespace: 07610e11-85ae-49cb-aa46-b8f0802543bd
        file-extension: yml
        username: nacos
        password: nacos
      discovery:
        server-addr: https://api.zhouhong.icu
        namespace: 07610e11-85ae-49cb-aa46-b8f0802543bd
        username: nacos
        password: nacos
---
spring:
  profiles: prod
  cloud:
    nacos:
      config:
        server-addr: https://api.zhouhong.icu
        namespace: 01940467-9f25-45e3-b9b8-1ad25c937544
        file-extension: yml
        extension-configs:
      discovery:
        server-addr: https://api.zhouhong.icu
        namespace: 01940467-9f25-45e3-b9b8-1ad25c937544

這裡只是為了測試簡單寫了兩個controller方法

/**
     * 服務名稱和介面第一段字元匹配
     * @return
     */
    @PostMapping("/server1/test")
    public String theSameServer() {
        return "你好呀( ⊙ o ⊙ )!";
    }

    /**
     * 服務名稱和介面第一段字元不匹配
     * @return
     */
    @PostMapping("/serverNotSame/test")
    public String gettoken() {
        return "你好呀( ⊙ o ⊙ )!";
    }

5、綜合測試

A.部署說明:

1.項目中我按照正常的公司開發形式,指定了 本地(local)、測試/開發(dev)、生產(prod)環境,搭建闊以按照自己的需求進行取捨;

2.本次測試我用測試環境進行演示、大家需要在nacos上建立與環境對應的命名空間(local、dev、prod),並且需要在網關和自己業務服務裡面指定對應的環境和nacos配置;

3.之前說過nacos開啟了用戶鑒權,所以需要在discovery 和 config 下麵配置登錄時的用戶名以及密碼

4.本項目實現的是根據服務名稱自動匹配介面首碼,至於根據nacos路由列表匹配大家可以自行百度。

B.部署

1.打包:需要選擇對應的環境,我這裡使用測試環境(dev)如圖所示:

2.需要打包網關和業務服務兩個服務,最終會得到 gateway.jar 和 server1.jar兩個jar包,然後上傳伺服器

3.部署(我這裡為了方便就不需要進行docker部署了)直接使用 java -jar XXX.jar 進行簡單部署了

部署成功後在nocos控制台查看服務是否註冊成功

image.png

如果服務列表為這個熊樣,就說明部署成功,接下來就可以測試啦

4.介面測試

使用PostMan進行介面調用
A 調用和服務名稱相匹配的那個介面 https://api.zhouhong.icu/server1/test

如圖所示調用成功(因為yml裡面那個配置,它會自動根據服務名稱 server1 進行匹配)
B 調用與服務名稱不匹配的那個介面

攔截成功

本文來自博客園,作者:Tom-shushu,轉載請註明原文鏈接:https://www.cnblogs.com/Tom-shushu/p/16557185.html


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

-Advertisement-
Play Games
更多相關文章
  • 在智慧工廠領域,智慧城市領域,都需要對設備進行監控。比如工廠需要對周圍環境溫度、濕度、氣壓、電壓,燈的開關進行監控。這時候就需要物聯網平臺來進行管理。 在智慧工廠領域,寶馬集團通過英偉達的Omniverse平臺在電腦中創建數字孿生工廠,併在數字孿生工廠中進行改變生產線配置、工人動線、倉儲管理等實驗 ...
  • 前言 在進行某些爬蟲任務的時候,我們經常會遇到僅用Http協議難以攻破的情況,比如協議中帶有加密參數,破解需要花費大量時間,那這時候就會用Selenium去模擬瀏覽器進行頁面上的元素抓取 大多數情況下我們用Selenium只是爬取一下頁面上可見的元素信息或者做一些模擬人工的操作,但頁面可見元素的數據 ...
  • 需求背景: 近日,在安裝某軟體過程,發現在安裝過程需要輸入一些信息才能繼續下一步操作,在機器數量較少情況下,我們可以單台登錄上去完成安裝操作,但當機器數量超過一定時,如果再手動登錄操作,就會產生大量重覆性操作,既不能帶來有效學習能力提升,同時也會極大產生不確定性,引發工作效率下降,那麼如何自動化完成 ...
  • getIRC-Best IRC Client for mac是一款實用的IRC客戶端,它由各種獨立的 IRC 伺服器網路(或“網路”)組成,這些機器允許用戶連接到 IRC。 詳情:getIRC-Best IRC Client for mac(IRC客戶端) 簡單介紹 macOS 上的 Interne ...
  • 有人相愛,有人夜裡開車看海,我是leetcode第一題都做不出來 題目 給定一個整數數組 nums 和一個整數目標值 target,請你在該數組中找出 和為目標值 target 的那 兩個 整數,並返回它們的數組下標。 你可以假設每種輸入只會對應一個答案。但是,數組中同一個元素在答案里不能重覆出現。 ...
  • Vue3 使用v-md-editor如何動態上傳圖片了 前端代碼: <v-md-editor :autofocus="true" v-model="blog.content" height="510px" placeholder="請輸入內容" left-toolbar="undo redo cle ...
  • 該案例主要是實現的功能有:添加商品功能,將商品添加到購物車的功能還有將商品刪除功能,還有就是移出購物車的功能 該案例實現的難點是將商品添加到購物車列表的時候 數量的增加,當購物車有該商品的時候就進行累加操作,沒有該商品就賦值為1. 上代碼: <!DOCTYPE html> <html lang="e ...
  • 樣式的衝突 當我們通過不同的選擇器,選中相同的元素,並且為相同的樣式設置不同的值時,此時就發生了樣式的衝突。 案例一:使用類選擇器與元素選擇器選中同一元素,設置不同顏色 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...