先建一个空项目作为父项目,用于管理依赖版本
建立相应子空项目
- 引相应依赖,自己建包、主启动类、配置文件,*
- 在主启动类上加相应注解,如
@EnableEurekaServer
- 然后开发,干自己该干的事情
注意:服务的名称 必须唯一(同一集群服务名要一样),不能出现下划线,可通过如下配置:spring.application.name=xxxxxx
指定服务名称
搭建父项目
```java 4.0.0
<groupId>cn.ccb</groupId>
<artifactId>springcloudparent</artifactId>
<version>1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.0</version>
<relativePath/>
</parent>
<packaging>pom</packaging>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR6</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
```
Eureka
- 依赖
```properties
服务端依赖
org.springframework.boot spring-boot-starter-web
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
```
```properties
客户端依赖
org.springframework.boot spring-boot-starter-web
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
```
自我保护:某一时刻一个微服务不可用了,不会被立刻清除
集群,同一个服务集群,应用服务名要一样
- 配置文件
```properties
服务端
server.port=8761
spring.application.name=eurekaserver eureka.client.service-url.defaultZone=http://localhost:8761/eureka,http://localhost:8762/eureka,http://localhost:8763/eureka
不再将自己同时作为客户端进行注册
eureka.client.register-with-eureka=false
关闭作为客户端时从eureka server获取服务信息
eureka.client.fetch-registry=false
关闭自我保护
eureka.server.enable-self-preservation=false
超时3s自动清除
eureka.server.eviction-interval-timer-in-ms=3000 ```
```properties
客服端
server.port=8888 spring.application.name=eurekaclient
eureka 注册中心地址
eureka.client.service-url.defaultZone=http://localhost:8761/eureka,http://localhost:8762/eureka,http://localhost:8763/eureka ```
Consul
服务端
- 下载服务端软件: https://www.consul.io/downloads
```markdown
查看版本号
- consul -v
以开发模式方式启动
- consul agent -dev
访问
- http://localhost:8500 ```
客户端
- 引入依赖
yml <dependencies> <dependency> # 服务发现 <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> <dependency> # 健康检查的 <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> # 微服务基于springboot <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
主启动类加注解,
@EnableDiscoveryClient
,通用注解,针对zookeeper,consul,nacos
编写配置
```properties server.port=8889 spring.application.name=consulclient
注册consul服务的主机
spring.cloud.consul.host=localhost
注册consul服务的端口号
spring.cloud.consul.port=8500
关闭consu了服务的健康检查[不推荐]
spring.cloud.consul.discovery.register-health-check=true
指定注册的服务名称 默认就是应用名
spring.cloud.consul.discovery.service-name=${spring.application.name} ```
Ribbon
在RestTemplate的基础上进行服务调用,使用RestTemplate+Ribbon。RestTemplate最大缺点是调用路径写死
- 引入依赖
java <!--引入ribbon依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency>
快速入门
调用端
```java @Configuration public class BaseConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
} ```
```java @RestController @Slf4j public class UserController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/user/findAll")
public String findAll(){
String res = restTemplate.getForObject("http://orders/product/findAll", String.class); // 服务名
return res;
}
} ```
被调用端
```java @RestController @Slf4j public class ProductController {
@GetMapping("/product/findAll")
public String findAll(){
return "OK";
}
} ```
负载均衡策略
- RoundRobinRule:轮训策略,按顺序循环选择 Server
- RandomRule:随机策略,随机选择 Server
- WeightedResponseTimeRule:响应时间加权策略,响应时间越快 服务权重越大 被选中的概率越高
- RetryRule:重试策略,先轮训,如果获取失败则在指定时间内进行重试,获取可用的服务
- BestAviableRule:先过滤掉多次访问故障的服务,然后选择一个并发量最小的服务
- AvailabilityFilteringRule :可用过滤策略,先过滤掉多次访问故障和并发连接数量超过阈值的服务,然后对剩下的轮训
修改默认的负载均衡策略(调用端)
properties 被调用服务id.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
如:
```properties server.port=9999
spring.application.name=users
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
orders.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule ```
Feign
SpringCloud Feign在Hoxton.M2 RELEASED版本之后不再使用ribbon,而是使用spring-cloud-loadbalancer,所以在不引入spring-cloud-loadbalancer情况下会报错
快速入门
调用端
引入依赖
```properties org.springframework.cloud spring-cloud-starter-openfeign
org.springframework.cloud spring-cloud-starter-loadbalancer ```
入口类加注解,开启Feign支持
```java @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class CategoryApplication { public static void main(String[] args) { SpringApplication.run(CategoryApplication.class,args); }
} ```
创建一个服务调用接口
```java @FeignClient(value = "product") // 被调用服务名称 public interface ProductClient {
@GetMapping("/product") // 要调用服务的接口 public String product();
} ```
controller中使用
```java @RestController public class UserController {
@Autowired private ProductClient productClient;
@GetMapping("/service") public String find(){ return productClient.product(); // 调用远程服务 } } ```
被调用端
```java @Slf4j @RestController public class ProductController {
@GetMapping("/product")
public String product(){
return "product ok";
}
} ```
超时时间
Feign在进行服务调用时,要求服务提供方必须在1s内返回,如果超过1s没有返回则直接报错
```properties feign.client.config.PRODUCTS.connectTimeout=5000 #配置指定服务连接超时 feign.client.config.PRODUCTS.readTimeout=5000 #配置指定服务等待超时
feign.client.config.default.connectTimeout=5000 #配置所有服务连接超时
feign.client.config.default.readTimeout=5000 #配置所有服务等待超时
```
日志展示
feign在调用时默认不输出详细日志,需要自己开启日志展示
```properties
feign.client.config.default.loggerLevel=full # 全局开启服务日志展示
logging.level.cn.ccb.feignclients=debug # 指定给哪些调用对象记录日志
feign.client.config.PRODUCTS.loggerLevel=full # 指定日志展示的级别 ```
loggerLevel的值:
- NONE 不记录任何日志
- BASIC 仅仅记录请求方法,url,响应状态代码及执行时间
- HEADERS 记录Basic级别的基础上,记录请求和响应的header
- FULL 记录请求和响应的header,body和元数据
完整配置文件
```properties server.port=8787
spring.application.name=category
注册consul服务的主机
spring.cloud.consul.host=localhost
注册consul服务的端口号
spring.cloud.consul.port=8500
product.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
配置指定服务连接超时
feign.client.config.product.connectTimeout=5000
配置指定服务等待超时
feign.client.config.product.readTimeout=5000
开启openfeign请求调用的日志展示
feign.client.config.product.loggerLevel=full
logging.level.cn.ccb.feignclients=debug ```
Hystrix
Hystrix 用来保护微服务系统 实现 服务降级 服务熔断
- 引入依赖
java <!--引入hystrix--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
- 主启动类开启注解
```java @SpringBootApplication @EnableDiscoveryClient @EnableCircuitBreaker //用来开启断路器 public class HystrixApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixApplication.class,args);
}
} ```
服务熔断
全局熔断
```java @RestController @DefaultProperties(defaultFallback = "global_FallbackMethod") public class DemoController {
@GetMapping("/demo") @HystrixCommand // 使用全局熔断处理 public String demo1(Integer id){ int tmp=10/0; return "商品1服务正常"; }
public String global_FallbackMethod(){ return "网络异常:全局处理"; } } ```
指定方法熔断
```java @RestController public class DemoController {
@GetMapping("/demo2") //使用指定的方法熔断,请求参数和返回值一样
@HystrixCommand(fallbackMethod = "testBreakFall") public String demo2(Integer id){ int tmp=10/0; return "商品2服务正常"; }public String testBreakFall(Integer id){ return "商品2服务不可用!"; } } ```
断路器打开条件
```markdown
- 1、 当满足一定的阀值的时候(默认10秒内超过20个请求次数)
- 2、 当失败率达到一定的时候(默认10秒内超过50%的请求失败)
- 3、 到达以上阀值,断路器将会开启
- 4、 当开启的时候,所有请求都不会进行转发
- 5、 一段时间之后(默认是5秒),这个时候断路器是半开状态,会让其中一个请求进行转发。如果成功,断路器会关闭,若失败,继续开启。重复4和5。 ```
服务降级
- 开启openfeign支持服务降级
java feign.hystrix.enabled=true
- 在调用接口中加入Hytrix
```java @FeignClient(value = "product",fallback = ProductFallBack.class) public interface ProductClient {
@GetMapping("/product/hystrix")
String test(@RequestParam("name") String name);
} ```
- 开发fallback处理类
java @Component public class ProductFallBack implements ProductClient { @Override public String test(String name) { return "我是客户端的Hystrix服务实现!!!"; } }
Sentinel
Sentinel提供了两个服务组件:控制台(Dashboard)和核心库,控制台用来监控流量调用情况;核心库用来实现服务熔断降级
Dashboard
下载官方提供的Sentinel Dashboard的jar包:https://github.com/alibaba/Sentinel/releases,本身是个springboot项目(默认端口是8080
),直接通过java命令启动:
java -Dserver.port=9191 -jar sentinel-dashboard-1.7.2.jar
访问localhost:9191,用户名密码都是sentinel
,微服务被首次调用的时候向Dashboard发送心跳信息
创建项目
引入依赖
java <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
修改配置
properties server.port=9642 spring.cloud.nacos.server-addr=localhost:8848 spring.cloud.sentinel.transport.dashboard=localhost:9191 # 配置dashboard连接地址
主启动加注解
java @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class SentinelApplication { public static void main(String[] args) { SpringApplication.run(SentinelApplication.class,args); } }
服务熔断
```java @GetMapping("/user/info") @SentinelResource(value = "app", fallback = "innerBlock") // @SentinelResource(value = "app", fallback = "block", fallbackClass = MyBlockHandler.class) public String getUserInfo() throws InterruptedException { Thread.sleep(800); System.out.println("UserController::/userInfo"); return "{name:ccb, age: 22}"; }
public String innerBlock(){ // 返回类型、参数与原方法一致 return "innerBlock...限流"; } ```
java public class MyBlockHandler{ // 方法必须用static修饰 public static String block() throws InterruptedException { // 返回类型、参数与原方法一致 return "UserController被限流了.."; } }
Feign熔断
```java @FeignClient(contextId = "remoteUserService", value = "system_service", fallbackFactory = RemoteUserFallbackFactory.class) public interface RemoteUserService { @GetMapping("/user/info/{username}") public R getUserInfo(@PathVariable("username") String username);
@PostMapping("/user/register")
public R<Boolean> registerUserInfo(@RequestBody SysUser sysUser);
} ```
```java // 用户服务降级处理 @Component public class RemoteUserFallbackFactory implements FallbackFactory { @Override public RemoteUserService create(Throwable throwable) { return new RemoteUserService() { @Override public R getUserInfo(String username) { return R.fail("获取用户失败:" + throwable.getMessage()); }
@Override
public R<Boolean> registerUserInfo(SysUser sysUser)
{
return R.fail("注册用户失败:" + throwable.getMessage());
}
}
}
} ```
Gateway
作用:路由转发 + 过滤器
RoutePredicateFactory GatewayFilterFactory GlobalFilter
使用
- 引入依赖,consul里面集成了ribbon,网关可以实现转发的负载均衡
- gateway使用了Flux异步非阻塞模型,不要添加spring-boot-starter-web,会冲突
```java org.springframework.cloud spring-cloud-starter-gateway
org.springframework.boot spring-boot-starter-actuator
org.springframework.cloud spring-cloud-starter-consul-discovery ```
主启动类:@EnableDiscoveryClient
配置路由规则
```yaml spring: application: name: gateway
cloud: consul: host: localhost # 向注册中心注册 port: 8500
gateway:
routes:
- id: route_product
uri: lb://product # 2.转发到对应服务,product为服务名字
predicates: # 断言
- Path=/product/**,/list # 1.匹配到对应路径,可以配置多个
- id: route_category
uri: lb://category
predicates:
- Path=/category
server: port: 9500 ```
断言
用来匹配满足指定规则的路由
```java - After=2020-07-21T11:33:33.993+08:00[Asia/Shanghai] // 指定日期之后的请求进行路由
Before=2020-07-21T11:33:33.993+08:00[Asia/Shanghai] // 指定日期之前的请求进行路由
Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
Cookie=username,chenyn // 基于指定cookie的请求进行路由
Cookie=username,[A-Za-z0-9]+ // 基于指定cookie的请求进行路由
Header=X-Request-Id, \d+ // 基于请求头中的指定属性的正则进行路由(这里全是整数)
Method=GET,POST // 基于指定的请求方式请求进行路由 ```
过滤器
客户端请求各个服务时,每个服务都需要做相同的事情,比如鉴权、限流、日志输出等,此时可以在网关中配置路由
- 常见过滤器:
java - AddRequestHeader=X-Request-red, blue // 增加请求头的filter - AddRequestParameter=red, blue // 增加请求参数的filterr - AddResponseHeader=X-Response-Red, AAA // 增加响应头filter - PrefixPath=/emp // 增加前缀的filter,在请求的路径前(端口号后)加前缀 - StripPrefix=1 // 去掉前缀的filter,数字代表去掉几级前缀,例:/emp/product/ ====> /product
StripPrefix
过滤器使用
```yaml spring: application: name: gateway
cloud: consul: host: localhost port: 8500
gateway:
routes:
- id: route_category
uri: lb://category
predicates:
- Path=/xxxxxxxx/**
filters:
- StripPrefix=1
```
访问路径:http://localhost:9500/xxx/category 匹配到指定路由后会将前缀去掉一级(/xxx/category ==> /category ),然后负载均衡找到对应服务进行转发
暴露路由规则
yaml management: endpoints: web: exposure: exclude: "*"
通过访问该地址查看 http://localhost:9500/actuator/gateway/routes,改成网关的端口,效果如下