【开源项目】分库分表实现短链接生成系统

项目地址

https://github.com/AmosWang0626/short-url

源码解析

短链接表结构

@Getter
@Setter
@Entity
@Table(name = "dev_short_url", indexes = {
        @Index(columnList = "url"),
        @Index(columnList = "fullUrl")
})
@Accessors(chain = true)
public class ShortUrlEntity extends BaseEntity {

    /**
     * 短链接
     */
    @Column(nullable = false)
    private String url;
    /**
     * 完整链接
     */
    @Column(nullable = false)
    private String fullUrl;
    /**
     * 过期时间-时间戳(-1表示永久有效)
     */
    @Column(nullable = false)
    private Long expireTime;

}

生成短链接

ShortUrlBusinessImpl#save,判断短链是否存在,补充数据的完整性,生成短链接。

    @Override
    public ShortUrlVO save(ShortUrlForm form) {
        ShortUrlEntity entity = new ShortUrlEntity();
        entity.setFullUrl(form.getFullUrl());
        entity.setExpireTime(-1L);

        // 校验 full_url, 存在就直接返回
        Optional<ShortUrlEntity> byFullUrl = shortUrlService.findByFullUrl(entity.getFullUrl());
        if (byFullUrl.isPresent()) {
            return parseVO(byFullUrl.get());
        }

        // 设置默认过期时间
        if (form.getExpire() == null || form.getTimeUnit() == null) {
            form.setExpire(-1);
        }

        // 设置过期时间(时间戳)
        if (form.getExpire() != -1) {
            LocalDateTime expireDateTime = form.getTimeUnit().setTime(LocalDateTime.now(), form.getExpire());
            entity.setExpireTime(expireDateTime.toEpochSecond(ZoneOffset.UTC));
        }

        entity.setUrl(UniqueShortUrl.getShortUrl(entity.getFullUrl()));
        shortUrlService.save(entity);

        return parseVO(entity);
    }

ShortUrlServiceImpl#save,将有过期时间的数据存放到Redis中。

    @Override
    public void save(ShortUrlEntity entity) {
        shortUrlDao.save(entity);

        // 如果过期时间不为-1,则将其置入Redis
        if (entity.getExpireTime() != -1) {
            expireService.addExpireInfo(entity.getId(), entity.getExpireTime());
        }
    }

获取短链接

ShortUrlBusinessImpl#find

    @Override
    @Cacheable(value = "short:url", cacheManager = "caffeine", key = "'short_url_' + #key")
    public String find(String key) {

        return shortUrlService.find(key);
    }

清除过期数据

ExpireServiceImpl#initExpireInfoJob,按照时间查询过期的短链接,修改过期状态;即将过期的数据存放在Redis中。

    @Override
    public void initExpireInfoJob() {
        List<ShortUrlEntity> deleteEntities = new ArrayList<>();
        List<ShortUrlEntity> needInitExpireInfoEntities = new ArrayList<>();

        Optional<List<ShortUrlEntity>> byExpireTime = shortUrlDao.findAllByExpireTime();
        byExpireTime.ifPresent(shortUrlEntities -> shortUrlEntities.forEach(shortUrlEntity -> {
            if (shortUrlEntity.getExpireTime() <= System.currentTimeMillis()) {
                shortUrlEntity.setDeleteFlag(true);
                deleteEntities.add(shortUrlEntity);
            } else {
                needInitExpireInfoEntities.add(shortUrlEntity);
            }
        }));

        // 删除过期数据
        shortUrlDao.saveAll(deleteEntities);

        // 过期信息保存至 Redis 
        needInitExpireInfoEntities.forEach(entity -> redisTemplate.opsForZSet().add(SHORT_URL_EXPIRE, entity.getId(), entity.getExpireTime()));
    }

数据统计

MonitorCaffeineController#caffeine,使用caffeine做统计数据。

    @GetMapping("stats")
    @ApiOperation("根据缓存key查询缓存监控信息")
    public SingleResponse<Map<String, Object>> caffeine(@RequestParam String cacheName) {
        CaffeineCache caffeineCache = (CaffeineCache) caffeine.getCache(cacheName);
        if (caffeineCache == null) {
            return SingleResponse.buildFailure("", String.format("缓存 [%s] 不存在 !!!", cacheName));
        }

        CacheStats stats = caffeineCache.getNativeCache().stats();

        Map<String, Object> map = new HashMap<>(16);
        map.put("请求次数", stats.requestCount());
        map.put("命中次数", stats.hitCount());
        map.put("未命中次数", stats.missCount());
        map.put("加载成功次数", stats.loadSuccessCount());
        map.put("加载失败次数", stats.loadFailureCount());
        map.put("加载失败占比", stats.loadFailureRate());
        map.put("加载总耗时", stats.totalLoadTime());
        map.put("回收总次数", stats.evictionCount());
        map.put("回收总权重", stats.evictionWeight());

        return SingleResponse.of(map);
    }

分库分表

主要是配置信息和JPA,不作详细研究。分库分表后面单独写一篇原理分析。该开源项目使用的是最新的版本。

        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
            <version>5.0.0-alpha</version>
        </dependency>

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值