一个 示例,包含:
-
系统架构
-
Nginx 配置
-
Java 代码(短链生成 + 统计 + 302 跳转)
-
Redis 数据结构
-
访问统计方案
这是一个完整、可部署的模板。
✔️ 1. 系统架构设计
用户访问短链 → Nginx → Java短链服务 → Redis查真实链接
↓
记录统计数据(异步)
↓
302 重定向
✔️ 2. Redis 数据结构设计
1)短链映射
Key: short:abcd123
Value: https://example.com/activity?channel=weibo
2)访问统计(可选)
短链点击次数:count:abcd123 → incr
访问记录:log:abcd123 → list push {...json...}
✔️ 3. Nginx 配置(负责把短链路由给 Java)
短链域名:https://s.example.com/xxxxxx
nginx.conf
server {
listen 80;
server_name s.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
}
}
访问 s.example.com/abcd12 时会转到 Java 后端:
GET http://127.0.0.1:8080/abcd12
✔️ 4. Java (Spring Boot) 实现示例
4.1 短链生成 API
Controller
@RestController
@RequestMapping("/api")
public class ShortLinkController {
@Autowired
private ShortLinkService shortLinkService;
@PostMapping("/shorten")
public String createShortLink(@RequestParam String longUrl) {
return shortLinkService.createShortUrl(longUrl);
}
}
4.2 Service:短链生成 + 存入 Redis
@Service
public class ShortLinkService {
@Autowired
private StringRedisTemplate redis;
private static final String SHORT_KEY_PREFIX = "short:";
public String createShortUrl(String longUrl) {
// 生成短链 ID:6 位
String shortId = RandomStringUtils.randomAlphanumeric(6);
// 保存到 Redis
redis.opsForValue().set(SHORT_KEY_PREFIX + shortId, longUrl);
// 返回短链
return "https://s.example.com/" + shortId;
}
}
✔️ 5. Java Controller:接收短链并 302 跳转
关键部分:
-
查 Redis
-
记录统计
-
返回 302
@RestController
public class RedirectController {@Autowired private StringRedisTemplate redis; @Autowired private AsyncLogService asyncLogService; private static final String SHORT_KEY_PREFIX = "short:"; @GetMapping("/{shortId}") public ResponseEntity<Void> redirect(@PathVariable String shortId, HttpServletRequest request) { String key = SHORT_KEY_PREFIX + shortId; String longUrl = redis.opsForValue().get(key); if (longUrl == null) { return ResponseEntity.notFound().build(); } // 异步记录访问 asyncLogService.log(shortId, request); // 302 跳转 HttpHeaders headers = new HttpHeaders(); headers.setLocation(URI.create(longUrl)); return new ResponseEntity<>(headers, HttpStatus.FOUND); }}
✔️ 6. 异步记录统计(可选)
通过 @Async 保存访问日志,不阻塞跳转。
异步记录点击数量 + 访问日志
@Service
public class AsyncLogService {
@Autowired
private StringRedisTemplate redis;
@Async
public void log(String shortId, HttpServletRequest request) {
String ua = request.getHeader("User-Agent");
String ip = request.getRemoteAddr();
long timestamp = System.currentTimeMillis() / 1000;
// 点击次数
redis.opsForValue().increment("count:" + shortId);
// 访问日志
JSONObject log = new JSONObject();
log.put("ip", ip);
log.put("ua", ua);
log.put("time", timestamp);
redis.opsForList().leftPush("log:" + shortId, log.toString());
}
}
✔️ 7. 查询统计的 API 示例
查询访问次数
@GetMapping("/api/count/{shortId}")
public Long getCount(@PathVariable String shortId) {
return Long.valueOf(redis.opsForValue().get("count:" + shortId));
}
查询访问日志
@GetMapping("/api/logs/{shortId}")
public List<String> getLogs(@PathVariable String shortId) {
return redis.opsForList().range("log:" + shortId, 0, 100);
}
✔️ 8. 运行效果
-
创建短链:
POST /api/shorten
longUrl=https://example.com/page?channel=test -
用户访问短链:
-
Java 读取 Redis → 记录统计 → 302 跳转
-
在后台看到统计数据:
count:aB92kF = 1023
log:aB92kF = [ {ip:..., ua:..., time:...}, ... ]