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
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...