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

Java使用ShardingSphere實(shí)現(xiàn)數(shù)據(jù)庫(kù)分片的策略指南

 更新時(shí)間:2025年08月05日 09:16:45   作者:程序媛學(xué)姐  
隨著業(yè)務(wù)數(shù)據(jù)量的爆炸式增長(zhǎng),數(shù)據(jù)庫(kù)分片作為解決大數(shù)據(jù)量存儲(chǔ)和查詢(xún)性能問(wèn)題的核心技術(shù),已成為現(xiàn)代分布式系統(tǒng)架構(gòu)的重要組成部分,ShardingSphere作為一套開(kāi)源的分布式數(shù)據(jù)庫(kù)解決方案,提供了強(qiáng)大的數(shù)據(jù)分片功能,本文將深入探討ShardingSphere的核心分片策略

引言

隨著業(yè)務(wù)數(shù)據(jù)量的爆炸式增長(zhǎng),單一數(shù)據(jù)庫(kù)的性能瓶頸日益凸顯。數(shù)據(jù)庫(kù)分片作為解決大數(shù)據(jù)量存儲(chǔ)和查詢(xún)性能問(wèn)題的核心技術(shù),已成為現(xiàn)代分布式系統(tǒng)架構(gòu)的重要組成部分。Apache ShardingSphere作為一套開(kāi)源的分布式數(shù)據(jù)庫(kù)解決方案,提供了強(qiáng)大的數(shù)據(jù)分片功能,能夠有效解決傳統(tǒng)數(shù)據(jù)庫(kù)在海量數(shù)據(jù)處理場(chǎng)景下的性能局限性問(wèn)題。

ShardingSphere通過(guò)其獨(dú)特的分片算法和路由策略,實(shí)現(xiàn)了數(shù)據(jù)的水平切分和垂直切分,為企業(yè)級(jí)應(yīng)用提供了高性能、高可用的數(shù)據(jù)存儲(chǔ)解決方案。本文將深入探討ShardingSphere的核心分片策略,并通過(guò)實(shí)際代碼示例展示如何在Java項(xiàng)目中實(shí)現(xiàn)數(shù)據(jù)庫(kù)分片功能。

一、ShardingSphere核心架構(gòu)概述

1.1 架構(gòu)組件設(shè)計(jì)

ShardingSphere采用了微內(nèi)核加插件的架構(gòu)設(shè)計(jì)模式,核心組件包括解析引擎、路由引擎、改寫(xiě)引擎、執(zhí)行引擎和歸并引擎。解析引擎負(fù)責(zé)將SQL語(yǔ)句解析為抽象語(yǔ)法樹(shù),路由引擎根據(jù)分片規(guī)則確定SQL的執(zhí)行路徑,改寫(xiě)引擎對(duì)SQL進(jìn)行必要的改寫(xiě)操作,執(zhí)行引擎負(fù)責(zé)在多個(gè)數(shù)據(jù)源上并行執(zhí)行SQL,歸并引擎將多個(gè)數(shù)據(jù)源的結(jié)果進(jìn)行合并處理。

@Configuration
@EnableShardingJdbc
public class ShardingSphereConfig {
    
    /**
     * 配置數(shù)據(jù)源映射
     * 定義多個(gè)數(shù)據(jù)庫(kù)實(shí)例用于分片存儲(chǔ)
     */
    @Bean
    public Map<String, DataSource> dataSourceMap() {
        Map<String, DataSource> dataSourceMap = new HashMap<>();
        
        // 配置第一個(gè)分片數(shù)據(jù)源
        HikariDataSource dataSource1 = new HikariDataSource();
        dataSource1.setJdbcUrl("jdbc:mysql://localhost:3306/shard_db_1");
        dataSource1.setUsername("root");
        dataSource1.setPassword("password");
        dataSourceMap.put("shard_db_1", dataSource1);
        
        // 配置第二個(gè)分片數(shù)據(jù)源  
        HikariDataSource dataSource2 = new HikariDataSource();
        dataSource2.setJdbcUrl("jdbc:mysql://localhost:3306/shard_db_2");
        dataSource2.setUsername("root");
        dataSource2.setPassword("password");
        dataSourceMap.put("shard_db_2", dataSource2);
        
        return dataSourceMap;
    }
}

1.2 分片規(guī)則配置體系

分片規(guī)則是ShardingSphere實(shí)現(xiàn)數(shù)據(jù)分片的核心配置,包括數(shù)據(jù)庫(kù)分片規(guī)則和表分片規(guī)則。數(shù)據(jù)庫(kù)分片規(guī)則定義了數(shù)據(jù)在不同數(shù)據(jù)庫(kù)實(shí)例間的分布策略,表分片規(guī)則定義了數(shù)據(jù)在同一數(shù)據(jù)庫(kù)內(nèi)不同表間的分布策略。通過(guò)合理配置分片規(guī)則,可以實(shí)現(xiàn)數(shù)據(jù)的均勻分布和高效查詢(xún)。

/**
 * 分片規(guī)則配置類(lèi)
 * 定義數(shù)據(jù)庫(kù)和表的分片策略
 */
@Component
public class ShardingRuleConfiguration {
    
    /**
     * 配置表分片規(guī)則
     * 基于用戶(hù)ID進(jìn)行分片
     */
    public TableRuleConfiguration getUserTableRuleConfig() {
        TableRuleConfiguration config = new TableRuleConfiguration();
        config.setLogicTable("t_user");
        config.setActualDataNodes("shard_db_${0..1}.t_user_${0..1}");
        
        // 配置數(shù)據(jù)庫(kù)分片策略
        config.setDatabaseShardingStrategyConfig(
            new InlineShardingStrategyConfiguration("user_id", "shard_db_${user_id % 2}")
        );
        
        // 配置表分片策略
        config.setTableShardingStrategyConfig(
            new InlineShardingStrategyConfiguration("user_id", "t_user_${user_id % 2}")
        );
        
        return config;
    }
}

二、數(shù)據(jù)庫(kù)分片策略實(shí)現(xiàn)

2.1 水平分片算法

水平分片是將同一張表的數(shù)據(jù)按照某種規(guī)則分散到不同的數(shù)據(jù)庫(kù)或表中。ShardingSphere提供了多種內(nèi)置的分片算法,包括取模算法、范圍算法、哈希算法等。取模算法是最常用的分片策略,通過(guò)對(duì)分片鍵進(jìn)行取模運(yùn)算來(lái)確定數(shù)據(jù)的存儲(chǔ)位置,能夠保證數(shù)據(jù)的相對(duì)均勻分布。

/**
 * 自定義水平分片算法
 * 實(shí)現(xiàn)基于用戶(hù)ID的取模分片
 */
@Component
public class UserShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
    
    /**
     * 執(zhí)行精確分片計(jì)算
     * @param availableTargetNames 可用的目標(biāo)分片名稱(chēng)集合
     * @param shardingValue 分片值對(duì)象
     * @return 目標(biāo)分片名稱(chēng)
     */
    @Override
    public String doSharding(Collection<String> availableTargetNames, 
                           PreciseShardingValue<Long> shardingValue) {
        
        Long userId = shardingValue.getValue();
        // 基于用戶(hù)ID取模運(yùn)算確定分片
        int shardIndex = (int) (userId % availableTargetNames.size());
        
        String[] targetNames = availableTargetNames.toArray(new String[0]);
        String targetShardName = targetNames[shardIndex];
        
        // 記錄分片路由信息用于調(diào)試
        System.out.println("User ID: " + userId + " routed to: " + targetShardName);
        
        return targetShardName;
    }
}

2.2 范圍分片策略

范圍分片策略根據(jù)分片鍵的數(shù)值范圍來(lái)確定數(shù)據(jù)存儲(chǔ)位置,適用于具有明顯時(shí)間特征或數(shù)值范圍特征的業(yè)務(wù)場(chǎng)景。通過(guò)將連續(xù)的數(shù)據(jù)范圍映射到特定的分片上,可以提高范圍查詢(xún)的執(zhí)行效率,減少跨分片查詢(xún)的復(fù)雜度。

/**
 * 范圍分片算法實(shí)現(xiàn)
 * 根據(jù)訂單創(chuàng)建時(shí)間進(jìn)行分片
 */
@Component
public class OrderRangeShardingAlgorithm implements RangeShardingAlgorithm<Date> {
    
    /**
     * 執(zhí)行范圍分片計(jì)算
     * @param availableTargetNames 可用目標(biāo)分片集合
     * @param shardingValue 包含范圍條件的分片值
     * @return 匹配的分片名稱(chēng)集合
     */
    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames,
                                       RangeShardingValue<Date> shardingValue) {
        
        Collection<String> result = new ArrayList<>();
        Range<Date> valueRange = shardingValue.getValueRange();
        
        // 定義時(shí)間分片邊界(以月為單位)
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(valueRange.lowerEndpoint());
        int startMonth = calendar.get(Calendar.MONTH);
        
        calendar.setTime(valueRange.upperEndpoint());
        int endMonth = calendar.get(Calendar.MONTH);
        
        // 根據(jù)時(shí)間范圍確定涉及的分片
        for (String shardName : availableTargetNames) {
            int shardMonth = Integer.parseInt(shardName.substring(shardName.length() - 1));
            if (shardMonth >= startMonth && shardMonth <= endMonth) {
                result.add(shardName);
            }
        }
        
        return result;
    }
}

三、表分片策略與優(yōu)化

3.1 復(fù)合分片鍵策略

復(fù)合分片鍵策略允許使用多個(gè)字段作為分片依據(jù),通過(guò)組合多個(gè)業(yè)務(wù)字段的值來(lái)確定數(shù)據(jù)的最終存儲(chǔ)位置。這種策略在復(fù)雜業(yè)務(wù)場(chǎng)景中能夠提供更精細(xì)的數(shù)據(jù)分布控制,有效避免數(shù)據(jù)熱點(diǎn)問(wèn)題,實(shí)現(xiàn)更均勻的負(fù)載分布。

/**
 * 復(fù)合分片鍵算法實(shí)現(xiàn)
 * 基于用戶(hù)ID和訂單類(lèi)型進(jìn)行復(fù)合分片
 */
@Component
public class CompositeShardingAlgorithm implements ComplexKeysShardingAlgorithm<String> {
    
    /**
     * 執(zhí)行復(fù)合鍵分片計(jì)算
     * @param availableTargetNames 可用分片目標(biāo)集合
     * @param shardingValue 復(fù)合分片鍵值對(duì)象
     * @return 目標(biāo)分片名稱(chēng)集合
     */
    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames,
                                       ComplexKeysShardingValue<String> shardingValue) {
        
        Collection<String> result = new ArrayList<>();
        
        // 獲取用戶(hù)ID分片鍵值
        Collection<Long> userIds = shardingValue.getColumnNameAndShardingValuesMap()
            .get("user_id");
        
        // 獲取訂單類(lèi)型分片鍵值
        Collection<String> orderTypes = shardingValue.getColumnNameAndShardingValuesMap()
            .get("order_type");
        
        // 基于復(fù)合鍵計(jì)算分片位置
        for (Long userId : userIds) {
            for (String orderType : orderTypes) {
                // 組合計(jì)算哈希值確定分片
                int hashCode = (userId.toString() + orderType).hashCode();
                int shardIndex = Math.abs(hashCode) % availableTargetNames.size();
                
                String[] targets = availableTargetNames.toArray(new String[0]);
                result.add(targets[shardIndex]);
            }
        }
        
        return result;
    }
}

3.2 分片性能優(yōu)化

分片性能優(yōu)化涉及多個(gè)層面的技術(shù)考量,包括分片鍵的選擇、分片數(shù)量的規(guī)劃、查詢(xún)路由的優(yōu)化等。合理的分片鍵選擇應(yīng)該保證數(shù)據(jù)分布的均勻性和查詢(xún)的高效性,避免產(chǎn)生數(shù)據(jù)傾斜和熱點(diǎn)問(wèn)題。同時(shí),需要考慮分片擴(kuò)展的便利性和維護(hù)成本。

/**
 * 分片性能監(jiān)控和優(yōu)化配置
 * 提供分片執(zhí)行狀態(tài)的監(jiān)控能力
 */
@Component
public class ShardingPerformanceMonitor {
    
    private final Logger logger = LoggerFactory.getLogger(ShardingPerformanceMonitor.class);
    
    /**
     * 監(jiān)控分片查詢(xún)執(zhí)行情況
     * @param sql 執(zhí)行的SQL語(yǔ)句
     * @param shardingResults 分片執(zhí)行結(jié)果
     */
    public void monitorShardingExecution(String sql, Map<String, Object> shardingResults) {
        
        long startTime = System.currentTimeMillis();
        
        // 統(tǒng)計(jì)涉及的分片數(shù)量
        int shardCount = shardingResults.size();
        
        // 記錄執(zhí)行時(shí)間和分片分布
        shardingResults.forEach((shardName, result) -> {
            logger.info("Shard: {} executed SQL: {} with result count: {}", 
                       shardName, sql, getResultCount(result));
        });
        
        long executionTime = System.currentTimeMillis() - startTime;
        
        // 性能告警檢查
        if (executionTime > 1000) { // 超過(guò)1秒進(jìn)行告警
            logger.warn("Slow sharding query detected. SQL: {}, " +
                       "Execution time: {}ms, Shard count: {}", 
                       sql, executionTime, shardCount);
        }
        
        // 分片均勻性檢查
        checkShardingBalance(shardingResults);
    }
    
    /**
     * 檢查分片數(shù)據(jù)分布均勻性
     */
    private void checkShardingBalance(Map<String, Object> shardingResults) {
        List<Integer> counts = shardingResults.values().stream()
            .mapToInt(this::getResultCount)
            .boxed()
            .collect(Collectors.toList());
        
        int maxCount = Collections.max(counts);
        int minCount = Collections.min(counts);
        
        // 如果最大值和最小值差異過(guò)大,記錄告警
        if (maxCount > minCount * 2) {
            logger.warn("Unbalanced sharding detected. Max: {}, Min: {}", maxCount, minCount);
        }
    }
    
    private int getResultCount(Object result) {
        if (result instanceof Collection) {
            return ((Collection<?>) result).size();
        }
        return 1;
    }
}

四、實(shí)際應(yīng)用場(chǎng)景

電商訂單分片實(shí)踐

電商訂單系統(tǒng)是分片技術(shù)的典型應(yīng)用場(chǎng)景,訂單數(shù)據(jù)量龐大且訪問(wèn)頻繁。通過(guò)合理的分片策略設(shè)計(jì),可以有效提升系統(tǒng)的并發(fā)處理能力和查詢(xún)響應(yīng)速度。建議采用用戶(hù)維度和時(shí)間維度相結(jié)合的分片策略,既保證了用戶(hù)相關(guān)訂單的查詢(xún)效率,又便于歷史數(shù)據(jù)的歸檔管理。

/**
 * 電商訂單分片配置實(shí)現(xiàn)
 * 結(jié)合用戶(hù)ID和創(chuàng)建時(shí)間的分片策略
 */
@Configuration
public class ECommerceShardingConfig {
    
    /**
     * 配置訂單表分片規(guī)則
     * 基于用戶(hù)ID進(jìn)行數(shù)據(jù)庫(kù)分片,基于創(chuàng)建時(shí)間進(jìn)行表分片
     */
    @Bean
    public ShardingRuleConfiguration orderShardingRule() {
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        
        // 配置訂單表規(guī)則
        TableRuleConfiguration orderTableRule = new TableRuleConfiguration();
        orderTableRule.setLogicTable("t_order");
        orderTableRule.setActualDataNodes("order_db_${0..3}.t_order_${2023..2024}${01..12}");
        
        // 數(shù)據(jù)庫(kù)分片策略:基于用戶(hù)ID取模
        InlineShardingStrategyConfiguration dbShardingStrategy = 
            new InlineShardingStrategyConfiguration("user_id", "order_db_${user_id % 4}");
        orderTableRule.setDatabaseShardingStrategyConfig(dbShardingStrategy);
        
        // 表分片策略:基于創(chuàng)建時(shí)間按月分片
        StandardShardingStrategyConfiguration tableShardingStrategy = 
            new StandardShardingStrategyConfiguration("create_time", new OrderTimeShardingAlgorithm());
        orderTableRule.setTableShardingStrategyConfig(tableShardingStrategy);
        
        // 配置主鍵生成策略
        orderTableRule.setKeyGeneratorConfig(new KeyGeneratorConfiguration("SNOWFLAKE", "order_id"));
        
        shardingRuleConfig.getTableRuleConfigs().add(orderTableRule);
        return shardingRuleConfig;
    }
    
    /**
     * 訂單業(yè)務(wù)服務(wù)層實(shí)現(xiàn)
     * 展示分片環(huán)境下的業(yè)務(wù)操作
     */
    @Service
    public class OrderService {
        
        @Autowired
        private OrderMapper orderMapper;
        
        /**
         * 創(chuàng)建訂單(自動(dòng)路由到正確分片)
         */
        public void createOrder(Order order) {
            // ShardingSphere會(huì)自動(dòng)根據(jù)user_id和create_time路由到正確的分片
            orderMapper.insert(order);
        }
        
        /**
         * 查詢(xún)用戶(hù)訂單(單分片查詢(xún))
         */
        public List<Order> getUserOrders(Long userId) {
            // 查詢(xún)會(huì)自動(dòng)路由到用戶(hù)對(duì)應(yīng)的數(shù)據(jù)庫(kù)分片
            return orderMapper.selectByUserId(userId);
        }
        
        /**
         * 按時(shí)間范圍查詢(xún)訂單(可能涉及多個(gè)分片)
         */
        public List<Order> getOrdersByTimeRange(Date startTime, Date endTime) {
            // ShardingSphere會(huì)自動(dòng)識(shí)別涉及的時(shí)間分片并合并結(jié)果
            return orderMapper.selectByTimeRange(startTime, endTime);
        }
    }
}

總結(jié)

ShardingSphere作為成熟的分布式數(shù)據(jù)庫(kù)中間件,為Java應(yīng)用提供了完善的數(shù)據(jù)分片解決方案。通過(guò)其強(qiáng)大的分片算法和路由引擎,開(kāi)發(fā)者可以輕松實(shí)現(xiàn)數(shù)據(jù)的水平擴(kuò)展和性能優(yōu)化。在實(shí)際應(yīng)用中,需要根據(jù)具體的業(yè)務(wù)場(chǎng)景選擇合適的分片策略,合理設(shè)計(jì)分片鍵,并持續(xù)監(jiān)控分片性能。

成功的分片實(shí)施不僅要考慮當(dāng)前的數(shù)據(jù)規(guī)模和訪問(wèn)模式,還要為未來(lái)的業(yè)務(wù)增長(zhǎng)預(yù)留擴(kuò)展空間。通過(guò)合理的架構(gòu)設(shè)計(jì)和性能優(yōu)化,ShardingSphere能夠幫助企業(yè)構(gòu)建高性能、高可用的分布式數(shù)據(jù)存儲(chǔ)系統(tǒng),有效應(yīng)對(duì)海量數(shù)據(jù)處理的挑戰(zhàn)。在微服務(wù)架構(gòu)日益普及的今天,掌握ShardingSphere的分片技術(shù)已成為Java開(kāi)發(fā)者的重要技能之一。

以上就是Java使用ShardingSphere實(shí)現(xiàn)數(shù)據(jù)庫(kù)分片的策略指南的詳細(xì)內(nèi)容,更多關(guān)于Java ShardingSphere數(shù)據(jù)庫(kù)分片的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解Idea 2019.2 安裝lombok插件失效問(wèn)題解決

    詳解Idea 2019.2 安裝lombok插件失效問(wèn)題解決

    這篇文章主要介紹了詳解Idea 2019.2 安裝lombok插件失效問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • JavaSE中compare、compareTo的區(qū)別

    JavaSE中compare、compareTo的區(qū)別

    本文主要介紹了JavaSE中compare、compareTo的區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • Java Properties作為集合三個(gè)方法詳解

    Java Properties作為集合三個(gè)方法詳解

    Properties是JDK1.0中引入的java類(lèi),目前也在項(xiàng)目中大量使用,主要用來(lái)讀取外部的配置,那除了這個(gè),你對(duì)它其他的一些api也了解嗎? 你了解它是怎么實(shí)現(xiàn)的嗎? 如果不清楚的話,就通過(guò)本篇文章帶你一探究竟
    2022-11-11
  • IntelliJ IDEA 好用插件之a(chǎn)nalyze inspect code詳解

    IntelliJ IDEA 好用插件之a(chǎn)nalyze inspect code詳解

    這篇文章主要介紹了IntelliJ IDEA 好用插件之a(chǎn)nalyze inspect code的相關(guān)知識(shí),本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2020-12-12
  • idea maven 構(gòu)建本地jar包及pom文件的過(guò)程

    idea maven 構(gòu)建本地jar包及pom文件的過(guò)程

    這篇文章主要介紹了idea maven 構(gòu)建本地jar包及pom文件的過(guò)程,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2023-11-11
  • 詳解高性能緩存Caffeine原理及實(shí)戰(zhàn)

    詳解高性能緩存Caffeine原理及實(shí)戰(zhàn)

    Caffeine是基于Java 8開(kāi)發(fā)的,提供了近乎最佳命中率的高性能本地緩存組件,Spring5開(kāi)始不再支持Guava Cache,改為使用Caffeine。Caffeine提供的內(nèi)存緩存使用參考Google guava的API
    2021-06-06
  • 淺談Spring Cloud Ribbon的原理

    淺談Spring Cloud Ribbon的原理

    這篇文章主要介紹了淺談Spring Cloud Ribbon的原理,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-02-02
  • IE8+SpringMVC文件上傳防止JSON下載

    IE8+SpringMVC文件上傳防止JSON下載

    這篇文章主要介紹了IE8+SpringMVC文件上傳防止JSON下載的相關(guān)資料,需要的朋友可以參考下
    2017-07-07
  • Mybatisplus自動(dòng)填充實(shí)現(xiàn)方式及代碼示例

    Mybatisplus自動(dòng)填充實(shí)現(xiàn)方式及代碼示例

    這篇文章主要介紹了Mybatisplus自動(dòng)填充實(shí)現(xiàn)方式及代碼示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-11-11
  • HarmonyOS實(shí)現(xiàn)Java端類(lèi)似Nine-Patch氣泡聊天框代碼

    HarmonyOS實(shí)現(xiàn)Java端類(lèi)似Nine-Patch氣泡聊天框代碼

    在HarmonyOS Java端實(shí)現(xiàn)氣泡聊天框,與Android 上的9圖(Nine-Patch)有相似的實(shí)現(xiàn)方式,在HarmonyOS中,可以使用ShapeElement和ElementContainer來(lái)創(chuàng)建和管理可伸縮的氣泡背景,下面提供一個(gè)簡(jiǎn)單的示例代碼,可以在 HarmonyOS 中實(shí)現(xiàn)類(lèi)似于Android的Nine-Patch氣泡聊天框效果
    2024-07-07

最新評(píng)論

肃宁县| 华亭县| 昆明市| 通海县| 葫芦岛市| 二连浩特市| 高唐县| 乌什县| 兰考县| 修武县| 丰顺县| 囊谦县| 邳州市| 白沙| 盐城市| 安平县| 南通市| 彭阳县| 建宁县| 白城市| 侯马市| 崇文区| 台东市| 东乡族自治县| 长葛市| 尼玛县| 九江市| 襄垣县| 永泰县| 逊克县| 慈利县| 凉城县| 万荣县| 沙洋县| 宝山区| 铜鼓县| 右玉县| 衡阳县| 红原县| 黎平县| 武威市|