**
Spring Cloud (三) zuul 动态路由+过滤器
**
路由在微服务体系结构的一个组成部分。例如,/可以映射到您的Web应用程序,/api/users映射到用户服务,并将/api/shop映射到商店服务。Zuul是Netflix的基于JVM的路由器和服务器端负载均衡器。
Netflix使用Zuul进行以下操作:
认证
洞察
压力测试
金丝雀测试
动态路由
服务迁移
负载脱落
安全
静态响应处理
主动/主动流量管理
在微服务架构中,需要几个基础的服务治理组件,包括服务注册与发现、服务消费、负载均衡、断路器、智能路由、配置管理等,由这几个基础组件相互协作,共同组建了一个简单的微服务系统。
在Spring Cloud微服务系统中,一种常见的负载均衡方式是,客户端的请求首先经过负载均衡(Ngnix),再到达服务网关(zuul集群),然后再到具体的服务。服务统一注册到高可用的服务注册中心集群(eureka, consul),服务的所有的配置文件由配置服务管理,配置服务的配置文件放在git仓库,方便开发人员随时改配置。
**
zuul实现动态路由+过滤拦截
**
1. 创建注册中心 cloud-server
创建spring boot项目cloud-server
(1. pom.xml文件依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
(2. application.yml文件配置
#配置服务端的端口号
server:
port: 7771
#eureka本身即是服务端又是客户端,只做服务需要把科户端关掉
eureka:
client:
register-with-eureka: false
fetch-registry: false
#配置注册中心的注册地址
service-url:
defaultZone: http://localhost:7771/eureka
#应用名称
spring:
application:
name: server
(3. 启动类添加注解
@SpringBootApplication
@EnableEurekaServer
public class CloudServerApplication {
public static void main(String[] args) {
SpringApplication.run(CloudServerApplication.class, args);
}
}
(4. 启动项目,浏览器访问 http://localhost:7771/ 看到如下界面
2. 创建服务提供者 cloud-service-provider1、cloud-service-provider2
创建spring boot项目cloud-service-provider1
(1. pom.xml依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
(2. application.yml配置
#配置端口号
server:
port: 8771
#引入注册中心
eureka:
client:
service-url:
defaultZone: http://localhost:7771/eureka
#配置应用名称
spring:
application:
name: provider
(3. 启动类配置注解
@SpringBootApplication
@EnableEurekaClient
public class CloudServiceProviderApplication {
public static void main(String[] args) {
SpringApplication.run(CloudServiceProviderApplication.class, args);
}
}
(4. 创建Controller层
package org.lhj.pro.controller;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.lhj.pro.service.ProviderService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProviderController {
@Autowired
private ProviderService aservice;
@RequestMapping("/hellofeign")
public String sayHello(HttpServletRequest request) {
String uri = request.getRequestURI();
int port = request.getLocalPort();
String ip = request.getRemoteAddr();
return aservice.hello() + ip + ":" + port + uri;
}
}
(5. 创建Service层和实现类
service接口
package org.lhj.pro.service;
public interface ProviderService {
String hello();
}
service实现类
package org.lhj.pro.service;
import org.springframework.stereotype.Service;
@Service
public class ProviderServiceImpl implements ProviderService{
@Override
public String hello() {
return "feign跑通了";
}
}
3. 将创建的项目cloud-service-provider1复制一份,修改项目名称为cloud-service-provider2
并且将application.yml的端口号改为8772,应用名称不用改,和provider1项目的一样即可。
启动注册中心cloud-server和两个服务提供者cloud-service-provider1、cloud-service-provider2,会发现 注册中心多了一个应用,并且有两个服务可用8771和8772
4. 创建项目cloud-consumer-zuul
(1. 导入pom.xml文件依赖 (注意spring boot的版本要和zuul的版本一致才兼容,不然会报错)
dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
<!-- 导入zuul的依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
(2. 配置yml文件
#定义端口号和服务名称
server:
port: 6771
spring:
application:
name: service-zuul
#zuul的相关配置
zuul:
routes:
#标识你服务的名字,可以自己定义,一般方便和规范来讲和自己应用的名称一样
myzuul:
#服务映射的路径,通过这路径就可以从外部访问你的服务了,目的是为了不暴露你机器的IP,面向服务的路由了,给你选一个可用的出来。
#这里zuul是自动依赖hystrix,ribbon的,不是面向单机
path: /provider/**
#这里一定要是服务提供者名称,所以这里配置serviceId因为跟eureka结合了,如果单独使用zuul,纳闷就必须写自己机器的Ip了
service-id: provider
eureka:
client:
service-url:
defaultZone: http://localhost:7771/eureka
(3. 启动类添加注解
package org.lhj.pro;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
(4. 配置过滤器
package org.lhj.pro.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* Created by lhj on 2019/11/01.
*/
@Component
public class TokenFilter extends ZuulFilter {
private static Logger log = LoggerFactory.getLogger(ZuulFilter.class);
//四种类型:pre,routing,error,post
//pre:主要用在路由映射的阶段是寻找路由映射表的
//routing:具体的路由转发过滤器是在routing路由器,具体的请求转发的时候会调用
//error:一旦前面的过滤器出错了,会调用error过滤器。
//post:当routing,error运行完后才会调用该过滤器,是在最后阶段的
@Override
public String filterType() {
return "pre";
}
//自定义过滤器执行的顺序,数值越大越靠后执行,越小就越先执行
@Override
public int filterOrder() {
return 0;
}
//控制过滤器生效不生效,可以在里面写一串逻辑来控制
@Override
public boolean shouldFilter() {
return true;
}
//执行过滤逻辑
@Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
log.info("{} >>> {}", request.getMethod(), request.getRequestURL().toString());
String token = request.getParameter("token");
if (StringUtils.isBlank(token) ){
context.setSendZuulResponse(false);
context.setResponseStatusCode(401);
context.setResponseBody("You request is unAuthrized,please remark your token !");
return null;
}else {
log.info("the request {} is ok", request.getRequestURL().toString());
}
return null;
}
}
启动注册中心、服务提供者(2个)和当前项目cloud-consumer-zuul,登录注册中心,页面如下
请求cloud-consumer-zuul 中application.yml配置的路径
- 不带token响应页面如下
2. 带token响应页面如下(重复刷新发现同时实现了负载均衡)
到此为止zuul的动态路由和过滤拦截就实现了。