最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

gateway Redisson接口級別限流問題及解決過程

 更新時間:2025年11月13日 08:44:01   作者:Mr-Wanter  
Redisson提供了基于令牌桶算法的限流器技術(shù),適用于API網(wǎng)關(guān)、分布式系統(tǒng)和微服務(wù)架構(gòu),該技術(shù)可以對系統(tǒng)進(jìn)行流量控制,保護(hù)重要接口,滿足個性化限流需求

前言

在當(dāng)今互聯(lián)網(wǎng)時代,面對快速增長的用戶流量和不斷擴(kuò)大的系統(tǒng)規(guī)模,合理的限流策略變得愈發(fā)重要。針對這一問題,Redisson作為一款高性能的分布式應(yīng)用開發(fā)框架,在限流方面擁有出色的技術(shù)。

本文將深入探討Redisson的限流器技術(shù),為讀者詳細(xì)介紹其原理和應(yīng)用。

在軟件架構(gòu)中,限流器(Rate Limiter)是一種用于控制資源利用、維持服務(wù)質(zhì)量和防止系統(tǒng)過載的重要組件。限流器可以按照不同的策略和算法來實現(xiàn),主要包括以下幾種類型:

1. 計數(shù)器算法(固定窗口限流器)

原理:在一個固定的時間窗口內(nèi)計算請求的數(shù)量,如果請求超過了預(yù)設(shè)的最大閾值,則拒絕后續(xù)的請求,直到下一個時間窗口開始。

優(yōu)點:簡單易理解和實現(xiàn)。

缺點:在窗口切換的時刻可能會發(fā)生兩倍于閾值的請求量,即“窗口邊緣效應(yīng)”。

2. 滑動窗口日志限流器

原理:改進(jìn)了固定窗口算法的邊緣效應(yīng)。它記錄了每個請求的時間戳,將時間窗口分成多個小窗口,滑動時只統(tǒng)計當(dāng)前時間窗口內(nèi)的請求量。

優(yōu)點:比固定窗口算法更平滑,減少了窗口切換時的峰值。

缺點:需要記錄更多的請求信息,可能會增加系統(tǒng)開銷。

3. 漏桶算法(Leaky Bucket)

原理:所有的請求都被放入到一個固定容量的桶里,請求按照固定的速率從桶中“漏出”(被處理)。如果桶滿了,則會丟棄進(jìn)來的請求。

優(yōu)點:輸出流量比較平滑,即使短時間內(nèi)有大量請求到達(dá),輸出速率也是恒定的。

缺點:不具備突發(fā)流量的處理能力,對于突發(fā)請求仍然可能導(dǎo)致請求被丟棄。

4. 令牌桶算法(Token Bucket)

原理:令牌以固定速率被添加到桶中,每個請求必須消耗一個令牌才能被處理。如果桶中沒有令牌,則請求被排隊或丟棄,直到有令牌可用。

優(yōu)點:允許一定程度的突發(fā)流量,因為桶中可以累積一些令牌。

缺點:如果突發(fā)流量持續(xù)時間較長,超過了桶的容量,那么超出的請求仍然會被丟棄或延遲處理。

5. 限流隊列

原理:請求首先進(jìn)入一個隊列,隊列根據(jù)系統(tǒng)的處理能力和預(yù)設(shè)的規(guī)則控制請求的進(jìn)入和退出。

優(yōu)點:可以根據(jù)系統(tǒng)負(fù)載動態(tài)調(diào)整請求的進(jìn)出,對系統(tǒng)影響較小。

缺點:實現(xiàn)相對復(fù)雜,需要精確控制隊列的大小和請求的處理速率。

應(yīng)用場景

  • API網(wǎng)關(guān):在API網(wǎng)關(guān)層面進(jìn)行限流,可以防止過多的請求直接壓垮后端服務(wù)。
  • 分布式系統(tǒng):在分布式服務(wù)之間進(jìn)行限流,可以協(xié)調(diào)不同服務(wù)的負(fù)載,防止某些服務(wù)成為瓶頸。
  • 微服務(wù)架構(gòu):對單個微服務(wù)的接口進(jìn)行限流,保證服務(wù)的穩(wěn)定性。

實現(xiàn)工具

  • Guava RateLimiter:基于令牌桶算法,是Java中一個廣泛使用的限流實現(xiàn)。
  • Nginx:使用漏桶算法進(jìn)行HTTP請求的限流。
  • Redis:可以使用Redis的原子性操作和腳本來實現(xiàn)復(fù)雜的限流策略。

選擇合適的限流策略應(yīng)當(dāng)基于系統(tǒng)的具體需求,比如是否需要應(yīng)對突發(fā)流量、系統(tǒng)的可用性要求、實現(xiàn)的復(fù)雜度等因素。在實際應(yīng)用中,很可能會結(jié)合多種策略來實現(xiàn)更加精細(xì)化的流量控制。

一、Redisson簡介

Redisson是一個基于Redis的Java駐留內(nèi)存(In-Memory)數(shù)據(jù)網(wǎng)格中間件,提供了豐富的分布式對象和服務(wù),可用于開發(fā)高性能、可擴(kuò)展的分布式應(yīng)用。其中,Redisson的限流器技術(shù)是其重要的功能之一。

二、Redisson限流器的原理

Redisson限流器采用了一種基于令牌桶算法的策略。令牌桶算法通過維護(hù)一個固定容量的令牌桶,在每個時間段內(nèi)產(chǎn)生若干個令牌,請求在獲取令牌后才能進(jìn)行處理。這種算法可以有效控制流量,并在一定程度上保護(hù)系統(tǒng)免受突然的高并發(fā)壓力。

三、Redisson限流器技術(shù)的應(yīng)用

  1. 系統(tǒng)流控Redisson的限流器技術(shù)可以用于對系統(tǒng)進(jìn)行流量控制,確保系統(tǒng)在高負(fù)載情況下依然能夠提供穩(wěn)定可靠的服務(wù)。通過限制請求的速率和并發(fā)數(shù),可以避免系統(tǒng)因為過載而崩潰或變得不可用。

  2. 接口保護(hù)對于一些重要的接口或某些敏感資源,我們可能希望限制用戶的訪問頻率,以避免惡意的使用或攻擊。Redisson的限流器技術(shù)可以很好地實現(xiàn)這一目的,通過限制請求的速率和次數(shù),有效保護(hù)接口的安全性和可用性。

  3. 基于用戶的限流對于一些特定用戶或特定用戶組,我們可能需要設(shè)置不同的限流策略。Redisson提供了基于用戶的限流管理能力,可以根據(jù)用戶身份信息或其他特征進(jìn)行流量控制,滿足個性化的限流需求。

四、gateway自定義接口限流實現(xiàn)

1. maven

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
        </dependency>

        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.16.1</version>
        </dependency>

2. 接口限流數(shù)據(jù)結(jié)構(gòu)定義RateLimitProperties

gateway.yaml

gateway:
  rate-limit:
    configs:
      - name: 服務(wù)1
        option:
          - uri: 接口1/*
            matchType: 1
            size: 200
            microSecond: 1000
      - name: 服務(wù)2
        option:
          - uri: 接口1/*
            matchType: 1
            size: 1
            second: 1
          - uri: 接口2
            matchType: 0
            size: 1
            microSecond: 1000
      - name: 服務(wù)3
        option:
          - uri: 接口1
            matchType: 0
            size: 10
            microSecond: 1000
            consumer: 1
            waitTimer: 1000

限流配置類 RateLimitProperties

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/**
 * 限流配置獲取,支持熱更新
 */

@Data
@Component
@JsonIgnoreProperties(ignoreUnknown = true)
@ConfigurationProperties(prefix = "gateway.rate-limit")
public class RateLimitProperties {

    private List<Configs> configs;

    @Data
    @Component
    @ConfigurationProperties(prefix = "gateway.rate-limit.configs")
    public static class Configs {
        private String name;
        private List<Option> option = new ArrayList<>();

        @Data
        @Component
        @JsonIgnoreProperties(ignoreUnknown = true)
        @ConfigurationProperties(prefix = "gateway.rate-limit.configs.option")
        public static class Option {
            private String uri; //api uri
            private int matchType; // 匹配類型 0 完整匹配 1 模糊匹配
            private int size; //令牌桶 大小
            private int microSecond = 1000; //生成令牌的周期
            private int consumer = 1; //一個請求消耗令牌數(shù)量
            private int waitTimer = 0; // 令牌沒有時,等待最多xx ms后拿令牌
        }
    }
}

3. RedissonClient 注入

RedisConfig

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.TransportMode;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    @Value("${spring.redis.host}")
    private String redisAddress;
    @Value("${spring.redis.port}")
    private String redisPort;
    @Value("${spring.redis.password}")
    private String redisPas;
    @Value("${spring.redis.database:1}")
    private Integer redisDb;

    @Bean
    public RedissonClient redissonClient(RedisProperties redisProperties) {
        Config config = new Config();
        config.setTransportMode(TransportMode.NIO);
        config.useSingleServer().setAddress("redis://" + redisAddress + ":" + redisPort)
                .setPassword(redisPas)
                .setDatabase(redisDb);
        return Redisson.create(config);
    }

    public static void redisSerialize(RedisTemplate redisTemplate) {
        // 2.設(shè)置統(tǒng)一序列化規(guī)則
        RedisSerializer stringRedisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);

        //Json序列化配置
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = serializer();
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setEnableTransactionSupport(false);
    }

    private static Jackson2JsonRedisSerializer serializer() {
        //Json序列化配置
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.WRAPPER_ARRAY);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        return jackson2JsonRedisSerializer;
    }
}

4. redis 初始化寫入限流配置

ApiRateLimitInit

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;

import javax.annotation.Resource;
import java.util.List;

@Configuration
@Slf4j
public class ApiRateLimitInit {

    @Resource
    private RateLimitProperties properties;

    @Resource
    private RedisTemplate<String, RateLimitProperties.Configs> redisTemplate;

    @Resource
    private RedissonClient redissonClient;

    @Bean
    public void initApis() {
        List<RateLimitProperties.Configs> configs = properties.getConfigs();
        //將最新的配置放到redis中,在做限流判斷的代碼中,直接從redis中讀取最新的配置
        redisLimitConfig(configs);
    }

    public void redisLimitConfig(List<RateLimitProperties.Configs> configs) {
        RedisConfig.redisSerialize(redisTemplate);
        redisTemplate.delete("ApiRateLimitFilter");
        RKeys keys = redissonClient.getKeys();
        keys.deleteByPattern("*buslimiters*");
        if (ObjectUtil.isNotNull(configs)) {
            redisTemplate.opsForList().leftPushAll("ApiRateLimitFilter", configs);
            //重新初始化所有l(wèi)imiter
            for (RateLimitProperties.Configs temp : configs) {
                temp.getOption().forEach(o -> {
                    RRateLimiter limiter = redissonClient.getRateLimiter(StrUtil.format("buslimiters:{}:{}", temp.getName(), o.getUri()));
                    limiter.trySetRate(RateType.OVERALL, o.getSize(), o.getMicroSecond(), RateIntervalUnit.MILLISECONDS);
                });
            }
        }
    }
}

5. 接口過濾限流規(guī)則

ApiRateLimitFilter

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RedissonClient;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class ApiRateLimitFilter implements GlobalFilter, Ordered {
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private RedisTemplate<String, RateLimitProperties.Configs> redisTemplate;

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

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String uri = exchange.getRequest().getURI().getPath().substring(1);
        int index = uri.indexOf("/");
        String microServiceName = StringUtils.substringBefore(uri, "/");
        String apiPath = uri.substring(index);
        List<RateLimitProperties.Configs> configs = redisTemplate.opsForList().range("ApiRateLimitFilter", 0, -1);
        List<RateLimitProperties.Configs.Option> option = configs.stream()
                .filter(s -> microServiceName.equals(s.getName()))
                .findFirst().orElse(new RateLimitProperties.Configs()).getOption();
        RateLimitProperties.Configs.Option thisOption = WebUtils.matchesRateLimit(apiPath, option);
        //不在限流配置中
        if (thisOption == null) {
            return chain.filter(exchange);
        }
        RRateLimiter limiter = redissonClient.getRateLimiter(StrUtil.format("buslimiters:{}:{}", microServiceName, thisOption.getUri()));
        boolean acquire = limiter.tryAcquire(thisOption.getConsumer(), thisOption.getWaitTimer(), TimeUnit.MICROSECONDS);
        if (acquire) {
            return chain.filter(exchange);
        }
        log.info(uri + "限流");
        //exchange 信息放入kafka中,等待消費
        return ResponseUtils.success(exchange.getResponse(), CommonConstant.ErrorResp.BUSY_ERROR);
    }
}

6. nacos配置熱更新redis配置

RateLimitNacosListener

import cn.hutool.json.JSONUtil;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.cloud.nacos.NacosConfigProperties;
import com.alibaba.nacos.api.config.listener.Listener;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.Executor;

@Slf4j
@Configuration
public class RateLimitNacosListener implements InitializingBean {

    @Value("${spring.application.name}")
    private String appName;
    @Resource
    private NacosConfigManager nacosConfigManager;
    @Resource
    private NacosConfigProperties configProperties;
    @Resource
    private ApiRateLimitInit rateLimitInit;

    @Override
    public void afterPropertiesSet() throws Exception {
        nacosConfigManager.getConfigService().addListener(appName + ".yaml", configProperties.getGroup(),
                new Listener() {
                    @Override
                    public Executor getExecutor() {
                        return null;
                    }

                    @Override
                    public void receiveConfigInfo(String configInfo) {
                        YAMLMapper yamlMapper = new YAMLMapper();
                        try {
                            JsonNode jsonNode = yamlMapper.readTree(configInfo);
                            String configStr = jsonNode.get("gateway").get("rate-limit").get("configs").toPrettyString();
                            List<RateLimitProperties.Configs> configs = JSONUtil.toList(configStr, RateLimitProperties.Configs.class);
                            //將最新的配置放到redis中,在做限流判斷的代碼中,直接從redis中讀取最新的配置
                            rateLimitInit.redisLimitConfig(configs);
                            log.info("config更新:" + configStr);
                        } catch (JsonProcessingException e) {
                            throw new RuntimeException(e);
                        }
                    }
                });
    }
}

7. 接口匹配工具類

WebUtils

/**
 * WebUtils 工具類
 *
 */
public class WebUtils {

    /**
     * 獲取限流接口對象
     */
    public static RateLimitProperties.Configs.Option matchesRateLimit(String uri, List<RateLimitProperties.Configs.Option> all) {
        PathMatcher matcher = new AntPathMatcher();
        List<RateLimitProperties.Configs.Option> allMatch = all.stream().filter(o -> !o.getUri().endsWith("*")).collect(Collectors.toList());
        for (RateLimitProperties.Configs.Option option : allMatch) {
            if (StringUtils.equals(option.getUri(), uri)) {
                return option;
            }
        }
        List<RateLimitProperties.Configs.Option> fuzzyMatch = all.stream().filter(o -> o.getUri().endsWith("*")).collect(Collectors.toList());
        for (RateLimitProperties.Configs.Option option : fuzzyMatch) {
            if (matcher.match(option.getUri(), uri)) {
                return option;
            }
        }
        return null;
    }
}

ResponseUtils

public class ResponseUtils {

    public static Mono<Void> error(ServerHttpResponse response, Result result) {
        //返回錯誤
        response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE);
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        DataBuffer buffer = response.bufferFactory().wrap(JSONUtil.toJsonStr(result).getBytes(StandardCharsets.UTF_8));
        return response.writeWith(Mono.just(buffer));
    }

    public static Mono<Void> success(ServerHttpResponse response, Result result) {
        //返回200
        response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE);
        response.setStatusCode(HttpStatus.OK);
        DataBuffer buffer = response.bufferFactory().wrap(JSONUtil.toJsonStr(result).getBytes(StandardCharsets.UTF_8));
        return response.writeWith(Mono.just(buffer));
    }

}

總結(jié)

其他方案嘗試:

gateway自帶限流器:令牌桶算法實現(xiàn),支持自定義key-resolver,即限流規(guī)則(用戶、ip、請求地址等),但是只支持服務(wù)級別限流,無法限制到具體接口

Redisson的限流器技術(shù)是一項用于控制系統(tǒng)流量并保護(hù)重要接口的強(qiáng)大工具。它通過令牌桶算法,提供了靈活的流控策略和個性化的限流管理,幫助開發(fā)者有效應(yīng)對高負(fù)載和突發(fā)流量的挑戰(zhàn)。在實際應(yīng)用中,合理利用Redisson的限流器技術(shù),可以提高系統(tǒng)的穩(wěn)定性、安全性和可用性,為用戶提供更好的體驗。

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • spring boot 3.3.0和mybatis plus 3.5.6版本沖突的問題解決

    spring boot 3.3.0和mybatis plus 3.5.6版本沖突

    這篇文章主要介紹了spring boot 3.3.0和mybatis plus 3.5.6版本沖突的問題解決,文中介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-07-07
  • java開發(fā)中遇到的異常匯總詳解

    java開發(fā)中遇到的異常匯總詳解

    這篇文章主要介紹了java開發(fā)中遇到的異常匯總詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-07-07
  • 基于Idea+Jconsole實現(xiàn)線程監(jiān)控步驟

    基于Idea+Jconsole實現(xiàn)線程監(jiān)控步驟

    這篇文章主要介紹了基于Idea+Jconsole實現(xiàn)線程監(jiān)控功能,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-04-04
  • java集合迭代器Iterator中的remove陷阱

    java集合迭代器Iterator中的remove陷阱

    這篇文章主要介紹了java集合迭代器Iterator中的remove陷阱,需要的朋友可以參考下
    2016-04-04
  • Java多線程之火車售票系統(tǒng)

    Java多線程之火車售票系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了Java多線程之火車售票系統(tǒng),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-07-07
  • Mybatis返回類型為Map時遇到的類型轉(zhuǎn)化的異常問題

    Mybatis返回類型為Map時遇到的類型轉(zhuǎn)化的異常問題

    這篇文章主要介紹了Mybatis返回類型為Map時遇到的類型轉(zhuǎn)化的異常問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • java將excel轉(zhuǎn)為pdf的方法步驟

    java將excel轉(zhuǎn)為pdf的方法步驟

    之前工作需要,查了挺多種Excel轉(zhuǎn)PDF的方法,下面這篇文章主要給大家介紹了關(guān)于java將excel轉(zhuǎn)為pdf的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-01-01
  • Java如何實現(xiàn)實體類轉(zhuǎn)Map、Map轉(zhuǎn)實體類

    Java如何實現(xiàn)實體類轉(zhuǎn)Map、Map轉(zhuǎn)實體類

    這篇文章主要介紹了Java 實現(xiàn)實體類轉(zhuǎn)Map、Map轉(zhuǎn)實體類的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • java實現(xiàn)音頻文件播放功能

    java實現(xiàn)音頻文件播放功能

    這篇文章主要為大家詳細(xì)介紹了java實現(xiàn)音頻文件播放功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-12-12
  • Maven腳手架如何基于jeecg實現(xiàn)快速開發(fā)

    Maven腳手架如何基于jeecg實現(xiàn)快速開發(fā)

    這篇文章主要介紹了Maven腳手架如何基于jeecg實現(xiàn)快速開發(fā),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-10-10

最新評論

丰原市| 新建县| 凉城县| 漠河县| 全南县| 瑞金市| 从江县| 阳曲县| 祁东县| 阳信县| 太湖县| 台山市| 邛崃市| 沁阳市| 民勤县| 化德县| 宁陕县| 夏邑县| 乌拉特中旗| 锦屏县| 海阳市| 东丽区| 紫金县| 杭锦后旗| 绩溪县| 肃宁县| 板桥市| 黔东| 晋州市| 定南县| 舟曲县| 伊金霍洛旗| 锦州市| 安化县| 尼木县| 桑植县| 鲁甸县| 饶河县| 汉川市| 新津县| 长白|