前言介绍
在实际的业务开发中不只是将路由配置放到文件中,而是需要进行动态管理并且可以在变化时不用重启系统就可以更新。与此同时还需要在接口访问的时候,可以增加一些权限验证以防止恶意访问。
1.filter过滤器,通过继承实现对应方法可以进行控制过滤;
pre
:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。routing
:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用 apache httpclient 或 netfilx ribbon 请求微服务。post
:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 http header、收集统计信息和指标、将响应从微服务发送给客户端等。error
:在其他阶段发生错误时执行该过滤器。 除了默认的过滤器类型,zuul 还允许我们创建自定义的过滤器类型。例如,我们可以定制一种 static 类型的过滤器,直接在 zuul 中生成响应,而不将请求转发到后端的微服务。
2.自定义路由,同构实现simpleroutelocator和refreshableroutelocator自动刷新
- protected map<string, zuulroute> locateroutes():此方法是加载路由配置的,父类中是获取properties中的路由配置,可以通过扩展此方法,达到动态获取配置的目的
- public route getmatchingroute(string path):此方法是根据访问路径,获取匹配的路由配置,父类中已经匹配到路由,可以通过路由id查找自定义配置的路由规则,以达到根据自定义规则动态分流的效果
环境准备
- 1.jdk 1.8、idea2018、maven3
- 2.spring boot 2.0.6.release
- 3.spring cloud finchley.sr2
代码示例
itstack-demo-springcloud-08 ├── itstack-demo-springcloud-eureka-client │ └── src │ └── main │ ├── java │ │ └── org.itstack.demo │ │ ├── web │ │ │ └── eurekaclientcontroller.java │ │ └── eurekaclientapplication.java │ └── resources │ └── application.yml ├── itstack-demo-springcloud-eureka-server │ └── src │ └── main │ ├── java │ │ └── org.itstack.demo │ │ └── eurekaserverapplication.java │ └── resources │ └── application.yml ├── itstack-demo-springcloud-hystrix-feign │ └── src │ └── main │ ├── java │ │ └── org.itstack.demo │ │ ├── service │ │ │ ├── hystrix │ │ │ │ └── feignservicehystrix.java │ │ │ └── feignservice.java │ │ ├── web │ │ │ └── feigncontroller.java │ │ └── feignapplication.java │ └── resources │ └── application.yml ├── itstack-demo-springcloud-hystrix-ribbon │ └── src │ └── main │ ├── java │ │ └── org.itstack.demo │ │ ├── service │ │ │ └── ribbonservice.java │ │ ├── web │ │ │ └── ribboncontroller.java │ │ └── ribbonapplication.java │ └── resources │ └── application.yml └── itstack-demo-springcloud-zuul └── src └── main ├── java │ └── org.itstack.demo │ ├── config │ │ └── zuulconfig.java │ ├── filter │ │ └── tokenfilter.java │ ├── router │ │ └── routelocator.java │ ├── service │ │ └── refreshrouteservice.java │ └── zuulapplication.java └── resources └── application.yml
itstack-demo-springcloud-zuul & 动态路由与权限过滤
- 1.通过routelocator实现自己的动态路由配置,其实就是把配置文件内容转移到这里用代码类实现,并且可以根据需要修改为从数据库里获取。
- 2.tokenfilter提供了权限验证功能,当用户访问时候会带上token否则拦截
- 3.此外还提供了自动刷新的接口,用于外部调用刷新配置
- 4.最后我们需要修改application配置,zuul中还需要排除不做路由的接口[刷新权限接口]
config/zuulconfig.java & 路由配置类
@configuration public class zuulconfig { @autowired private zuulproperties zuulproperties; @autowired private serverproperties server; @bean public routelocator routelocator() { return new routelocator(this.server.getservlet().getpath(), this.zuulproperties); } }
filter/tokenfilter.java & 权限校验类
public class tokenfilter extends zuulfilter { /** * 过滤器的类型,它决定过滤器在请求的哪个生命周期中执行。 * filterconstants.pre_type:代表会在请求被路由之前执行。 * pre、routing、post、error */ public string filtertype() { return filterconstants.pre_type; } /** * filter执行顺序,通过数字指定。[数字越大,优先级越低] */ public int filterorder() { return 0; } /** * 判断该过滤器是否需要被执行。这里我们直接返回了true,因此该过滤器对所有请求都会生效。 * 实际运用中我们可以利用该函数来指定过滤器的有效范围。 */ public boolean shouldfilter() { return true; } /* * 具体执行逻辑 */ public object run() { requestcontext ctx = requestcontext.getcurrentcontext(); httpservletrequest request = ctx.getrequest(); string token = request.getparameter("token"); if (token == null || token.isempty()) { ctx.setsendzuulresponse(false); ctx.setresponsestatuscode(401); ctx.setresponsebody("refuse! token is empty"); } return null; } }
router/routelocator.java & 路由类
public class routelocator extends simpleroutelocator implements refreshableroutelocator { private zuulproperties properties; public routelocator(string servletpath, zuulproperties properties) { super(servletpath, properties); this.properties = properties; } @override public void refresh() { dorefresh(); } @override protected map<string, zuulroute> locateroutes() { linkedhashmap<string, zuulroute> routesmap = new linkedhashmap<string, zuulroute>(); //从application.properties中加载路由信息 routesmap.putall(super.locateroutes()); //从db中加载路由信息 routesmap.putall(routesconfiggroup()); //优化一下配置 linkedhashmap<string, zuulroute> values = new linkedhashmap<>(); for (map.entry<string, zuulroute> entry : routesmap.entryset()) { string path = entry.getkey(); // prepend with slash if not already present. if (!path.startswith("/")) { path = "/" + path; } if (stringutils.hastext(this.properties.getprefix())) { path = this.properties.getprefix() + path; if (!path.startswith("/")) { path = "/" + path; } } values.put(path, entry.getvalue()); } return values; } /** * 路由配置组,可以从数据库中读取 * 基本配置与写在文件中配置类似,如下; * # routes: * # api-a: * # path: /route-a/** * # serviceid: itstack-demo-springcloud-feign * # api-b: * # path: /route-b/** * # serviceid: itstack-demo-springcloud-ribbon * @return 配置组内容 */ private map<string, zuulroute> routesconfiggroup() { map<string, zuulroute> routes = new linkedhashmap<>(); zuulroute zuulroute = new zuulroute(); zuulroute.setid("route-a"); zuulroute.setpath("/route-a/**"); zuulroute.setserviceid("itstack-demo-springcloud-feign"); // 如果使用了注册中心,那么可以根据serviceid进行访问。 // zuulroute.seturl("http://localhost:9001"); zuulroute.setretryable(false); zuulroute.setstripprefix(true); zuulroute.setsensitiveheaders(new hashset<>()); routes.put(zuulroute.getpath(), zuulroute); return routes; } }
service/refreshrouteservice.java & 路由刷新服务
@service public class refreshrouteservice { @autowired private applicationeventpublisher publisher; @autowired private routelocator routelocator; public void refreshroute() { routesrefreshedevent routesrefreshedevent = new routesrefreshedevent(routelocator); publisher.publishevent(routesrefreshedevent); } }
zuulapplication.java & 启动服务注意注解,另外提供了服务接口
@springbootapplication @enablezuulproxy @enableeurekaclient @enablediscoveryclient @restcontroller public class zuulapplication { public static void main(string[] args) { springapplication.run(zuulapplication.class, args); } @bean public tokenfilter tokenfilter() { return new tokenfilter(); } @autowired private refreshrouteservice refreshrouteservice; @autowired private zuulhandlermapping zuulhandlermapping; @requestmapping("api/refresh") public string refresh(){ refreshrouteservice.refreshroute(); return "success"; } @requestmapping("api/queryrouteinfo") @responsebody public map<string, object> queryrouteinfo(){ return zuulhandlermapping.gethandlermap(); } }
application.yml & 配置文件修改,路由过滤
server: port: 10001 spring: application: name: itstack-demo-ddd-zuul eureka: client: serviceurl: defaultzone: http://localhost:7397/eureka/ # 动态路由,以下配置注释; # http://localhost:10001/route-a/api/queryuserinfo?userid=111 # http://localhost:10001/route-b/api/queryuserinfo?userid=111 zuul: ignoredpatterns: /api/** # routes: # api-a: # path: /route-a/** # serviceid: itstack-demo-springcloud-feign # api-b: # path: /route-b/** # serviceid: itstack-demo-springcloud-ribbon
测试验证
1.分别启动如下服务;
- itstack-demo-springcloud-eureka-server 服务注册与发现
- itstack-demo-springcloud-eureka-client 接口提供方
- itstack-demo-springcloud-hystrix-feign 调用端
- itstack-demo-springcloud-hystrix-ribbon 调用端
- itstack-demo-springcloud-zuul 路由服务
2.可测试接口列表;
- 路由服务:http://localhost:10001/route-a/api/queryuserinfo?userid=111&token=111
hello | 111 >: from eureka client port: 8001 from feign
- 刷新配置:http://localhost:10001/api/refresh
- 内容配置:http://localhost:10001/api/queryrouteinfo
综上总结
路由服务可以方便的帮我们控制业务类型的区分访问,同时自动刷新可以更加方便的使用网关路由
权限验证是几乎不可少的在实际开发过程中会经常用到,所有的接口必须是安全可靠的,保证数据不泄露
另外还可以考虑从入参的用户身份进行路由,这样可以把数据库路由提前,让不同用户组直接访问到不同的数据库组
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。