框架选型
项目结构
项目搭建
父工程 cloud_init 依赖管理
pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mote.cloud</groupId>
<artifactId>cloud_init</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>cloud_init</name>
<packaging>pom</packaging>
<!--统一管理jar包和版本-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<junit.version>4.12</junit.version>
<log4j.version>1.2.17</log4j.version>
<lombok.version>1.16.18</lombok.version>
</properties>
<dependencyManagement>
<dependencies>
<!--spring boot 2.2.2-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<!-- log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
公共工程 cloud_init_common 定义公共实体类
pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.mote.cloud</groupId>
<artifactId>cloud_init</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.mote.cloud</groupId>
<artifactId>cloud_init_common</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>cloud_init_common</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
实体类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> {
private int code;
private String message;
private T date;
public CommonResult(int code, String message) {
this(code, message, null);
}
}
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class Payment {
private long id;
private String serial;
}
模块工程 cloud_init_provider_payment8001 提供者8001
pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>cloud_init</artifactId>
<groupId>com.mote.cloud</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.mote.cloud</groupId>
<artifactId>cloud_init_provider_payment8001</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>cloud_init_provider_payment8001</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>com.mote.cloud</groupId>
<artifactId>cloud_init_common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
controller
@RestController
public class PaymentController {
@GetMapping("/pay/get/{id}")
public CommonResult get(@PathVariable("id") int id) {
Payment payment = new Payment();
payment.setId(8081).setSerial("i am 8081 provider");
return new CommonResult(200, "查询成功", payment);
}
}
yml
server:
port: 8001
spring:
application:
name: cloud-payment-service
访问
模块工程 cloud_init_provider_payment8002 提供者8002
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>cloud_init</artifactId>
<groupId>com.mote.cloud</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.mote.cloud</groupId>
<artifactId>cloud_init_provider_payment8002</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>cloud_init_provider_payment8001</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>com.mote.cloud</groupId>
<artifactId>cloud_init_common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
controller
@RestController
public class PaymentController {
@GetMapping("/pay/get/{id}")
public CommonResult get(@PathVariable("id") int id) {
Payment payment = new Payment();
payment.setId(8082).setSerial("i am 8082 provider");
return new CommonResult(200, "查询成功", payment);
}
}
yml
server:
port: 8002
spring:
application:
name: cloud-payment-service
访问
模块工程 cloud_init_consumer_order80 消费者80
yml(代码暂时先不写)
server:
port: 80
spring:
application:
name: cloud-order-consumer
注册中心nacos接入
nacos下载启动
下载地址:https://github.com/alibaba/nacos/releases
windows下载zip,然后解压,免安装,点击bin/startup.cmd命令启动nacos
访问nacos控制台,用户名和密码默认都是nacos
创建命名空间
模块8001/8002提供者模块接入nacos
引入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
模块8001/8002 yml配置
spring:
application:
name: cloud-payment-service
cloud:
nacos:
discovery:
namespace: cloud-dev
server-addr: 127.0.0.1:8848
group: group-test
模块8001/8002启动类添加注解 @EnableDiscoveryClient
@SpringBootApplication
@EnableDiscoveryClient
public class Application8002 {
public static void main(String[] args) {
SpringApplication.run(Application8002.class, args);
}
}
启动模块8001/8002,访问nacos,察看实例
服务调用openfeign接入
模块consumer80引入openfeign依赖和nacos依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
模块consumer80 yml
spring:
application:
name: cloud-order-consumer
cloud:
nacos:
discovery:
namespace: cloud-dev
server-addr: 127.0.0.1:8848
group: group-test
register-enabled: false #只发现,不注册
模块consumer80 定义PaymentFeignService(interface类型),里面的方法名称和参数保持和调用的接口一致
@Component
@FeignClient(value = "cloud-payment-service")
public interface PaymentFeignService {
@GetMapping("/pay/get/{id}")
CommonResult get(@PathVariable("id") int id);
}
模块consumer80 定义OrderController
@RestController
@Slf4j
public class OrderController {
@Autowired
private PaymentFeignService paymentFeignService;
@GetMapping("/order/get/{id}")
public CommonResult create(@PathVariable("id") int id) {
return paymentFeignService.get(id);
}
}
模块consumer80启动类新增@EnableFeignClients和@EnableDiscoveryClient注解
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class Application80 {
public static void main(String[] args) {
SpringApplication.run(Application80.class, args);
}
}
启动Consumer80,连续访问接口,观察负载,默认是轮询策略
服务网关gateway接入
新建网关模块 cloud_init_gateway_payment8003
pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>cloud_init</artifactId>
<groupId>com.mote.cloud</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.mote.cloud</groupId>
<artifactId>cloud_init_gateway_payment8003</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
</project>
yml
server:
port: 8003
spring:
application:
name: cloud-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
group: group-test
gateway:
discovery:
locator:
enabled: true
routes:
- id: payment_routh #id 随便取值,保持唯一
uri: lb://cloud-payment-service #代理的服务名
predicates:
- Path=/gateway/pay/get/** #代理路径
filters:
- StripPrefix=1 #代理路径的过滤操作,网关真实访问路径/gateway/pay/get/**,转发到服务的路径是/pay/get/**
网关过滤器:用于鉴权、限流等
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class SafeFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// TODO 鉴权和限流
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
启动类
@SpringBootApplication
@EnableDiscoveryClient
public class Application8003 {
public static void main(String[] args) {
SpringApplication.run(Application8003.class, args);
}
}
启动访问
将模块consumer80项目中openfeign调用的服务改成网关服务
重启模块consumer80,访问
配置中心nacos接入
已consumer80为例,接入nacos配置中心
添加依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
修改配置文件名称application.yml为bootstrap.yml(bootstrap优先级高于application),并添加内容如下
spring:
profiles:
active: dev
application:
name: cloud-order-consumer
cloud:
nacos:
discovery:
namespace: cloud-dev #命名空间
server-addr: 127.0.0.1:8848
group: group-test
register-enabled: false #只发现,不注册
config:
namespace: cloud-dev #命名空间
server-addr: 127.0.0.1:8848
file-extension: yaml #文件类型
group: group-test
在 Nacos中,dataId的完整格式如下:
prefix−prefix−{spring-profile.active}.${file-extension}
- prefix默认为spring.application.name的值,也可以通过配置项spring.cloud.nacos.config.prefix来配置。
- spring.profile.active即为当前环境对应的 profile,详情可以参考 Spring Boot文档。注意:当spring.profile.active为空时,对应的连接符 - 也将不存在,datald 的拼接格式变成prefix.prefix.{file-extension}
- file-exetension为配置内容的数据格式,可以通过配置项spring .cloud.nacos.config.file-extension来配置。目前只支持properties和yaml类型。
- 通过Spring Cloud 原生注解@RefreshScope实现配置自动更新
在nacos控制台新建配置文件
写一个TestController用于配置测试
@RestController
@Slf4j
@RefreshScope // 实时刷新配置
public class TestController {
@Value("${test}")
private String test;
@GetMapping("/order/test")
public String test() {
return test;
}
}
启动,访问测试
服务限流、降级sentinel接入
下载启动sentinel
下载地址:https://github.com/alibaba/Sentinel/releases
下载jar包到本地,使用java -jar xxx运行,访问sentinel控制台,地址:127.0.0.1:8080,默认端口8080,用户名和密码默认sentinel
模块provider8001/8002改造(以8081为例,接入sentinel做限流)
引入pom
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
修改PaymentController
@RestController
public class PaymentController {
@GetMapping("/pay/get/{id}")
@SentinelResource(value = "pay-get", blockHandler = "blockHandler", fallback = "fallbackHandler")
public CommonResult get(@PathVariable("id") int id) {
// int i = 1/0;
Payment payment = new Payment();
payment.setId(8081).setSerial("i am 8081 provider");
return new CommonResult(200, "查询成功", payment);
}
/**
* 针对BlockException做处理,也就是触发了sentinel配置的策略后的兜底方法
*/
public CommonResult blockHandler(int id, BlockException exception) {
return new CommonResult(200, "blockHandler");
}
/**
* 针对程序异常做处理,程序异常直接走这个方法
*/
public CommonResult fallbackHandler(int id) {
return new CommonResult(200, "fallbackHandler");
}
}
yml
spring:
application:
name: cloud-payment-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
group: group-test
sentinel:
transport:
dashboard: 127.0.0.1:8080
查看sentinel控制台,观察服务是否注册,若没有,先访问/pay/get/{id}接口,再查看
配置流控规则
浏览器访问接口测试,一秒内第3次往后的请求会被拒绝,直接进入兜底方法
配置sentinel配置持久化,因为sentinel配置只是暂时的,项目重启,配置好的策略就不见了,所以需要把策略持久化起来,解决方案是集成nacos,持久化到nacos中。
引入pom
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
yml
spring:
application:
name: cloud-payment-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
group: group-test
sentinel:
transport:
dashboard: 127.0.0.1:8080
datasource:
ds1:
nacos:
server-addr: 127.0.0.1:8848
dataId: sentinel-limit.json
groupId: group-test
data-type: json
rule_type: flow
配置nacos
resource:资源名称
limitApp:来源应用
grade:阀值类型,0:线程数,1:QPS
count:单机阀值
strategy:流控模式,0:直接,1:关联,2:链路
controlBehavior:流控效果,0:快速失败,1:warmUp,2:排队等待
clusterMode:是否集群
重启模块provider8081观察sentinel或者访问接口,如果限流存在则持久化成功
服务调用openFeign配置sentinel
改造consumer80
pom添加sentinel依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
配置openfeign支持sentinel
feign:
sentinel:
enabled: true
配置兜底方法的两种方式
方式一:实现服务调用类,实现兜底方法,注解指定
public class PaymentServiceFallback implements PaymentFeignService {
@Override
public CommonResult get(int id) {
return new CommonResult(201,"调用失败");
}
}
@Component
@FeignClient(value = "cloud-gateway", fallback = PaymentServiceFallback.class)
public interface PaymentFeignService {
@GetMapping("/gateway/pay/get/{id}")
CommonResult get(@PathVariable("id") int id);
}
方式二:实现FallbackFactory,注解指定
public class PaymentServiceFallbackFactory implements FallbackFactory<PaymentFeignService> {
@Override
public PaymentFeignService create(Throwable throwable) {
return new PaymentFeignService() {
@Override
public CommonResult get(int id) {
return new CommonResult(201,"服务调用失败");
}
};
}
}
@Component
@FeignClient(value = "cloud-gateway", fallbackFactory = PaymentServiceFallbackFactory.class)
public interface PaymentFeignService {
@GetMapping("/gateway/pay/get/{id}")
CommonResult get(@PathVariable("id") int id);
}