本文目录导读:

我将为您提供一个完整的Spring Cloud Zuul网关案例,包含服务注册、路由转发、过滤器等功能。
项目结构
zuul-gateway-demo
├── pom.xml
├── src/main/java/com/example/zuul/
│ ├── ZuulGatewayApplication.java
│ ├── config/
│ │ └── ZuulConfig.java
│ ├── filter/
│ │ ├── TokenFilter.java
│ │ ├── LogFilter.java
│ │ └── ErrorFilter.java
│ └── controller/
│ └── FallbackController.java
└── src/main/resources/
└── application.yml
父POM文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>zuul-gateway-demo</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
<relativePath/>
</parent>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR12</spring-cloud.version>
</properties>
<modules>
<module>zuul-server</module>
<module>service-provider</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Zuul网关服务模块
1 Zuul网关POM文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>zuul-gateway-demo</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>zuul-server</artifactId>
<dependencies>
<!-- Zuul网关依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<!-- Eureka客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- Ribbon负载均衡 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<!-- Hystrix熔断 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!-- Web Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
2 启动类
package com.example.zuul;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@SpringBootApplication
@EnableZuulProxy // 启用Zuul网关
@EnableDiscoveryClient // 启用服务发现
public class ZuulGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulGatewayApplication.class, args);
}
}
3 配置文件application.yml
server:
port: 8769
spring:
application:
name: zuul-gateway
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
zuul:
# 路由配置
routes:
# 用户服务
user-service:
path: /api/user/**
serviceId: service-user
stripPrefix: false
# 订单服务
order-service:
path: /api/order/**
serviceId: service-order
stripPrefix: false
# 直接URL路由
direct-url:
path: /external/**
url: http://www.example.com
# 排除某些路径
ignored-patterns:
- /admin/**
# 路由前缀
prefix: /api
# 是否添加主机头
add-host-header: true
# 是否设置自定义sensitive头
sensitive-headers:
- Cookie
- Set-Cookie
# 是否启用重试
retryable: true
# 是否允许所有路由共享Headera
strip-prefix: true
# Ribbon配置
ribbon:
# 连接超时
ConnectTimeout: 3000
# 读取超时
ReadTimeout: 5000
# 最大自动重试次数
MaxAutoRetries: 1
# 切换实例最大重试次数
MaxAutoRetriesNextServer: 1
# Hystrix熔断配置
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 60000
# Actuator端点
management:
endpoints:
web:
exposure:
include: '*'
4 过滤器实现
package com.example.zuul.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
/**
* 令牌验证过滤器
*/
@Slf4j
@Component
public class TokenFilter extends ZuulFilter {
/**
* 过滤器类型
* pre: 路由之前
* routing: 路由之时
* post: 路由之后
* error: 发送错误调用
*/
@Override
public String filterType() {
return "pre";
}
/**
* 过滤顺序
*/
@Override
public int filterOrder() {
return 0;
}
/**
* 是否执行该过滤器
*/
@Override
public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
String requestURI = request.getRequestURI();
// swagger路径等不需要过滤
if (requestURI.contains("/api/user/login") ||
requestURI.contains("/api/user/register")) {
return false;
}
// 排除swagger相关
if (requestURI.contains("swagger") || requestURI.contains("api-docs")) {
return false;
}
return true;
}
/**
* 具体过滤逻辑
*/
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
log.info("=============== Token Filter ===============");
log.info("请求方法: {},请求路径: {}", request.getMethod(), request.getRequestURI());
// 获取token
String token = request.getHeader("token");
// 简单验证token
if (StringUtils.isEmpty(token)) {
log.warn("token为空,拒绝访问");
ctx.setSendZuulResponse(false); // 不进行路由
ctx.setResponseStatusCode(401); // 设置响应码
ctx.setResponseBody("{\"code\": 401, \"message\": \"未授权访问\"}");
return null;
}
// 模拟验证token
if (!"valid-token".equals(token)) {
log.warn("token无效,拒绝访问");
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
ctx.setResponseBody("{\"code\": 401, \"message\": \"token无效\"}");
return null;
}
log.info("token验证通过");
ctx.addZuulRequestHeader("userId", "123456"); // 向下游传递用户信息
return null;
}
}
5 日志过滤器
package com.example.zuul.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/**
* 日志过滤器
*/
@Slf4j
@Component
public class LogFilter extends ZuulFilter {
@Override
public String filterType() {
return FilterConstants.PRE_TYPE; // "pre"
}
@Override
public int filterOrder() {
return 1; // 在TokenFilter之后执行
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
long startTime = System.currentTimeMillis();
ctx.set("startTime", startTime);
log.info("日志过滤器 - 请求: {} {}", request.getMethod(), request.getRequestURI());
log.info("请求头信息:");
java.util.Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
String headerValue = request.getHeader(headerName);
log.info(" {}: {}", headerName, headerValue);
}
return null;
}
}
6 响应后置过滤器
package com.example.zuul.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletResponse;
/**
* 响应过滤器
*/
@Slf4j
@Component
public class ResponseFilter extends ZuulFilter {
@Override
public String filterType() {
return FilterConstants.POST_TYPE; // "post"
}
@Override
public int filterOrder() {
return FilterConstants.SEND_RESPONSE_FILTER_ORDER - 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletResponse response = ctx.getResponse();
Long startTime = (Long) ctx.get("startTime");
if (startTime != null) {
long duration = System.currentTimeMillis() - startTime;
log.info("请求处理耗时: {} ms", duration);
}
// 添加响应头
response.addHeader("X-Gateway", "Zuul");
response.addHeader("X-Request-Id", java.util.UUID.randomUUID().toString());
return null;
}
}
7 错误过滤器
package com.example.zuul.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletResponse;
/**
* 错误过滤器
*/
@Slf4j
@Component
public class ErrorFilter extends ZuulFilter {
@Override
public String filterType() {
return FilterConstants.ERROR_TYPE; // "error"
}
@Override
public int filterOrder() {
return -1; // 最先执行
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
Throwable throwable = ctx.getThrowable();
if (throwable != null) {
log.error("Zuul过滤器异常: ", throwable);
ctx.setResponseStatusCode(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
ctx.setResponseBody("{\"code\": 500, \"message\": \"网关内部错误\"}");
}
return null;
}
}
8 熔断降级处理
package com.example.zuul.controller;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
/**
* 服务降级处理
*/
@Component
public class ServiceFallbackProvider implements FallbackProvider {
@Override
public String getRoute() {
return "*"; // 所有路由都启用降级
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return 200;
}
@Override
public String getStatusText() throws IOException {
return "OK";
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
String response = "{\"code\": 503, \"message\": \"服务暂时不可用,请稍后重试\"}";
return new ByteArrayInputStream(response.getBytes(StandardCharsets.UTF_8));
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
return headers;
}
};
}
}
9 网关配置类
package com.example.zuul.config;
import com.example.zuul.filter.LogFilter;
import com.example.zuul.filter.TokenFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Zuul网关配置
*/
@Configuration
public class ZuulConfig {
/**
* 创建Token过滤器
*/
@Bean
public TokenFilter tokenFilter() {
return new TokenFilter();
}
/**
* 创建日志过滤器
*/
@Bean
public LogFilter logFilter() {
return new LogFilter();
}
}
服务提供者模块
1 服务提供者POM文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>zuul-gateway-demo</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>service-provider</artifactId>
<dependencies>
<!-- Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Eureka客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
2 启动类
package com.example.provider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class ServiceProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceProviderApplication.class, args);
}
}
3 配置文件
server:
port: 8081
spring:
application:
name: service-user
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
management:
endpoints:
web:
exposure:
include: '*'
4 用户服务Controller
package com.example.provider.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping("/api/user")
public class UserController {
/**
* 用户登录(不需要token)
*/
@PostMapping("/login")
public Map<String, Object> login(@RequestBody Map<String, String> params) {
log.info("用户登录请求: {}", params);
Map<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("message", "登录成功");
result.put("data", "登录成功后的token");
return result;
}
/**
* 获取用户信息(需要token)
*/
@GetMapping("/info/{id}")
public Map<String, Object> getUserInfo(@PathVariable("id") String id,
HttpServletRequest request) {
String userId = request.getHeader("userId");
log.info("获取用户信息,请求ID: {}, 用户ID: {}", id, userId);
Map<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("userId", id);
result.put("username", "张三");
result.put("email", "zhangsan@example.com");
return result;
}
/**
* 用户列表
*/
@GetMapping("/list")
public Map<String, Object> getUserList(@RequestParam(required = false) Integer page,
@RequestParam(required = false) Integer size) {
log.info("查询用户列表,page: {}, size: {}", page, size);
Map<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("data", new Object[]{
new HashMap<String, Object>() {{
put("id", 1);
put("username", "张三");
}},
new HashMap<String, Object>() {{
put("id", 2);
put("username", "李四");
}}
});
return result;
}
}
5 订单服务(另一个服务的示例)
package com.example.provider.controller;
import org.springframework.web.bind.annotation.*;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping("/api/order")
public class OrderController {
/**
* 创建订单
*/
@PostMapping("/create")
public Map<String, Object> createOrder(@RequestBody Map<String, Object> order) {
log.info("创建订单: {}", order);
Map<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("orderId", "ORD" + System.currentTimeMillis());
result.put("message", "订单创建成功");
return result;
}
/**
* 查询订单
*/
@GetMapping("/{orderId}")
public Map<String, Object> getOrder(@PathVariable("orderId") String orderId) {
log.info("查询订单: {}", orderId);
Map<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("orderId", orderId);
result.put("amount", 199.00);
result.put("status", "已支付");
return result;
}
}
Eureka注册中心(可选)
1 Eureka Server POM
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
2 Eureka Server配置
server:
port: 8761
spring:
application:
name: eureka-server
eureka:
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://localhost:8761/eureka/
测试用例
package com.example.zuul;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
@SpringBootTest
public class ZuulGatewayTest {
private RestTemplate restTemplate = new RestTemplate();
/**
* 测试登录(不需要token)
*/
@Test
public void testLogin() {
String url = "http://localhost:8769/api/api/user/login";
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "application/json");
String requestBody = "{\"username\":\"zhangsan\",\"password\":\"123456\"}";
HttpEntity<String> entity = new HttpEntity<>(requestBody, headers);
ResponseEntity<String> response = restTemplate.exchange(
url, HttpMethod.POST, entity, String.class);
System.out.println("响应状态: " + response.getStatusCode());
System.out.println("响应内容: " + response.getBody());
}
/**
* 测试获取用户信息(需要token)
*/
@Test
public void testGetUserInfo() {
String url = "http://localhost:8769/api/api/user/info/123";
HttpHeaders headers = new HttpHeaders();
headers.set("token", "valid-token");
HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<String> response = restTemplate.exchange(
url, HttpMethod.GET, entity, String.class);
System.out.println("响应状态: " + response.getStatusCode());
System.out.println("响应内容: " + response.getBody());
}
/**
* 测试无token访问(应该被拒绝)
*/
@Test
public void testWithoutToken() {
String url = "http://localhost:8769/api/api/user/info/123";
ResponseEntity<String> response = restTemplate.exchange(
url, HttpMethod.GET, null, String.class);
System.out.println("响应状态: " + response.getStatusCode());
System.out.println("响应内容: " + response.getBody());
}
}
测试命令
# 启动Eureka Server
mvn spring-boot:run -pl eureka-server
# 启动服务提供者
mvn spring-boot:run -pl service-provider
# 启动Zuul网关
mvn spring-boot:run -pl zuul-server
# 测试接口
curl -X POST http://localhost:8769/api/api/user/login \
-H "Content-Type: application/json" \
-d '{"username":"zhangsan","password":"123456"}'
curl -X GET http://localhost:8769/api/api/user/info/123 \
-H "token: valid-token"
curl -X GET http://localhost:8769/api/api/user/info/123
关键特性说明
路由配置说明
- serviceId: 服务的唯一标识
- path: 路由匹配的路径表达式
- stripPrefix: 是否去除前缀
- url: 直接URL路由
过滤器类型
- pre: 路由之前执行
- routing: 路由时执行
- post: 路由之后执行
- error: 错误处理
主要功能
- 请求路由转发
- 认证与授权
- 日志记录
- 熔断降级
- 负载均衡
- 响应处理
这个案例展示了Spring Cloud Zuul的核心功能,可以作为实际项目的参考模板。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播