SpringCloud构建微服务之-apiGateway
通过前面几节,我们已经用spring cloud的组件构建了一个简单的微服务。比如通过swarm集群中部署了一个高可用的eureka服务注册中心,以及两个简单的微服务,一个服务提供者microservice-provider-userservic(用户信息服务),一个即是服务消费者,又是服务提供者的microservice-consumer-productservice(商品信息服务)。
本节主要讨论一下微服务中不可缺少的功能服务网关,主要服务网关在微服务中,不仅对外提供了访问微服务的路由和微服务的负载均衡策略,而且还可以利用过滤器实现对微服务的权限控制。在本文中,主要用了spring cloud zuul来实现,通过对eureka-service中注册的serviceId的映射内部微服务,向外部定义匹配微服务路由的模式,通过继承zuulfilter来自定义过滤器实现外部系统访问微服务的权限控制。通过实现ZuulFallbackProvider接口返回访问微服务失败后的回退方法,使其更易直观的说明微服务的问题,提高容错能力。
参考
疑问 | 参考 |
---|---|
如果你对eureka注册中心不太了解? | SpringCloud构建微服务-eureka |
如果你对服务注册不太了解 | SpringCloud构建微服务-provider |
如果你对服务消费不太了解 | SpringCloud构建微服务-consumer |
如果你对docker安装不了解 | docker、docker-compse最新版本安装 |
如果你对docker-swarm集群创建不太了解 | docker-swarm创建与管理集群 |
如果你对swarm集群的服务部署不太了解 | docker-swarm集群服务部署与维护 |
如果你不知道docker-compose怎么部署swarm集群? | docker-compose部署swarm服务 |
准备工作
如果不熟悉相关的技术,请在参考中找到对应的文章进行操作。也可以通过文章最后附的源码地址,自己clone下来,再构建。
在使用Zuul之前,我们先启动eureka-server和这两个服务。通过访问eureka-server注册中心,可以看到microservice-provider-userservice和microservice-consumer-productservice注册到了服务中心。如下图所示
开始使用Zuul之旅
创建microservice-api-gataway子项目,引入zuul的依赖spring-cloud-starter-zuul
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<!-- 如果不是通过指定serviceId的方式,eureka依赖不需要,但是为了对服务集群细节的透明性,还是用serviceId来避免直接引用url的方式吧-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
应用主类ApiGatewayApplication中使用@EnableZuulProxy注解开启Zuul
@EnableZuulProxy
@SpringBootApplication
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
}
application.yml中配置Zuul应用的基础信息和路由微服务的相关配置
spring:
application:
name: microservice-api-gateway
server:
port: 9516
eureka:
instance:
hostname: apiGateway
prefer-ip-address: true
ip-address: ${eureka.instance.hostname}
instance-id: ${eureka.instance.hostname}:${server.port}
client:
serviceUrl:
defaultZone: http://eadmin:eadmin123@eurekaService1:9511/eureka/,.......
healthcheck:
enabled: true
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 60000
zuul:
routes:
api-productservice:
path: /api/product/**
serviceId: microservice-consumer-productservice
stripPrefix: true
api-usertservice:
path: /api/users/**
serviceId: microservice-provider-userservice
zuul配置说明:
在之前的构建微服务案例时,服务名与服务实例地址的关系已经存在eureka server中了,所以只需将Zuul注册到eureka server上去就可以发现其他服务,通过在zuul中配置serviceId的映射。
zuul:
routes:
api-productservice:
path: /api/product/**
serviceId: microservice-consumer-productservice
api-usertservice:
path: /api/users/**
serviceId: microservice-provider-userservice
针对我们在准备工作中实现的两个微服务microservice-provider-userservice和microservice-consumer-productservice,定义了两个路由api-a和api-b来分别映射。另外为了让Zuul能发现microservice-provider-userservice和microservice-consumer-productservice,也加入了eureka的配置
服务过滤之安全控制
通过以上配置我们完成了微服务路由,我们需要一些安全措施来保护内部服务。所以我们需要利用Zuul的过滤器来实现我们对外服务的安全控制。
在服务网关中定义过滤器只需要继承ZuulFilter,覆盖以下四个方法,如:
/**
* Created by troylc on 2017/3/14.
*/
@Component
public class AccessControlFilter extends ZuulFilter {
private static Logger log = LoggerFactory.getLogger(AccessControlFilter.class);
/*
*filterType:返回一个字符串代表过滤器的类型:
* pre:可以在请求被路由之前调用
* routing:在路由请求时候被调用
* post:在routing和error过滤器之后被调用
* error:处理请求时发生错误时被调用
*/
@Override
public String filterType() {
return "pre";
}
/*
*filterOrder:通过int值来定义过滤器的执行顺序
*/
@Override
public int filterOrder() {
return 0;
}
/*
*shouldFilter:返回一个boolean类型来判断该过滤器是否要执行,所以通过此函数可实现过滤器的开
*关,以下直接返回true,所以该过滤器总是生效。
*/
@Override
public boolean shouldFilter() {
return true;
}
/*
*过滤器的具体逻辑。需要注意,这里我们通过ctx.setSendZuulResponse(false)令zuul过滤该请求,
* 不对其进行路由,然后通过ctx.setResponseStatusCode(401)设置了其返回的错误码,
* 当然我们也可以进一步优化我们的返回,比如,通过ctx.setResponseBody(body)对返回body内容进行编辑,
* 如果有中文乱码。则可以:ctx.getResponse().setContentType("text/html;charset=UTF-8")
*/
@Override
public Object run() {
....... 实现授权认证逻辑,具体参考源码。
}
}
定义zuul服务fallback
完成了服务网站的filter,我们可以针对具体的内部服务,在zuul中定义服务的回退方法
如:
UserFallbackProvider:
/**
* 用户微服务短路器返回调用
* Created by troylc on 2017/3/14.
*/
@Component
public class UserFallbackProvider implements ZuulFallbackProvider{
//得到具体的路由serviceid,这里写的是用户微服务的serviceid
@Override
public String getRoute() {
return "microservice-provider-userservice";
}
@Override
public ClientHttpResponse fallbackResponse() {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return 200;
}
@Override
public String getStatusText() throws IOException {
return "OK";
}
@Override
public void close() {
}
//短路器返回的具体内容
@Override
public InputStream getBody() throws IOException {
ResultInfo resultInfo = new ResultInfo<>(ReturnInfoEnum.ERROR_SERVICE.getState(),
ReturnInfoEnum.ERROR_SERVICE.getStateInfo() + ";服务名为:" + UserFallbackProvider.this.getRoute());
return new ByteArrayInputStream(resultInfo.toString().getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}
ProductFallbackProvider:
/**
* 商品微服务短路器返回调用
* Created by troylc on 2017/3/14.
*/
@Component
public class ProductFallbackProvider implements ZuulFallbackProvider {
@Override
public String getRoute() {
return "microservice-consumer-productservice";
}
@Override
public ClientHttpResponse fallbackResponse() {
return new ClientHttpResponse() {
.......
@Override
public InputStream getBody() throws IOException {
ResultInfo resultInfo = new ResultInfo<>(ReturnInfoEnum.ERROR_SERVICE.getState(),
ReturnInfoEnum.ERROR_SERVICE.getStateInfo()+";服务名为:"+ProductFallbackProvider.this.getRoute());
return new ByteArrayInputStream(resultInfo.toString().getBytes());
}
};
}
}
compose部署服务及测试
docker-compose文件如下:
version: "3"
services:
.........在之前的docker-compose文件基础之上,增加以下内容:
apiGateway:
image: tcr:5000/myhub/microservice-api-gateway:0.0.1
deploy:
replicas: 1 #定义 replicated 模式的服务的复本数量
update_config:
parallelism: 1 #每次更新复本数量
delay: 2s #每次更新间隔
restart_policy:
condition: on-failure #定义服务的重启条件
networks:
- eureka-net
ports:
- "9516:9516"
networks:
eureka-net:
driver: overlay
接下来,我们将microservice-eureka-services、microservice-provider-userservice、microservice-consumer-productservice以及这里用Zuul实现的服务网关启动起来,在eureka-server的控制页面中,我们可以看到分别注册了icroservice-provider-userservice、microservice-consumer-productservice以及microservice-api-gateway
在swarm集群的manager节点中执行以下操作:
查看eureka注册中心
通过zuul去访问商品服务中的获取用户节点,此时不带accessToken参数,测试访问接受需要授权
加上accessToken参数访问:
下面测试停止商品服务后,zuul的fallback方法回调,首先操作swarm集群中,把商品服务停止
查看eureka注册中心,确认product服务是否已经停止
再次访问商品服务,提示fallback方法返回的内容:
附代码仓库地址:
码云:
https://git.oschina.net/gittroylc/spring-cloud-docker-microservice
github:
https://github.com/troychn/spring-cloud-docker-microservice
请先 后发表评论~