微服务网关
API(接口) Gateway(网关)
接口分类:
外部接口:
其他机构合作伙伴进行调用(必须在外网访问),蚂蚁开放平台,微信公众号开发 需要通过appid + appsocet生成accessToken进行通讯。
内部接口:
一般只能在局域网中进行访问,服务与服务调用之间都在同一个微服务系统中。目 的是为了保证安全
如何设计一套api接口:
- 接口权限(开放接口 | 内部接口)
- 考虑幂等性
- 安全性(Https)
- 防止篡改数据(验证签名)
- 使用网关拦截接口实现接口的黑名单和白名单
- 接口使用Http + json格式,目的是为了跨平台
- 考虑高并发,实现服务保护(降级,隔离,熔断)
- 最后使用统一的API管理平台api swagger
网关
网关分为内网网关和外网网关,相当于客户端请求统一请求到网关服务器上,再由网关服务器进行转发到实际的服务地址上,类似于Nginx
网关的作用:网关可以拦截客户端所有请求,对该请求进行权限控制,负载均衡,日志管理,接口调用监控等
过滤器与网关的区别:过滤器适合单个tomcat服务器进行拦截请求。网关是拦截整个微服务的所有请求
Zuul
Nginx与Zuul的区别
相同点:都可以实现负载均衡,反向代理,过滤请求,实现网关效果
不同点:Nginx采用c语言编写,Zuul采用java语言编写;Zuul实现负载均衡采用ribbon+eureka实现本地负载均衡,Nginx实现负载均衡采用服务器实现负载均衡;Nginx比Zuul功能会更加强大,因为Nginx能整合一些脚本语言(Nginx+Lua);Nginx适合于服务端负载均衡,Zuul适合微服务中的网关
最好是Nginx+Zuul实现网关:Nginx实现反向代理,Zuul对微服务实现网关拦截
搭建Zuul网关系统
Maven依赖

<?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.jx.zuul</groupId> <artifactId>zuul_demo</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.6.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Finchley.SR2</spring-cloud.version> </properties> <dependencies> <!-- web starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 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> </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>
application.yml配置
# 服务注册地址 eureka: client: service-url: defaultZone: http://localhost:8100/eureka # 网关端口 server: port: 80 # 网关名称 spring: application: name: server-zuul zuul: routes: api-a: # api-a这个是随便写的,也可以写api-member # 当客户端发送请求127.0.0.1:80/api-member/**时,都会转发到serviceId为jx-member的服务 path: /api-member/** # 服务别名,zuul网关默认整合ribbon,自动实现负载均衡 serviceId: jx-member api-b: path: /api-order/** serviceId: jx-order
在启动类上标注@EnableZuulProxy开启网关代理
@SpringBootApplication @EnableZuulProxy // 开启网关代理 @EnableEurekaClient public class ZuulDemoApplication { public static void main(String[] args) { SpringApplication.run(ZuulDemoApplication.class, args); } }
然后通过127.0.0.1:80/api-member/ 就可以访问到会员服务的地址了
搭建ZuulFilter拦截请求参数
搭建ZuulFilter拦截请求非常简单,只需要写一个类继承ZuulFilter类即可,然后将这个类添加到容器即可
import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import org.apache.commons.lang.StringUtils; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; @Component public class TokenFilter extends ZuulFilter { /** * 过滤器类型 * return "pre"; 表示请求之前执行 * @return */ @Override public String filterType() { return "pre"; } /** * 过滤器执行的顺序 * 当一个请求在同一个阶段存在多个过滤器的时候,指定过滤器的执行顺序 * @return */ @Override public int filterOrder() { return 0; } /** * 判断过滤器是否生效 * @return */ @Override public boolean shouldFilter() { return true; } /** * 编写过滤器拦截业务代码 * @return * @throws ZuulException */ @Override public Object run() throws ZuulException { // 获取上下文 RequestContext currentContext = RequestContext.getCurrentContext(); // 获取request HttpServletRequest request = currentContext.getRequest(); // 获取Token,从请求头获取 String userToken = request.getParameter("userToken"); if (StringUtils.isEmpty(userToken)) { // 不会继续执行,不会调用服务接口,网关服务直接响应客户端 currentContext.setSendZuulResponse(false); // 封装到map中 currentContext.setResponseBody("userToken is empty!"); // 封装到map中 currentContext.setResponseStatusCode(401); return null; } // 正常调用其他服务 return null; } }
搭建动态Zuul网关路由转发
需求,如果在服务器启动的时候,我们需要给网关添加路由规则,比如加个api-c,这时候是需要重启服务器的,但是如果我们结合SpringCloud Config分布式配置中心,就能实现动态路由规则
导入Maven依赖
这里导入的是config-client依赖,假设configServer已经搭建好了
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-client</artifactId> </dependency>
然后将zuul相关配置写到gitee上面
命名为zuul-server-dev.yml
然后在本地配置bootstrap.yml
# 服务注册地址 eureka: client: service-url: defaultZone: http://localhost:8100/eureka # 网关端口 server: port: 80 # 网关名称 spring: application: name: zuul-server cloud: config: # 读取的环境 profile: dev discovery: # ConfigServer在注册中心上的名字 service-id: config-server # 开启读取权限 enabled: true
最后在启动类上添加zuulProperties()使zuul能够使用config
public class ZuulDemoApplication { public static void main(String[] args) { SpringApplication.run(ZuulDemoApplication.class, args); } // zuul配置能够使用config实现实时更新 @RefreshScope @ConfigurationProperties("zuul") public ZuulProperties zuulProperties() { return new ZuulProperties(); } }
之后访问127.0.0.1:80/api-member/** 即可转发到会员服务
实现网关集群
Zuul如何实现搭建集群版本: Nginx +Zuul一主一备或者轮询多个
上图即是zuul搭建集群的基本思想:
客户端发送请求统一到Nginx上,在使用Nginx实现反向代理和负载均衡,然后采用轮询算法转发到网关上,最后由网关转发请求到对应的服务