前言
为什么会有路由层?
因为在微服务架构设计中,往往并不会直接将服务暴漏给调用端,而是通过调用路由层进行业务隔离,以达到不同的业务调用对应的服务模块。
spring cloud zuul
spring cloud zuul 路由是微服务架构的不可或缺的一部分,提供动态路由、监控、弹性、安全等的边缘服务。
zuul 是 netflix 出品的一个基于 jvm 路由和服务端的负载均衡器。
环境准备
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 │ └── zuulapplication.java └── resources └── application.yml
itstack-demo-springcloud-eureka-client | 服务提供方
提供一个查询用户信息的简单方法,在配置文件中通过修改端口启动2次,模拟双实例应用,为调用方负载做准备。
- web/eurekaclientcontroller.java & 注意@enableeurekaclient用于向注册中心提供服务
@enableeurekaclient @restcontroller public class eurekaclientcontroller { @value("${server.port}") private int port; @requestmapping(path = "/api/queryuserinfo", method = requestmethod.get) public string queryuserinfo(@requestparam string userid) { return "hi 微信公众号:bugstack虫洞栈 | " + userid + " >: from eureka client port: " + port; } }
- eurekaclientapplication.java & 服务启动类
@springbootapplication public class eurekaclientapplication { public static void main(string[] args) { springapplication.run(eurekaclientapplication.class, args); } }
- pom.xml & 配置文件指向注册中心
server: port: 8001 spring: application: name: itstack-demo-springcloud-eureka-client eureka: client: serviceurl: defaultzone: http://localhost:7397/eureka/
itstack-demo-springcloud-eureka-server | 单个服务注册中心
服务注册中心用于承载接口提供方向上注册,同时正在调用方链接后可以获取指定应用的服务实例。
- eurekaserverapplication.java & 通过注解@enableeurekaserver启动服务注册与发现中心
@springbootapplication @enableeurekaserver public class eurekaserverapplication { public static void main(string[] args) { springapplication.run( eurekaserverapplication.class, args ); } }
- pom.xml & 服务注册中心
server: port: 7397 eureka: instance: hostname: localhost client: registerwitheureka: false fetchregistry: false serviceurl: defaultzone: http://${eureka.instance.hostname}:${server.port}/eureka/ spring: application: name: itstack-demo-springcloud-eureka-server
itstack-demo-springcloud-feign | feign服务调用方
feign 是一个声明式的 web service 客户端,它的目的就是让 web service 调用更加简单。它整合了 ribbon 和 hystrix,从而让我们不再需要显式地使用这两个组件。feign 还提供了 http 请求的模板,通过编写简单的接口和插入注解,我们就可以定义好 http 请求的参数、格式、地址等信息。接下来,feign 会完全代理 http 的请求,我们只需要像调用方法一样调用它就可以完成服务请求。
feign 具有如下特性:
可插拔的注解支持,包括 feign 注解和 jax-rs 注解 支持可插拔的 http 编码器和解码器 支持 hystrix 和它的 fallback 支持 ribbon 的负载均衡 支持 http 请求和响应的压缩
service/feignservice.java |
注解方式调用,方便易用。@feignclient会在调用时进行解析服务到具体的http://ip:port/
@feignclient(value = "itstack-demo-springcloud-eureka-client", fallback = feignservicehystrix.class) public interface feignservice { @requestmapping(value = "/api/queryuserinfo", method = requestmethod.get) string queryuserinfo(@requestparam string userid); }
- service/hystrix/feignservicehystrix.java | 提供熔断服务,当发生异常时主动返回预定结果
@component public class feignservicehystrix implements feignservice { @override public string queryuserinfo(string userid) { return "queryuserinfo by userid:" + userid + " err!from feign hystrix"; } }
- web/feigncontroller.java | 使用接口提供服务 from feign
@restcontroller public class feigncontroller { @resource private feignservice ribbonservice; @requestmapping(path = "/api/queryuserinfo", method = requestmethod.get) public string queryuserinfo(@requestparam string userid) { return ribbonservice.queryuserinfo(userid) + " from feign"; } }
feignapplication.java |
- 注解@enableeurekaclient、@enablefeignclients、@enablediscoveryclient获取调用注册中心服务
@springbootapplication @enableeurekaclient @enablediscoveryclient @enablefeignclients @enablehystrix public class feignapplication { public static void main(string[] args) { springapplication.run(feignapplication.class, args); } }
- application.yml | eureka服务配置,从注册中心获取可用服务。开启hystrix=true
server: port: 9001 spring: application: name: itstack-demo-springcloud-feign eureka: client: serviceurl: defaultzone: http://localhost:7397/eureka/ feign.hystrix.enabled: true
itstack-demo-springcloud-ribbon | ribbon服务调用方
ribbon是一个基于 http 和 tcp 的客户端负载均衡器。它可以通过在客户端中配置 ribbonserverlist 来设置服务端列表去轮询访问以达到均衡负载的作用。
当 ribbon 与 eureka 联合使用时,ribbonserverlist 会被 discoveryenabledniwsserverlist 重写,扩展成从 eureka 注册中心中获取服务实例列表。同时它也会用 niwsdiscoveryping 来取代 iping,它将职责委托给 eureka 来确定服务端是否已经启动。
service/ribbonservice.java |
- 接口式硬编码调用不太易于维护,因此也是比较少用的方式。hystrix实际通过getfallback()返回熔断结果
@service public class ribbonservice { @autowired private resttemplate resttemplate; @hystrixcommand(fallbackmethod = "queryuserinfofallback") public string queryuserinfo(string userid) { return resttemplate.getforobject("http://itstack-demo-springcloud-eureka-client/api/queryuserinfo?userid=" + userid, string.class); } /** * specifies a method to process fallback logic. * a fallback method should be defined in the same class where is hystrixcommand. * also a fallback method should have same signature to a method which was invoked as hystrix command. * for example: * <code> * @hystrixcommand(fallbackmethod = "getbyidfallback") * public string getbyid(string id) {...} * * private string getbyidfallback(string id) {...} * </code> * also a fallback method can be annotated with {@link hystrixcommand} * <p/> * default => see {@link com.netflix.hystrix.contrib.javanica.command.genericcommand#getfallback()} * * @return method name * * getfallback() * * @override * protected object getfallback() { * final commandaction commandaction = getfallbackaction(); * if (commandaction != null) { * try { * return process(new action() { * @override * object execute() { * metaholder metaholder = commandaction.getmetaholder(); * object[] args = createargsforfallback(metaholder, getexecutionexception()); * return commandaction.executewithargs(metaholder.getfallbackexecutiontype(), args); * } * }); * } catch (throwable e) { * logger.error(fallbackerrormessagebuilder.create() * .append(commandaction, e).build()); * throw new fallbackinvocationexception(unwrapcause(e)); * } * } else { * return super.getfallback(); * } * } */ public string queryuserinfofallback(string userid) { return "queryuserinfo by userid:" + userid + " err!from ribbon hystrix"; } }
- web/ribboncontroller.java | 使用接口提供服务 from ribbon
@restcontroller public class ribboncontroller { @resource private ribbonservice ribbonservice; @requestmapping(path = "/api/queryuserinfo", method = requestmethod.get) public string queryuserinfo(@requestparam string userid) { return ribbonservice.queryuserinfo(userid) + " from ribbon"; } }
ribbonapplication.java |
- 通过注解@loadbalanced注册rest模版,用于ribbon接口调用。并启动@enablehystrix
@springbootapplication @enableeurekaclient @enablediscoveryclient @enablehystrix public class ribbonapplication { public static void main(string[] args) { springapplication.run(ribbonapplication.class, args); } @bean @loadbalanced resttemplate resttemplate() { return new resttemplate(); } }
- application.yml | eureka服务配置,从注册中心获取可用服务
server: port: 9002 spring: application: name: itstack-demo-springcloud-ribbon eureka: client: serviceurl: defaultzone: http://localhost:7397/eureka/
itstack-demo-springcloud-zuul | zull路由层
spring cloud zuul 路由是微服务架构的不可或缺的一部分,提供动态路由、监控、弹性、安全等的边缘服务。zuul 是 netflix 出品的一个基于 jvm 路由和服务端的负载均衡器。
- zuulapplication.java & 路由服务启动
@springbootapplication @enablezuulproxy @enableeurekaclient @enablediscoveryclient public class zuulapplication { public static void main(string[] args) { springapplication.run(zuulapplication.class, args); } }
- pom.mxl & 路由配置
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: 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 接口调用方feign
- itstack-demo-springcloud-hystrix-ribbon 接口调用方ribbon
- itstack-demo-springcloud-zuul 路由服务
2.测试接口
- 访问feign、ribbon接口,验证服务是否可用;http://localhost:9001/api/queryuserinfo?userid=111、http://localhost:9002/api/queryuserinfo?userid=111
- 访问路由接口a;http://localhost:10001/route-a/api/queryuserinfo?userid=111
- 访问路由接口b;http://localhost:10001/route-b/api/queryuserinfo?userid=111
hello| 111 >: from eureka client port: 8001 from ribbon
综上总结
- zuul目前springcloud结合的是zuul 1, netflix 已经发布了 zuul 2但目前还未整合
- springcloud还有自己的网关服务;spring cloud gateway
- 通过最上层的路由功能可以很方便的隔离业务,但是路由层一定是高可用的,否则路由瘫痪整个服务将不可用
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论