[toc]
SpringCloud父类
在pom.xml
中添加spring-cloud-dependencies
统一管理版本:
org.springframework.cloud
spring-cloud-dependencies
Finchley.RC1
pom
import
服务注册与发现中心Euerka
Spring Cloud Netflix 的 Eureka,Eureka 是一个服务注册和发现模块。
2.1. 引入依赖
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
服务端配置
配置application.yml
在默认情况下 Erureka Server 也是一个 Eureka Client ,必须要指定一个 Server。
spring:
application:
name: hello-spring-cloud-eureka
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
通过 eureka.client.registerWithEureka:false
和 fetchRegistry:false
来表明自己是一个 Eureka Server。
服务端注解
启动一个服务注册中心,只需要一个注解 @EnableEurekaServer
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
客户端配置
配置application.yml
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
客户端注解
通过注解 @EnableEurekaClient
表明自己是一个 Eureka Client。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class ServiceAdminApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceAdminApplication.class, args);
}
}
负载均衡客户端Ribbon
Ribbon 是一个负载均衡客户端,可以很好的控制 http 和 tcp 的一些行为。
3.1. 引入依赖
org.springframework.cloud
spring-cloud-starter-netflix-ribbon
发现服务配置
通过 @EnableDiscoveryClient 注解获取注册到服务中心的服务。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class WebAdminRibbonApplication {
public static void main(String[] args) {
SpringApplication.run(WebAdminRibbonApplication.class, args);
}
}
负载均衡客户端Feign
Feign 是一个声明式的伪 Http 客户端, 支持可插拔的编码器和解码器。Feign 默认集成了 Ribbon,并和 Eureka 结合,默认实现了负载均衡的效果
- Feign 采用的是基于接口的注解
- Feign 整合了 ribbon
引入依赖
org.springframework.cloud
spring-cloud-starter-openfeign
开启Feign注解
通过 @EnableFeignClients
注解开启 Feign 功能。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class WebAdminFeignApplication {
public static void main(String[] args) {
SpringApplication.run(WebAdminFeignApplication.class, args);
}
}
熔断器Hystrix
Netflix 开源了 Hystrix 组件,实现了熔断器模式,Spring Cloud 对这一组件进行了整合。
引入依赖
org.springframework.cloud
spring-cloud-starter-netflix-hystrix
开启熔断器配置
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
@SpringBootApplication
@EnableCircuitBreaker
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
使用注解@HystrixCommand
@HystrixCommand(fallbackMethod = "fallbackEcho")
@GetMapping(value = "/echo/app/name")
public String echo() {
TbUser tbUser = new TbUser();
tbUser.setUsername("adfad");
TbUser serviceOne = userService.findOne(tbUser);
return "test";
}
public String fallbackEcho(){
return "error";
}
Feign中使用熔断器配置
Feign 是自带熔断器的,但默认是关闭的。需要在配置文件中配置打开它,在application.yml
配置文件增加以下代码:
feign:
hystrix:
enabled: true
熔断器Hystrix仪表盘监控
引入依赖
org.springframework.cloud
spring-cloud-starter-netflix-hystrix-dashboard
增加仪表盘注解
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix
@EnableHystrixDashboard
public class WebAdminRibbonApplication {
public static void main(String[] args) {
SpringApplication.run(WebAdminRibbonApplication.class, args);
}
}
访问路径:http://ip:port/hystrix
创建 hystrix.stream 的 Servlet 配置
Spring Boot 2.x 版本开启 Hystrix Dashboard 与 Spring Boot 1.x 的方式略有不同,需要增加一个 HystrixMetricsStreamServlet
的配置,代码如下:
import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class HystrixDashboardConfiguration {
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
}
路由网关Zuul
Zuul 的主要功能是路由转发和过滤器。
添加依赖
org.springframework.cloud
spring-cloud-starter-netflix-zuul
开启路由网关
增加 @EnableZuulProxy 注解开启 Zuul 功能。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
配置application.yml
zuul:
routes:
api-a:
path: /api/a/**
serviceId: hello-spring-cloud-web-admin-ribbon
api-b:
path: /api/b/**
serviceId: hello-spring-cloud-web-admin-feign
- 以 /api/a 开头的请求都转发给 hello-spring-cloud-web-admin-ribbon 服务
- 以 /api/b 开头的请求都转发给 hello-spring-cloud-web-admin-feign 服务
配置中心Config
在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。在 Spring Cloud 中,有分布式配置中心组件 Spring Cloud Config。
服务端配置
添加依赖
org.springframework.cloud
spring-cloud-config-server
开启配置中心服务
通过 @EnableConfigServer
注解,开启配置服务器功能。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
@SpringBootApplication
@EnableConfigServer
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class, args);
}
}
配置application.yml
spring:
application:
name: hello-spring-cloud-config
cloud:
config:
label: master
server:
git:
uri: https://github.com/topsale/spring-cloud-config
search-paths: respo
username:
password:
server:
port: 8888
- spring.cloud.config.label:配置仓库的分支
- spring.cloud.config.server.git.uri:配置 Git 仓库地址(GitHub、GitLab、码云 ...)
- spring.cloud.config.server.git.search-paths:配置仓库路径(存放配置文件的目录)
- spring.cloud.config.server.git.username:访问 Git 仓库的账号
- spring.cloud.config.server.git.password:访问 Git 仓库的密码
服务端配置
添加依赖
org.springframework.cloud
spring-cloud-starter-config
配置application.yml
spring:
application:
name: hello-spring-cloud-config-client
cloud:
config:
uri: http://localhost:8888
name: config-client
label: master
profile: dev
相关配置说明,如下:
- spring.cloud.config.uri:配置服务中心的网址
- spring.cloud.config.name:配置文件名称的前缀
- spring.cloud.config.label:配置仓库的分支
- spring.cloud.config.profile:配置文件的环境标识
dev:表示开发环境
test:表示测试环境
prod:表示生产环境
注意事项:
配置服务器的默认端口为 8888,如果修改了默认端口,则客户端项目就不能在 application.yml 或 application.properties 中配置 spring.cloud.config.uri,必须在 bootstrap.yml 或是 bootstrap.properties 中配置,原因是 bootstrap 开头的配置文件会被优先加载和配置。
链路追踪ZipKin
ZipKin 是一个开放源代码的分布式跟踪系统,由 Twitter 公司开源,它致力于收集服务的定时数据,以解决微服务架构中的延迟问题,包括数据的收集、存储、查找和展现。
服务端配置
添加依赖
io.zipkin.java
zipkin
2.10.1
io.zipkin.java
zipkin-server
2.10.1
io.zipkin.java
zipkin-autoconfigure-ui
2.10.1
开启服务配置
通过 @EnableZipkinServer 注解开启 Zipkin Server 功能。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import zipkin.server.internal.EnableZipkinServer;
@SpringBootApplication
@EnableZipkinServer
public class ZipKinApplication {
public static void main(String[] args) {
SpringApplication.run(ZipKinApplication.class, args);
}
}
配置application.yml
设置端口号为:9411,该端口号为 Zipkin Server 的默认端口号。
spring:
application:
name: hello-spring-cloud-zipkin
server:
port: 9411
management:
metrics:
web:
server:
auto-time-requests: false
客户端端配置
添加依赖
org.springframework.cloud
spring-cloud-starter-zipkin
配置application.yml
spring:
zipkin:
base-url: http://localhost:9411
路由网关Gateway
Spring Cloud Gateway 是 Spring 官方基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,Spring Cloud Gateway 旨在为微服务架构提供一种简单而有效的统一的 API 路由管理方式。
添加依赖
org.springframework.cloud
spring-cloud-starter-gateway
- Spring Cloud Gateway 不使用 Web 作为服务器,而是 使用 WebFlux 作为服务器,Gateway 项目已经依赖了 starter-webflux,所以这里 千万不要依赖 starter-web
- 由于过滤器等功能依然需要 Servlet 支持,故这里还需要依赖 javax.servlet:javax.servlet-api
配置application.yml
spring:
application:
# 应用名称
name: spring-gateway
cloud:
nacos:
discovery:
server-addr: 192.168.25.138:8848
# 路由网关配置
gateway:
# 设置与服务注册发现组件结合,这样可以采用服务名的路由策略
discovery:
locator:
enabled: true
# 配置路由规则
routes:
# 采用自定义路由 ID(有固定用法,不同的 id 有不同的功能,详见:https://cloud.spring.io/spring-cloud-gateway/2.0.x/single/spring-cloud-gateway.html#gateway-route-filters)
- id: NACOS-CONSUMER
# 采用 LoadBalanceClient 方式请求,以 lb:// 开头,后面的是注册在 Nacos 上的服务名
uri: lb://nacos-consumer
# Predicate 翻译过来是“谓词”的意思,必须,主要作用是匹配用户的请求,有很多种用法
predicates:
# Method 方法谓词,这里是匹配 GET 和 POST 请求
- Method=GET,POST
- Path=/api/consumer/**
filters:
# 前缀过滤,默认配置下,我们的请求路径是 http://localhost:9000/nacos-consumer/** 这时会路由到指定的服务
# 此处配置去掉 1 个路径前缀,再配置上面的 Path=/api/**,就能按照 http://localhost:9000/api/** 的方式访问了
- StripPrefix=1
- id: NACOS-CONSUMER-FEIGN
uri: lb://nacos-consumer-feign
predicates:
- Method=GET,POST
- Path=/api/feign/**
filters:
- StripPrefix=1
server:
port: 9000
# 配置日志级别,方别调试
logging:
level:
org.springframework.cloud.gateway: debug
解决跨域问题配置
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.gateway.discovery.DiscoveryClientRouteDefinitionLocator;
import org.springframework.cloud.gateway.discovery.DiscoveryLocatorProperties;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.codec.support.DefaultServerCodecConfigurer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.cors.reactive.CorsUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayApplication {
// ----------------------------- 解决跨域 Begin -----------------------------
private static final String ALL = "*";
private static final String MAX_AGE = "18000L";
@Bean
public RouteDefinitionLocator discoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) {
return new DiscoveryClientRouteDefinitionLocator(discoveryClient, properties);
}
@Bean
public ServerCodecConfigurer serverCodecConfigurer() {
return new DefaultServerCodecConfigurer();
}
@Bean
public WebFilter corsFilter() {
return (ServerWebExchange ctx, WebFilterChain chain) -> {
ServerHttpRequest request = ctx.getRequest();
if (!CorsUtils.isCorsRequest(request)) {
return chain.filter(ctx);
}
HttpHeaders requestHeaders = request.getHeaders();
ServerHttpResponse response = ctx.getResponse();
HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();
HttpHeaders headers = response.getHeaders();
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());
headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders.getAccessControlRequestHeaders());
if (requestMethod != null) {
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());
}
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, ALL);
headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, MAX_AGE);
if (request.getMethod() == HttpMethod.OPTIONS) {
response.setStatusCode(HttpStatus.OK);
return Mono.empty();
}
return chain.filter(ctx);
};
}
// ----------------------------- 解决跨域 End -----------------------------
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}