Spring Cloud学习(五):Zuul服务网关
1. Zuul介绍
Zuul在微服务中主要是做路由转发功能和请求过滤功能。
- 路由转发:根据一定的规则,区分请求并转发到对应的服务商,比如两个请求/api/user/getUser,/api/blog/getBlog在zuul服务中分别转发到用户服务和博客服务。
- 请求过滤:很多服务请求都有会身份验证,就可以直接在zuul中去对身份验证的token去做校验,而不用在每个服务中都去做一次校验那么麻烦
2. 准备工作
准备一个用户服务,一个博客服务,一个注册中心
2.1. 用户服务
- application.yml
server: port: 4003 spring: application: name: service-user eureka: client: service-url: defaultZone: http://eureka-server1:3001/eureka/ instance: instance-id: 用户服务-4003
- UserController.java
/** * @author 吕梁山 * @date 2019/7/19 */ @RestController @RequestMapping("/api/user") public class UserController { @RequestMapping(value = "/getUser", method = RequestMethod.GET) public String getUser(String userId){ return "查找用户:" + userId; } @RequestMapping(value = "/addUser", method = RequestMethod.POST) public String addUser(String userName){ return "新增用户姓名:" + userName; } @RequestMapping(value = "/updateUser", method = RequestMethod.PUT) public String updateUser(String userName){ return "更新用户姓名:" + userName; } @RequestMapping(value = "/deleteUser", method = RequestMethod.DELETE) public String deleteUser(String userId){ return "删除用户:" + userId; } }
2.2. 博客服务
- application.yml
server: port: 4004 spring: application: name: service-blog eureka: client: service-url: defaultZone: http://eureka-server1:3001/eureka/ instance: instance-id: 博客服务-4004
- UserController.java
/** * @author 吕梁山 * @date 2019/7/19 */ @RestController @RequestMapping("/api/blog") public class BlogController { @RequestMapping(value = "/getBlog", method = RequestMethod.GET) public String getBlog(String blogId){ return "查找博客:" + blogId; } @RequestMapping(value = "/addBlog", method = RequestMethod.POST) public String addBlog(String blogName){ return "新增博客标题:" + blogName; } @RequestMapping(value = "/updateBlog", method = RequestMethod.PUT) public String updateBlog(String blogName){ return "更新博客标题:" + blogName; } @RequestMapping(value = "/deleteBlog", method = RequestMethod.DELETE) public String deleteBlog(String blogId){ return "删除博客:" + blogId; } }
2.3. 注册中心
- application.yml
server: port: 3001 eureka: instance: # 在服务中心显示的名称 hostname: eureka-server1 client: # false表示不向注册中心注册自己 register-with-eureka: false # false声明是服务端 fetch-registry: false service-url: # 设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址(单机)。 defaultZone: http://eureka-server1:3001/eureka/ server: enable-self-preservation: false
3. 创建Zuul服务
- pom.xml
<?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>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.pikaqiu.springcloud</groupId> <artifactId>zuul</artifactId> <version>0.0.1-SNAPSHOT</version> <name>SpringCloud-Zuul</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.SR2</spring-cloud.version> </properties> <dependencies> <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.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <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>
- SpringCloudZuulApplication.java
@SpringCloudApplication @EnableEurekaClient @EnableZuulProxy public class SpringCloudZuulApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudZuulApplication.class, args); } }
- application.yml
server: port: 5005 spring: application: name: zuul eureka: client: service-url: defaultZone: http://eureka-server1:3001/eureka/ instance: instance-id: Zuul路由服务-5005
启动后查看注册中心
4. 配置Zuul
配置zuul之前先看用户服务和博客服务能否正常访问,分别访问
http://127.0.0.1:4003/api/user/getUser?userId=1 http://127.0.0.1:4004/api/blog/getBlog?blogId=1
可以看到分别返回
查找用户:1 查找博客:1
Zuul的路由转发配置主要在配置文件去配置
zuul: routes: service-user: path: /api/user/** serviceId: service-user stripPrefix: false service-blog: path: /api/blog/** serviceId: service-blog stripPrefix: false
- path:匹配的规则
- serviceId:要转发至的服务id
- stripPrefix: 是否去掉前缀(为true的时候访问/api/user/getUser在转发到用户服务时会去除path配置的/api/user/变成/getUser)
配置好Zuul后,分别访问
http://127.0.0.1:5005/api/user/getUser?userId=1 http://127.0.0.1:5005/api/blog/getBlog?blogId=1
可以看到分别返回
查找用户:1 查找博客:1
这样就说明Zuul的路由转发成功了。
5. 请求过滤
请求过滤即将那些不符合要求的请求过滤掉,这里演示就只以请求是否带token这个参数为例
5.1. 新建一个过滤器(新建一个类去继承ZuulFilter类)
/** * @author 吕梁山 * @date 2019/7/19 */ @Component public class MyZuulFilter extends ZuulFilter { Logger logger = LoggerFactory.getLogger(MyZuulFilter.class); /** * pre:路由之前 * routing:路由之时 * post: 路由之后 * error:发送错误调用 */ @Override public String filterType() { logger.info("准备进入路由-------------->"); return "pre"; } /** * 过滤的顺序 */ @Override public int filterOrder() { return 0; } /** * 这里可以写逻辑判断,是否要过滤,true为永远过滤。 */ @Override public boolean shouldFilter() { return true; } /** * 过滤器的具体逻辑 */ @Override public Object run() { RequestContext currentContext = RequestContext.getCurrentContext(); HttpServletRequest request = currentContext.getRequest(); HttpServletResponse response = currentContext.getResponse(); response.setHeader("Content-Type","text/html;charset=UTF-8"); response.setCharacterEncoding("UTF-8"); String token = request.getParameter("token"); if(StringHelper.isNullOrEmptyString(token)){ logger.warn("token is empty"); currentContext.setSendZuulResponse(false); currentContext.setResponseStatusCode(401); currentContext.setResponseBody("token为空"); return null; } return null; } }
自定义过滤器的实现,继承ZuulFilter后需要重写实现下面四个方法
- filterType:返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型
- pre:可以在请求被路由之前调用
- routing:在路由请求时候被调用
- post:在routing和error过滤器之后被调用
- error:处理请求时发生错误时被调用
- filterOrder:通过int值来定义过滤器的执行顺序
- shouldFilter:返回一个boolean类型来判断该过滤器是否要执行,所以通过此函数可实现过滤器的开关。在上例中,我们直接返回true,所以该过滤器总是生效。
- run:过滤器的具体逻辑。需要注意,这里我们通过ctx.setSendZuulResponse(false)令zuul过滤该请求,不对其进行路由,然后通过ctx.setResponseStatusCode(401)设置了其返回的错误码,当然我们也可以进一步优化我们的返回,比如,通过ctx.setResponseBody(body)对返回body内容进行编辑等。
5.2. 进行测试
- 首先不带token进行一次访问
http://127.0.0.1:5005/api/blog/getBlog?blogId=1
返回结果为
token为空
- 然后带上token进行访问
http://127.0.0.1:5005/api/blog/getBlog?blogId=1&token=test
返回结果为
查找博客:1
至此Zuul过滤器也成功了
欢迎留言:http://pikaqiu.vip/article/2370.html
示例代码:https://github.com/Liangshan1994/SpringCloud
相关文章