java 将long或int转换为62进制的字符串,可用于短链生成

本文介绍了一种将自增ID转换为62进制字符串的短链生成方法,并提供了具体的实现代码。通过自定义的62进制字符集,可以有效地缩短原始链接的长度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

短链生成方法,将自增的ID转化为62进制字符串即可缩短长度。 

package com.urlservices.common;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

//自定义数值跟字符串的映射
public class ShortURLHelper {
	private static Logger logger = LoggerFactory.getLogger(ShortURLHelper.class);
	// 自定62进制义字符
	private final static String sp = "iBCW3eQs5RJagMT7nOX8f2ozwGKxDZFbuNLSlYVIt4q6Ay1cP0Urkjmp9hvdEH";
	private final static int radix = sp.length();

	/**
	 * 将自定义62进制的字符串转为10进制long
	 * 
	 * @param str
	 * @return
	 */
	public static long StrToNum(String str) {
		try {
			long result = 0;
			for (int i = 0; i < str.length(); i++) {
				result += sp.indexOf(str.charAt(i)) * Math.pow(radix, i);
			}
			return result;
		} catch (Exception ex) {
			logger.error("将code转为long异常,input:"+str+",ex:"+ex.getMessage());
			ex.printStackTrace();
			return -1;
		}
	}

	/**
	 * 将10进制的数值转换为自定义的62进制的字符串
	 * 
	 * @param num
	 * @return
	 */
	public static String NumToStr(long num) {
		try {
			if (num < 0) {
				return null;
			}
			StringBuilder buf = new StringBuilder();
			int remainder = 0;
			do {
				remainder = (int) (num % radix);// 求余数
				num = num / radix;// 除以基数
				buf.append(sp.charAt(remainder));// 保存余数,记得要倒叙排列
			} while (num != 0);
			return buf.toString();
		} catch (Exception ex) {
			logger.error("将long转为code异常,input:"+num+",ex:"+ex.getMessage());
			return null;
		}
	}

	public static void main(String[] args) {
		for (int i = 0; i < 50000; i++)
			System.out.println(i + "--" + NumToStr(i) + "--" + StrToNum(NumToStr(i)));
	}
}

<think>好的,我现在需要帮用户用Gin框架写一个生成系统的后端部分,并且使用雪花算法生成分布式唯一ID。首先,我得理清楚用户的需求是什么。他们需要的是一个后端服务,能够接收原始URL,生成对应的,并且存储起来,当访问时重定向到原始URL。同时,要确保生成ID在分布式环境下是唯一的,所以需要用到雪花算法。 首先,我得回忆一下Gin框架的基本用法。Gin是一个高性能的HTTP框架,用Go语言写的,路由和中间件的处理都很方便。接下来是雪花算法的实现,我需要确认雪花算法的结构,通常包括时间戳、机器ID、序列号等部分,确保在分布式系统中生成唯一ID。 然后,考虑项目的结构。通常一个Gin项目会有main.go作为入口,定义路由和启动服务。可能还需要一个单独的包来处理雪花算法,比如snowflake,里面实现ID生成逻辑。另外,数据存储部分,用户可能暂时不需要数据库,但为了持久化,可能需要使用内存存储者文件存储,不过根据用户的要求,可能先用内存存储,方便演示。 接下来是路由设计。用户需要两个主要的端点:一个用于创建(POST请求),另一个用于重定向(GET请求)。例如,POST /api/shorten 接收原始URL,返回;GET /:shortID 根据重定向到原始URL。 然后考虑数据存储结构。可以用一个map来存储ID到原始URL的映射,这样查找起来快速。不过要注意并发安全,因为Gin处理请求是并发的,所以需要使用sync.Map者加锁机制。不过sync.Map可能在性能上更好,特别是在读多写少的情况下。 雪花算法的实现是关键。需要确保每个实例的机器ID不同,否则可能生成重复ID。通常雪花算法中的机器ID需要配置,者通过某种方式自动获取,比如环境变量。这里可能需要用户自行配置,者通过其他方式确保不同实例的机器ID不同,比如使用Kubernetes的Pod ID者IP地址的一部分。但在示例中,可能先写死者通过环境变量传入。 接下来是生成。雪花算法生成的ID是int64,需要将其转换为更字符串,比如Base62编码,这样比Base64更友好,没有特殊字符。Base62编码可以使用数字和大小写字母,缩字符串长度。例如,一个int64的ID转换为Base62后大概是11位字符,取决于具体数值。 然后是处理流程。当用户POST一个原始URL时,生成雪花ID,转换字符串,存储到map中,然后返回。当用户访问时,查找map中对应的原始URL,返回302重定向。 还需要处理可能的错误,比如URL格式不正确,不存在的情况。Gin提供了方便的返回JSON和状态码的方法,可以在这里使用。 现在需要测试代码的正确性。比如,生成ID是否唯一,转换后的是否正确,重定向是否正常工作。可能需要写单元测试,但用户可能只需要示例代码,所以可以暂时省略测试部分。 最后,考虑部署和配置。比如机器ID如何设置,是否需要环境变量,者配置文件。在示例中,可能需要从环境变量读取,者默认一个值,但提醒用户在生产环境中需要正确配置。 可能遇到的问题包括:雪花算法的时间回拨问题,如何处理?通常雪花算法实现需要考虑这种情况,比如等待时间追上者抛出异常。但示例中可能暂时不处理,假设时间正常。另外,的存储如果重启会丢失,所以用户可能需要持久化存储,但根据当前需求,可能只需要内存存储,用户可以根据需要扩展。 总结步骤: 1. 实现雪花算法生成唯一ID。 2. 创建Gin路由,处理POST和GET请求。 3. 使用Base62将ID转换字符串。 4. 使用并发安全的数据结构存储映射。 5. 处理错误和重定向。 现在需要将这些步骤转化为代码,确保每个部分都正确,并且有必要的注释,方便用户理解。同时,用户可能需要知道如何运行和测试,比如使用curl命令测试API。</think>我将以模块化方式为您构建基于Gin的系统,包含分布式ID生成和核心功能实现: 一、项目结构 ``` shorturl/ ├── main.go # 入口文件 ├── go.mod ├── handler/ # 业务处理 │ └── shorten.go ├── pkg/ │ ├── snowflake/ # 雪花算法实现 │ │ └── id_gen.go │ └── base62/ # 进制转换 │ └── encode.go └── storage/ # 存储抽象 └── memory.go ``` 二、核心代码实现 1. 雪花算法实现 (`pkg/snowflake/id_gen.go`) ```go package snowflake import ( "sync" "time" ) const ( epoch = 1640995200000 // 2022-01-01 00:00:00 UTC machineIDBits = 10 sequenceBits = 12 maxMachineID = -1 ^ (-1 << machineIDBits) ) type IDGenerator struct { machineID int sequence int lastStamp int64 mu sync.Mutex } func New(machineID int) *IDGenerator { if machineID < 0 || machineID > maxMachineID { panic("invalid machine ID") } return &IDGenerator{machineID: machineID} } func (g *IDGenerator) Generate() int64 { g.mu.Lock() defer g.mu.Unlock() now := time.Now().UnixNano() / 1e6 if now < g.lastStamp { panic("clock moved backwards") } if now == g.lastStamp { g.sequence = (g.sequence + 1) & ((1 << sequenceBits) - 1) if g.sequence == 0 { for now <= g.lastStamp { now = time.Now().UnixNano() / 1e6 } } } else { g.sequence = 0 } g.lastStamp = now return ((now - epoch) << (machineIDBits + sequenceBits)) | (int64(g.machineID) << sequenceBits) | int64(g.sequence) } ``` 2. Base62编码 (`pkg/base62/encode.go`) ```go package base62 const charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" func Encode(n int64) string { if n == 0 { return string(charset[0]) } var result []byte for n > 0 { result = append(result, charset[n%62]) n /= 62 } // 反转数组 for i, j := 0, len(result)-1; i < j; i, j = i+1, j-1 { result[i], result[j] = result[j], result[i] } return string(result) } ``` 3. 存储实现 (`storage/memory.go`) ```go package storage import "sync" type MemoryStorage struct { mu sync.RWMutex store map[string]string } func NewMemoryStorage() *MemoryStorage { return &MemoryStorage{ store: make(map[string]string), } } func (s *MemoryStorage) Save(shortURL, originalURL string) { s.mu.Lock() defer s.mu.Unlock() s.store[shortURL] = originalURL } func (s *MemoryStorage) Get(shortURL string) (string, bool) { s.mu.RLock() defer s.mu.RUnlock() val, exists := s.store[shortURL] return val, exists } ``` 4. 主逻辑 (`main.go`) ```go package main import ( "net/http" "os" "shorturl/pkg/base62" "shorturl/pkg/snowflake" "shorturl/storage" "github.com/gin-gonic/gin" ) var ( idGen *snowflake.IDGenerator store *storage.MemoryStorage ) func init() { // 从环境变量获取机器ID,默认0 machineID := 0 if envID := os.Getenv("MACHINE_ID"); envID != "" { machineID = // 添加转换逻辑 } idGen = snowflake.New(machineID) store = storage.NewMemoryStorage() } func main() { r := gin.Default() r.POST("/shorten", func(c *gin.Context) { type request struct { URL string `json:"url" binding:"required,url"` } var req request if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid URL"}) return } // 生成唯一ID id := idGen.Generate() shortKey := base62.Encode(id) // 存储映射关系 store.Save(shortKey, req.URL) c.JSON(http.StatusOK, gin.H{ "short_url": "http://localhost:8080/" + shortKey, }) }) r.GET("/:key", func(c *gin.Context) { key := c.Param("key") if original, exists := store.Get(key); exists { c.Redirect(http.StatusMovedPermanently, original) return } c.JSON(http.StatusNotFound, gin.H{"error": "URL not found"}) }) r.Run(":8080") } ``` 三、运行与测试 1. 启动服务: ```bash export MACHINE_ID=1 # 不同节点设置不同ID go run main.go ``` 2. 测试请求: ```bash # 生成 curl -X POST http://localhost:8080/shorten \ -H "Content-Type: application/json" \ -d '{"url":"https://www.example.com/some/long/url"}' # 访问 curl -v http://localhost:8080/Abc123 ``` 四、系统优化方向 1. 性能提升: $$ \text{QPS} = \frac{\text{可用连接数}}{\text{平均响应时间}} $$ - 添加Redis缓存层 - 使用连接池 2. 扩展功能: ```go // 添加过期时间 type URLRecord struct { Original string ExpireAt time.Time } // 定时清理任务 func startCleanupTask() { ticker := time.NewTicker(1 * time.Hour) go func() { for range ticker.C { // 清理过期记录 } }() } ``` 3. 监控指标: - 使用Prometheus监控接口成功率 $$ \text{成功率} = \frac{\text{成功请求数}}{\text{总请求数}} \times 100\% $$ 需要补充实现分布式锁机制数据库持久化存储方案吗?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

myshare2022

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值