* 1、项目结构:
涉及到的服务有:
api-gateway、userservice、taskservice,项目由Springboot-springcloud-jpa-fegin-consul-zuul构成微服务
* 2、网关服务:
其中 api-gateway 是网关服务 ,负责url的分发到各个微服务,用的是zuul,通过url匹配,在consul中找到并分发到不同服务,服务注册发现容器用的是consul,还可以用于配置项目key-value
consul配置如下:
spring:
cloud:
inetutils:
ignoredInterfaces:
- docker0
- veth.*
- VMware.*
- VPN.*
consul:
host: consul.zhibi.config
#port: 8500
discovery:
healthCheckInterval: 15s #服务中心健康检查间隔
preferIpAddress: true#使用外部IP注册服务,默认使用的是主机名
health-check-critical-timeout: 5m #5分钟没有恢复服务的从服务注册中心移除
instanceId: ${spring.cloud.client.hostname}:${spring.application.name}:${server.port}
config:
prefix: config
defaultContext: xiuba-apigateway
profileSeparator: ','
format: PROPERTIES
服务分发用的是cloud全家桶中的zuul服务
配置如下:
spring:
application:
name: apigateway
zuul:
ignoredServices: '*'
routes:
user-api:
path: /user/**
stripPrefix: false
serviceId: userservice
task-api:
path: /task/**
stripPrefix: false
serviceId: taskservice
debug:
request: true
include-debug-header: true
host:
connect-timeout-millis: 10000
socket-timeout-millis: 60000
max-total-connections: 1000
max-per-route-connections: 200
ribbon:
ReadTimeout: 10000
#开启饥饿模式,启动时创建好对应服务的client
eager-load:
enabled: true
clients: userservice,taskservice,orderservice,payservice,searchservice
以/user开始的url会被分配到userservice服务中,
以/task开始的url会被分派到taskservice服务中,
stripPrefix: true的时候,访问相应服务的时候会去掉前面的/user、/task前缀
* 3、task、user服务
cloud配置:
spring:
cloud:
inetutils:
ignoredInterfaces:
- docker0
- veth.*
- VMware.*
- VPN.*
consul:
host: consul.zhibi.config
#port: 8500
discovery:
healthCheckInterval: 15s #服务中心健康检查间隔
preferIpAddress: true#使用外部IP注册服务,默认使用的是主机名
health-check-critical-timeout: 5m #5分钟没有恢复服务的从服务注册中心移除
instanceId: ${spring.cloud.client.hostname}:${spring.application.name}:${server.port}
config:
prefix: config
defaultContext: xiuba-task
profileSeparator: ','
format: PROPERTIES
* 4、swagger的maven依赖如下:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>RELEASE</version>
</dependency>
* 5、zuul-swagger配置
①、SwaggerConfig.java:
package com.zhibi.xiuba.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.web.UiConfiguration;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* Created by qinhe_admin on 2017/9/20.
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("白拿拿系统")
.description("白拿拿系统接口文档说明")
.termsOfServiceUrl("http://www.xiuba365.com")
.contact(new Contact("秦贺", "", "qinhelili@gmail.com"))
.version("1.0")
.build();
}
@Bean
UiConfiguration uiConfig() {
return new UiConfiguration(null, "list", "alpha", "schema",
UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS, false, true);
}
}
②、DocumentationConfig
package com.zhibi.xiuba.config;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import java.util.ArrayList;
import java.util.List;
/**
* Created by qinhe_admin on 2017/9/20.
*/
@Component
@Primary
public class DocumentationConfig implements SwaggerResourcesProvider {
@Override
public List<SwaggerResource> get() {
List resources = new ArrayList<>();
resources.add(swaggerResource("活动task模块API", "/task/v2/api-docs", "2.0"));
resources.add(swaggerResource("用户账户user模块API", "/user/v2/api-docs", "2.0"));
return resources;
}
private SwaggerResource swaggerResource(String name, String location, String version) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location);
swaggerResource.setSwaggerVersion(version);
return swaggerResource;
}
}
* 6、task和user服务的swagger配置
配置:
xiuba:
swagger:
title: 活动task模块API
description: 活动模块接口文档说明
termsOfServiceUrl: http://www.xiuba365.com/
version: 1.0
basePackage: com.zhibi.xiuba.controller
module: task
xiuba:
swagger:
title: 用户账户user模块API
description: 用户账户模块接口文档说明
termsOfServiceUrl: http://www.xiuba365.com/
version: 1.0
basePackage: com.zhibi.xiuba.controller
module: user
SwaggerConfig.java:
package com.zhibi.xiuba.configers;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.web.UiConfiguration;
/**
* Created by qinhe_admin on 2017/9/20.
*/
@Configuration
@ConfigurationProperties(prefix = "xiuba.swagger")
public class SwaggerConfig {
private String title;
private String description;
private String termsOfServiceUrl;
private String version;
private String basePackage;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getTermsOfServiceUrl() {
return termsOfServiceUrl;
}
public void setTermsOfServiceUrl(String termsOfServiceUrl) {
this.termsOfServiceUrl = termsOfServiceUrl;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getBasePackage() {
return basePackage;
}
public void setBasePackage(String basePackage) {
this.basePackage = basePackage;
}
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage(basePackage))
// .apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
// .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title(title)
.description(description)
.termsOfServiceUrl(termsOfServiceUrl)
.contact(new Contact("秦贺", "", "qinhelili@gmail.com"))
.version(version)
.build();
}
@Bean
UiConfiguration uiConfig() {
return new UiConfiguration(null, "list", "alpha", "schema", UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS, false, true);
}
}
修改源代码的url:
Swagger2Controller.java:
package com.zhibi.xiuba.configers;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import io.swagger.models.Swagger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.util.UriComponents;
import springfox.documentation.annotations.ApiIgnore;
import springfox.documentation.service.Documentation;
import springfox.documentation.spring.web.DocumentationCache;
import springfox.documentation.spring.web.PropertySourcedMapping;
import springfox.documentation.spring.web.json.Json;
import springfox.documentation.spring.web.json.JsonSerializer;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.mappers.ServiceModelToSwagger2Mapper;
import javax.servlet.http.HttpServletRequest;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.zhibi.xiuba.configers.HostNameProvider.componentsFrom;
import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE;
@Controller
@ApiIgnore
@ConfigurationProperties(prefix = "xiuba.swagger")
public class Swagger2Controller {
private String module;
private static final String HAL_MEDIA_TYPE = "application/hal+json";
private final String hostNameOverride;
private final DocumentationCache documentationCache;
private final ServiceModelToSwagger2Mapper mapper;
private final JsonSerializer jsonSerializer;
public String getModule() {
return module;
}
public void setModule(String module) {
this.module = module;
}
@Autowired
public Swagger2Controller(
Environment environment,
DocumentationCache documentationCache,
ServiceModelToSwagger2Mapper mapper,
JsonSerializer jsonSerializer) {
this.hostNameOverride = environment.getProperty("springfox.documentation.swagger.v2.host", "DEFAULT");
this.documentationCache = documentationCache;
this.mapper = mapper;
this.jsonSerializer = jsonSerializer;
}
@RequestMapping(value = "/{module}/v2/api-docs", method = RequestMethod.GET, produces = {APPLICATION_JSON_VALUE, HAL_MEDIA_TYPE})
@PropertySourcedMapping(
value = "${springfox.documentation.swagger.v2.path}",
propertyKey = "springfox.documentation.swagger.v2.path")
@ResponseBody
public ResponseEntity<Json> getDocumentation(
@RequestParam(value = "group", required = false) String swaggerGroup,
@PathVariable(value = "module", required = true) String module,
HttpServletRequest servletRequest) {
if (org.apache.commons.lang3.StringUtils.isNotBlank(module) && module.equalsIgnoreCase(this.module)) {
String groupName = Optional.fromNullable(swaggerGroup).or(Docket.DEFAULT_GROUP_NAME);
Documentation documentation = documentationCache.documentationByGroup(groupName);
if (documentation == null) {
return new ResponseEntity<Json>(HttpStatus.NOT_FOUND);
}
Swagger swagger = mapper.mapDocumentation(documentation);
UriComponents uriComponents = componentsFrom(servletRequest, swagger.getBasePath());
swagger.basePath(Strings.isNullOrEmpty(uriComponents.getPath()) ? "/" : uriComponents.getPath());
if (isNullOrEmpty(swagger.getHost())) {
swagger.host(hostName(uriComponents));
}
return new ResponseEntity<Json>(jsonSerializer.toJson(swagger), HttpStatus.OK);
}else {
return new ResponseEntity<Json>(HttpStatus.NOT_FOUND);
}
}
private String hostName(UriComponents uriComponents) {
if ("DEFAULT".equals(hostNameOverride)) {
String host = uriComponents.getHost();
int port = uriComponents.getPort();
if (port > -1) {
return String.format("%s:%d", host, port);
}
return host;
}
return hostNameOverride;
}
}
HostNameProvider.java:
package com.zhibi.xiuba.configers;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.web.util.UrlPathHelper;
import javax.servlet.http.HttpServletRequest;
import static org.springframework.util.StringUtils.hasText;
import static org.springframework.web.servlet.support.ServletUriComponentsBuilder.fromContextPath;
public class HostNameProvider {
public HostNameProvider() {
throw new UnsupportedOperationException();
}
static UriComponents componentsFrom(
HttpServletRequest request,
String basePath) {
ServletUriComponentsBuilder builder = fromServletMapping(request, basePath);
UriComponents components = UriComponentsBuilder.fromHttpRequest(
new ServletServerHttpRequest(request))
.build();
String host = components.getHost();
if (!hasText(host)) {
return builder.build();
}
builder.host(host);
builder.port(components.getPort());
return builder.build();
}
private static ServletUriComponentsBuilder fromServletMapping(
HttpServletRequest request,
String basePath) {
ServletUriComponentsBuilder builder = fromContextPath(request);
builder.replacePath(prependForwardedPrefix(request, basePath));
if (hasText(new UrlPathHelper().getPathWithinServletMapping(request))) {
builder.path(request.getServletPath());
}
return builder;
}
private static String prependForwardedPrefix(
HttpServletRequest request,
String path) {
String prefix = request.getHeader("X-Forwarded-Prefix");
if (prefix != null) {
return prefix + path;
} else {
return path;
}
}
}
* 7、访问
访问api-gateway微服务的swagger-ui,通过此服务的ui间接访问其他服务的接口文档数据
http://{ip}:{port}/swagger-ui.html