一、概述
zuul和nginx都是一种代理服务器,它们主要的目的都是将客户端请求转发到后端的服务器上。但是,它们之间还是有一些区别的。
二、负载均衡
nginx早期就引入了负载均衡的概念,它可以通过一些算法将请求分发到多台服务器上,从而降低单机的压力,提高反应速度。而zuul的负载均衡是在Netflix的Eureka上实现的,基于Ribbon和RestTemplate实现,在请求到达zuul后,zuul会向Eureka注册中心查询可用的服务,并选出一台最优的服务器,将请求转发到这台服务器上。
此外,在使用zuul实现负载均衡时,我们还需要在服务提供者中配置对应的注解。
@RestController
@RequestMapping("/api")
public class ApiController {
@Autowired
private RestTemplate restTemplate;
@ApiOperation("测试zuul的负载均衡")
@GetMapping("/test")
public String test() {
return "This is from provider " + restTemplate.getForEntity("http://provider/test", String.class).getBody();
}
}
在上述代码中,我们可以看到,@Autowired注解引入了RestTemplate,这是实现负载均衡的核心组件,而restTemplate.getForEntity()方法中的URL参数中只写了服务的名称,比如"provider",而没有写具体的IP地址和端口号,这是因为要使用zuul的负载均衡功能。
三、动态路由
zuul相对于nginx的另一个优势在于它支持动态路由。在微服务中,如果有新的服务需要加入,或者某个服务需要下线,这个时候我们就需要修改nginx的配置文件并重启nginx,而这个过程可能比较繁琐。但是,在zuul中,我们只需要在Eureka上注册或者下线服务,zuul可以动态地检测这个变化,并相应地调整路由规则,从而实现自动化的服务发现和动态路由。
以下是一个实现了动态路由功能的zuul网关。
@SpringBootApplication
@EnableZuulProxy
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
@Value("${spring.cloud.config.uri}")
private String url;
@Autowired
private DiscoveryClient discoveryClient;
@EventListener
public void onApplicationEvent(ApplicationReadyEvent event) {
refreshRoute();
}
@Scheduled(fixedRate = 60000)
public void refreshRoute() {
List
instances = discoveryClient.getInstances("zuul");
if (!instances.isEmpty()) {
ServiceInstance serviceInstance = instances.get(0);
String path = "/actuator/bus-refresh";
String url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + path;
RestTemplate restTemplate = new RestTemplate();
restTemplate.postForEntity(url, null, null);
}
}
}
上述代码实现了通过定时器定时向Eureka Server查询可用服务并更新路由规则。我们可以注意到,使用@EnableZuulProxy注解开启了zuul的代理功能,@Autowired引入了DiscoveryClient,它能查询Eureka上注册的服务。在事件监听方法onApplicationEvent()中,我们在应用启动时即刻启动一次路由刷新,以防重启zuul前Eureka上已有服务改变。在定时任务方法refreshRoute()中,我们通过RestTemplate发送POST请求以触发路由刷新。
四、动态访问日志
nginx在访问日志这个方面表现异常优秀,可以实时记录请求日志,并支持不同格式的日志输出。但是,在zuul这个框架中,要实现动态日志需要借助第三方工具。最常见的做法是结合Logstash和Elasticsearch,这样我们就可以把所有的请求日志都存储到Elasticsearch上,并通过Kibana进行可视化展示和分析。
以下是一个实现动态日志功能的zuul网关。
#zuul配置文件
zuul:
routes:
user:
path: /user/**
url: http://localhost:8080
product:
path: /product/**
url: http://localhost:8081
request:
occlude-headers:
#Logback日志配置文件
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
./logs/api-apm.log
true
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
UTF-8
./logs/%d{yyyy-MM-dd}/api-apm-%d{yyyy-MM-dd_HH}.log
7
#Spring Boot启动类
@SpringBootApplication(scanBasePackages = "com.example")
@EnableZuulProxy
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
@Bean
public ConfigurableServletWebServerFactory configurableServletWebServerFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.addConnectorCustomizers((connector -> {
AbstractHttp11Protocol protocol = (AbstractHttp11Protocol) connector.getProtocolHandler();
protocol.setCompression("on");
protocol.setCompressionMinSize(1024);
String[] mimeTypes = protocol.getCompressableMimeTypes();
List
mimeList = new ArrayList<>(Arrays.asList(mimeTypes));
mimeList.add("application/json");
protocol.setCompressableMimeTypes(mimeList.toArray(new String[0]));
}));
return factory;
}
@Bean
public PatternLayoutEncoder encoder() {
PatternLayoutEncoder encoder = new PatternLayoutEncoder();
encoder.setPattern("%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n");
encoder.setCharset(Charset.forName("UTF-8"));
return encoder;
}
@Bean
public LoggerFilter loggerFilter() {
LoggerFilter loggerFilter = new LoggerFilter();
loggerFilter.setAppenders("api-apm");
loggerFilter.setAdditive(false);
return loggerFilter;
}
@Bean
public Logger logger() {
Logger logger = (Logger) LoggerFactory.getLogger("com.netflix.zuul.monitoring.MonitoringHelper");
logger.setLevel(Level.ERROR);
return logger;
}
}
在上述代码中,我们使用Logback配置了日志相关信息,通过@EnableZuulProxy注解启用了zuul的服务代理功能。在@Bean注解的方法中,我们还配置了Tomcat的压缩策略和编码信息,以及zuul的日志过滤器和日志级别。
总之,zuul和nginx二者的优劣势都很明显,它们的选择要根据实际使用场景来判断。在微服务架构中,通常会使用zuul作为服务网关,以实现动态路由、负载均衡等功能。但是,在静态网页服务器、WEB路由、反向代理等方面,nginx的性能和稳定性都是值得称赞的。