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

举报
评论 0