分布式系统开发---服务网关SpringCloud(十)
有几天没有写文章了,新搬的公司感觉味道重,经常头疼,影响进度😂。
言归正传,本节我们引入 路由
路由是微服务架构的不可或缺的一部分,比如我们使用"/" 映射到你应用主页,/goods-service/goods映射到
商品服务
,/order-service/order映射到订单服务
。
Spring Cloud中的Zuul不仅可以帮助我们完成路由,还具有认证
、压力测试
、金丝雀测试
、动态路由
、负载削减
、安全
、静态响应处理
、主动/主动交换管理
,同时Zuul的规则引擎允许通过任何JVM语言来编写规则和过滤器。
接下来我们在项目中引入Zuul,首先,创建一个新的模块zuul
,在依赖选择里面分别添加Eureka Server
以及
Zuul
本节我上传代码到了私有Git仓库,同时更换了MBP作开发,IDE使用了IDEA 2018.3,所以可能看到的图片跟之前的不是太一样,大家仍然按照正常开发就可。
引入完依赖之后,修改ZuulApplication
代码
package com.felix.zuul;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@EnableDiscoveryClient
@EnableZuulProxy
@SpringBootApplication
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
这里,我们启用了服务发现@EnableDiscoveryClient
,启用了服务网关@EnableZuulProxy
。接下来,修改application.properties
为application.yml
,对服务器做如下配置:
server:
port: 9000
spring:
application:
name: zuul-service #指定服务名称
eureka:
client:
register-with-eureka: true #是否注册到Eureka服务中
fetch-registry: true #是否从Eureka服务中获取注册信息
service-url: #Eureka客户端与服务端进行交互的地址
defaultZone: http://felix:123456@127.0.0.1:7000/eureka/
instance:
prefer-ip-address: true #把ip地址注册到Eureka服务中
ip-address: 127.0.0.1
zuul:
routes:
goods-service:
path: /goods-service/**
serviceId: goods-service
这里,把服务网关注册到了服务中心
,同时,把所有商品服务
相关的接口映射到了goods-service
这个服务中,启动如下服务器
1台或多台```服务中心服务器```
1台或多台```商品服务器```
1台```网关服务器```
所有服务器启动之后,访问下http://127.0.0.1:7000/
查看服务是否成功注册:
没问题,那么我们首先访问下
http://127.0.0.1:8000/goods/2
,返回信息如下:商品信息正常返回,但是异常的确实返回的数据不再是JSON格式的数据,而是XML格式的数据了,这个问题我们之前提到过,解决方案就是在
Eureka
依赖中排除jackson-dataformat-xml
,以下是goods
模块中的Eureka
依赖修改后的样子:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</exclusion>
</exclusions>
</dependency>
修改后重启商品服务器
并再次访http://127.0.0.1:8000/goods/2
返回格式已经正常了。为了测试网关的路由是否能够为我们正常提供服务,我们访问
http://localhost:9000/goods-service/goods/3
可见,9000端口的服务网关已经能够正常的为我们的服务提供路由转发了。但是,每次访问又要把原本业务的api添加一个
goods-service
字符串,很麻烦有木有。当然,解决办法是有的,修改zuul
模块中的application.yml
的zuul配置
server:
port: 9000
spring:
application:
name: zuul-service #指定服务名称
eureka:
client:
register-with-eureka: true #是否注册到Eureka服务中
fetch-registry: true #是否从Eureka服务中获取注册信息
service-url: #Eureka客户端与服务端进行交互的地址
defaultZone: http://felix:123456@127.0.0.1:7000/eureka/
instance:
prefer-ip-address: true #把ip地址注册到Eureka服务中
ip-address: 127.0.0.1
zuul:
routes:
goods-service:
path: /goods/**
serviceId: goods-service
strip-prefix: false
我们把所有跟goods
相关的服务映射到商品信息
服务上,同时设置strip-prefix
为false
,就达到我们的需求了,修改后启动服务器测试下http://localhost:9000/goods/3
这样,我们在不破坏原有接口的前提下,通过9000端口,就可以像访问8000端口的商品信息一样获取信息了。
当然,我们平时还需要它帮我们做一些其他的事情,比如说请求验证、请求预处理等等,接下来,我们就来看下如何实现这些功能。
首先我们需要创建一个继承于
ZuulFilter
的类,并实现它的四个方法
public String filterType()
FilterConstants.PRE_TYPE
请求被路由前调用FilterConstants.POST_TYPE
在ROUTE和ERROR后调用FilterConstants.ROUTE_TYPE
请求时调用FilterConstants.ERROR_TYPE
请求出现错误时调用
public int filterOrder()
数值越大优先级越靠后
public boolean shouldFilter()
是否进行过滤
public Object run() throws ZuulException
具体的过滤规则实现
接下来,我们加入自己的逻辑,创建一个UserFilter
,对于到来的请求查看是否携带token
信息,如果携带了,则认为是已经登录的用户,正常返回数据,否则,不进行路由,返回我们设定好的数据,如下
package com.felix.zuul.filter;
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.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class UserFilter extends ZuulFilter{
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
HttpServletRequest req = RequestContext.getCurrentContext().getRequest();
String token = req.getParameter("token");
if (StringUtils.isEmpty(token)){
RequestContext.getCurrentContext().setSendZuulResponse(false);
RequestContext.getCurrentContext().setResponseStatusCode(200);
RequestContext.getCurrentContext().setResponseBody("{\"error\":\"invalid token\"}");
}
return null;
}
}
其中setSendZuulResponse
设置为false
则不在进行路由,相反,路由按照正常罗技的执行。代码里,我们设定请求中必须携带token信息,如果没有token信息则返回invalid token
,启动服务器,我们首先访问http://127.0.0.1:9000/goods/3
,由于没有携带token信息,被提示为无效的token信息
然后访问
http://127.0.0.1:9000/goods/3?token=123
,此时,由于携带了token信息,路由到了商品服务器
,正常获得商品信息截止到现在,分布式系统常用的组件基本介绍完毕了,项目也已经改造的差不多了,有个问题也就凸显了出来,其实我们目前的业务逻辑很少改变,改变的大多数是服务器的配置,随着服务器数量的增多,服务器配置将是一件繁琐的事情,那么,我们怎么样才能让这件事情更简单呢?下节,我们将开始把将服务器的配置工作一步一步的减轻,最终达到在同一个地方修改配置,无需重启服务器,自动同步到所有的服务器中。
以上内容转载请注明出处,同时也请大家不吝你的关注和下面的赞赏
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓