前言
为什么会有路由层?
因为在微服务架构设计中,往往并不会直接将服务暴漏给调用端,而是通过调用路由层进行业务隔离,以达到不同的业务调用对应的服务模块。
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
- 通过最上层的路由功能可以很方便的隔离业务,但是路由层一定是高可用的,否则路由瘫痪整个服务将不可用
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论