我们都知道,zuul 网关可以拦截微服务系统中请求做统一的处理,比如说可以结合 JWT 实现无状态的身份验证,拒绝 Cookie 中没有携带 Token 或者 Token 解析失败的的请求,这样做的好处是减轻了其他微服务的压力,但也无疑加大 zuul 网关的工作量,那要怎么解决这个问题呢?答案是可以使用负载均衡服务器(比如说 Nginx),将请求均匀地转发到 zuul 网关集群,下面我来尝试实现这一方案,大致的流程如下:
- 搭建 Eureka 注册中心
- 创建业务相关的微服务模块(仅作简单的模拟)
- 搭建 zuul 集群
- 配置 Nginx,实现负载均衡
- 测试
1. Eureka 注册中心
创建 Maven 模块,取名为 eureka-registry,在 pom.xml 中设置相关依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
</dependencies>
在 resources 目录下新建 application.yml 配置文件
server:
port: 10086
spring:
application:
name: eureka-registry
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:${server.port}/eureka
register-with-eureka: false # 把自己注册到eureka服务列表
fetch-registry: false # 拉取eureka服务信息
server:
enable-self-preservation: false # 关闭自我保护
eviction-interval-timer-in-ms: 5000 # 每隔5秒钟,进行一次服务列表的清理
编写启动类
@SpringBootApplication
@EnableEurekaServer
public class RegistryApplication {
public static void main(String[] args) {
SpringApplication.run(RegistryApplication.class, args);
}
}
项目架构如下所示
![](https://img.haomeiwen.com/i10650679/bda6fc19fba666ce.jpg)
2. 创建业务相关的微服务模块
创建 micro-service 模块,在 pom.xml 文件中设置相关依赖
<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>
</dependencies>
在 resources 目录下新建 application.yml 配置文件
server:
port: 8084
spring:
application:
name: micro-service
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
instance:
lease-renewal-interval-in-seconds: 5 # 每隔5秒发送一次心跳
lease-expiration-duration-in-seconds: 10 # 10秒不发送就过期
编写启动类
@SpringBootApplication
@EnableDiscoveryClient
public class MicroApplication {
public static void main(String[] args) {
SpringApplication.run(MicroApplication.class, args);
}
}
在 cc.geekeye.micro.pojo 包下新建
package cc.geekeye.micro.pojo;
public class CommonObject {
private Integer id;
private String col1;
private String col2;
//constructors...
//getters and setters
}
添加 controller
@Controller
@RequestMapping("common")
public class CommonController {
@ResponseBody
@RequestMapping("get")
public CommonObject getCommonObject() {
System.out.println("bfjkabfjk");
return new CommonObject(1, "col1", "col2");
}
}
3. 搭建 zuul 集群
创建 zuul-gateway 模块,在 pom.xml 文件中设置相关依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
在 resources 目录下新建 application.yml 配置文件
server:
port: 10010
spring:
application:
name: zuul-gateway
eureka:
client:
registry-fetch-interval-seconds: 5
service-url:
defaultZone: http://127.0.0.1:10086/eureka
zuul:
prefix: /api
routes:
micro-service: /micro/**
编写启动类
@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
编写网关过滤器
@Component
public class AccessFilter extends ZuulFilter {
private static final Logger logger = LogManager.getLogger(AccessFilter.class);
@Value("${server.port}")
private String serverPort;
@Override
public String filterType() {
return "pre"; //
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
logger.info("网关执行端口号:" + serverPort);
return null;
}
}
这里使用端口区分集群节点,为了方便测试,所以将当前处理请求的 zuul 网关的响应端口输出到日志文件,而此处使用的日志框架是 log4j2。
现在有一个网关了,那要怎么搭建集群呢?部署3份 jar 包吗?但如果我们还要修改一些代码又要重新打包和部署,岂不是很麻烦?其实我们可以使用 IDEA 提供的 Run Dashboard 选项运行 zuul 网关微服务,等到运行它成功后, application.yml 配置文件已被加载进 Tomcat 容器,这时我们再修改 application.yml 配置文件中的运行端口,然后复制运行配置就行了。
![](https://img.haomeiwen.com/i10650679/2e4aa5b7e230767e.jpg)
保留默认配置,这里只是改了个名字
![](https://img.haomeiwen.com/i10650679/66f5b39f164bc1ec.jpg)
运行配置,结果如下所示
![](https://img.haomeiwen.com/i10650679/758696fbb009e6f4.jpg)
4. Nginx 负载均衡
修改 nginx.conf 配置文件,在 http 节点中配置三个上游服务器
http {
upstream backServer {
server 127.0.0.1:10010;
server 127.0.0.1:10011;
server 127.0.0.1:10012;
}
server {
listen 80;
server_name api.blabla.com;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
location / {
proxy_pass http://backServer/;
proxy_connect_timeout 600;
proxy_read_timeout 600;
}
}
}
其中 api.blabla.com 是在 host 文件中配置解析到 localhost 的。
5. 测试
现在环境已经搭建好,下面先来通过一张整体的结构图来了解我在上面做了什么事情。
![](https://img.haomeiwen.com/i10650679/ab966cbd501caf64.jpg)
- 客户端访问 http://api.blabla.com/api/micro/common/get
- 由于 Nginx 代理了80端口,所以该请求交给它处理,它将解析到 http 节点中配置的 api.blabla.com 需要转发的上游服务器并根据其负载均衡策略进行转发。
- 比如说转发到 http://127.0.0.1:10010/api/micro/common/get ,则交由监听该端口的网关处理,它将先消掉前缀 /api,进行相关的过滤和处理后,因为 /micro 匹配 micro-service,它查找其在 Eureka 注册中心中拉取到的服务列表,若找到则进行转发。
- 请求转发到 http://127.0.0.1:8084/micro/common/get ,这时 micro-service 就可以进行处理并响应了。
了解整个流程后就可以进行测试,为了方便,我使用 HttpClient 访问 http://api.blabla.com/api/micro/common/get ,通过比较网关中输出的日志信息检验负载均衡是否成功。
public class HttpTests {
private CloseableHttpClient httpClient;
@Before
public void init() {
httpClient = HttpClients.createDefault();
}
@Test
public void testGetPojo() throws IOException {
for (int i = 0; i < 1000; i++) {
HttpGet request = new HttpGet("http://api.blabla.com/api/micro/common/get");
this.httpClient.execute(request, new BasicResponseHandler());
Thread.sleep(200);
}
}
}
为了看得更直观,我在日志配置文件 log4j2.xml 中过滤掉了其他类库的日志信息,测试运行成功后查看输出日志:
![](https://img.haomeiwen.com/i10650679/27de93c3482545c9.jpg)
网友评论