
Eureka 服务注册与发现
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
<groupId>com.imooc</groupId> <artifactId>eureka</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging>
<name>eureka</name> <description>Demo project for Spring Boot</description>
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.2.RELEASE</version> <relativePath/> </parent>
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> </properties>
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
</project>
|
application.yml
1 2 3
| spring: profiles: active: eureka2
|
application-eureka1.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| eureka: instance: hostname: eureka1 client: service-url: defaultZone: http://eureka2:8762/eureka/ register-with-eureka: false server: enable-self-preservation: false eviction-interval-timer-in-ms: 5000 spring: application: name: eureka1
|
application-eureka2.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| eureka: instance: hostname: eureka2 client: service-url: defaultZone: http://eureka1:8761/eureka/ register-with-eureka: false server: enable-self-preservation: false eviction-interval-timer-in-ms: 3000 spring: application: name: eureka2
|
启动类EurekaApplication
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.imooc.eureka;
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); } }
|
Eureka控制台

应用模块改造 - product子项目
全局pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
<groupId>com.imooc</groupId> <artifactId>product</artifactId> <version>0.0.1-SNAPSHOT</version> <modules> <module>common</module> <module>server</module> <module>client</module> </modules> <packaging>pom</packaging>
<name>product</name> <description>Demo project for Spring Boot</description>
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.2.RELEASE</version> <relativePath/> </parent>
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> <product-common.version>0.0.1-SNAPSHOT</product-common.version> </properties>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.imooc</groupId> <artifactId>product-common</artifactId> <version>${product-common.version}</version> </dependency> </dependencies> </dependencyManagement>
</project>
|
server模块
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>product</artifactId> <groupId>com.imooc</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion>
<artifactId>product-server</artifactId>
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
<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> <scope>test</scope> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
<dependency> <groupId>com.imooc</groupId> <artifactId>product-common</artifactId> </dependency>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-client</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
|
application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| server: port: 8080
spring: application: name: product datasource: driver-class-name: com.mysql.jdbc.Driver username: root password: welcome123 url: jdbc:mysql://localhost:3306/sell?characterEncoding=utf-8&useSSL=false jpa: show-sql: true
eureka: client: service-url: defaultZone: http://eureka1:8761/eureka/
|
启动类 ProductApplication
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.imooc.product;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication @EnableDiscoveryClient public class ProductApplication {
public static void main(String[] args) { SpringApplication.run(ProductApplication.class, args); } }
|
common模块
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>product</artifactId> <groupId>com.imooc</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion>
<artifactId>product-common</artifactId>
<dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> </project>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.imooc.product.common;
import lombok.Data;
@Data public class DecreaseStockInput {
private String productId;
private Integer productQuantity;
public DecreaseStockInput() { }
public DecreaseStockInput(String productId, Integer productQuantity) { this.productId = productId; this.productQuantity = productQuantity; } }
|
ProductInfoOutput.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| package com.imooc.product.common;
import lombok.Data;
import java.math.BigDecimal;
@Data public class ProductInfoOutput {
private String productId;
private String productName;
private BigDecimal productPrice;
private Integer productStock;
private String productDescription;
private String productIcon;
private Integer productStatus;
private Integer categoryType; }
|
client模块
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>product</artifactId> <groupId>com.imooc</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion>
<artifactId>product-client</artifactId>
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> </dependency>
<dependency> <groupId>com.imooc</groupId> <artifactId>product-common</artifactId> </dependency> </dependencies> </project>
|
ProductClient.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| package com.imooc.product.client;
import com.imooc.product.common.DecreaseStockInput; import com.imooc.product.common.ProductInfoOutput; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
@FeignClient(name = "product", fallback = ProductClient.ProductClientFallback.class) public interface ProductClient {
@PostMapping("/product/listForOrder") List<ProductInfoOutput> listForOrder(@RequestBody List<String> productIdList);
@PostMapping("/product/decreaseStock") void decreaseStock(@RequestBody List<DecreaseStockInput> decreaseStockInputList);
@Component static class ProductClientFallback implements ProductClient {
@Override public List<ProductInfoOutput> listForOrder(List<String> productIdList) { return null; }
@Override public void decreaseStock(List<DecreaseStockInput> decreaseStockInputList) {
} } }
|
应用模块改造 - order子项目
全局pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
<groupId>com.imooc</groupId> <artifactId>order</artifactId> <version>0.0.1-SNAPSHOT</version> <modules> <module>client</module> <module>common</module> <module>server</module> </modules> <packaging>pom</packaging>
<name>order</name> <description>Demo project for Spring Boot</description>
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.2.RELEASE</version> <relativePath/> </parent>
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> <product-client.version>0.0.1-SNAPSHOT</product-client.version> <order-common.version>0.0.1-SNAPSHOT</order-common.version> </properties>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.imooc</groupId> <artifactId>product-client</artifactId> <version>${product-client.version}</version> </dependency> <dependency> <groupId>com.imooc</groupId> <artifactId>order-common</artifactId> <version>${order-common.version}</version> </dependency> </dependencies> </dependencyManagement>
</project>
|
common模块
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>order</artifactId> <groupId>com.imooc</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion>
<artifactId>order-common</artifactId>
<dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> </project>
|
client模块
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>order</artifactId> <groupId>com.imooc</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion>
<artifactId>order-client</artifactId>
</project>
|
server模块
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
| <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>order</artifactId> <groupId>com.imooc</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion>
<artifactId>order-server</artifactId>
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
<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> <scope>test</scope> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
<dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> </dependency>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
<dependency> <groupId>com.imooc</groupId> <artifactId>product-client</artifactId> </dependency>
<dependency> <groupId>com.imooc</groupId> <artifactId>order-common</artifactId> </dependency>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-client</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin</artifactId> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
|
bootstrap.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| spring: rabbitmq: host: 127.0.0.1 port: 5672 username: guest password: guest application: name: order cloud: config: discovery: enabled: true service-id: config profile: test eureka: client: service-url: defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
|
启动类 OrderApplication.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.imooc.order;
import org.springframework.boot.SpringApplication; import org.springframework.cloud.client.SpringCloudApplication; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.ComponentScan;
@EnableFeignClients(basePackages = "com.imooc.product.client")
@SpringCloudApplication @ComponentScan(basePackages = "com.imooc") public class OrderApplication {
public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } }
|
Config 配置中心
RabbitMQ
Docker镜像
1 2 3
| docker pull daocloud.io/library/rabbitmq:3.7-management
docker run -d --hostname my-rabbit -p 5672:5672 -p 15672:15672 daocloud.io/library/rabbitmq:3.7-management
|
RabbitMQ 控制台
http://localhost:15672/
guest/gutst

消息队列

pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.2.RELEASE</version> <relativePath/> </parent> <groupId>com.imooc</groupId> <artifactId>config</artifactId> <version>0.0.1-SNAPSHOT</version> <name>config</name> <description>Demo project for Spring Boot</description>
<properties> <java.version>1.8</java.version> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> </properties>
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency>
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> </dependency> </dependencies>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
</project>
|
application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| server: port: 8090 spring: rabbitmq: host: 127.0.0.1 port: 5672 username: guest password: guest application: name: config cloud: config: server: git: uri: git@github.com:wfg2513148/Spring-Cloud-Test.git username: wfgdlut@gmail.com password: UBVkUVNmv4xDqHgJyqyzctKy
eureka: client: service-url: defaultZone: http://eureka1:8761/eureka/,http://eureka2:8762/eureka/ management: endpoints: web: exposure: include: bus-refresh
|
配置类 GirlConfig.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.imooc.order.config;
import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.stereotype.Component;
@Data @Component @ConfigurationProperties("girl") @RefreshScope public class GirlConfig {
private String name;
private Integer age; }
|
GirlController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.imooc.order.controller;
import com.imooc.order.config.GirlConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
@RestController @RequestMapping("/girl") public class GirlController {
@Autowired private GirlConfig girlConfig;
@GetMapping("print") public String print() { return "name: " + girlConfig.getName() + ", age: " + girlConfig.getAge(); } }
|
启动类 ConfigApplication.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.imooc.config;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.config.server.EnableConfigServer; import org.springframework.cloud.context.config.annotation.RefreshScope;
@SpringBootApplication @EnableDiscoveryClient @EnableConfigServer @RefreshScope public class ConfigApplication {
public static void main(String[] args) { SpringApplication.run(ConfigApplication.class, args); }
}
|
热更新接口服务 - RefreshController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package com.imooc.config.controller;
import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.client.RestTemplate;
@Controller public class RefreshController {
@RequestMapping("/refresh") public void refresh() { RestTemplate restTemplate = new RestTemplate(); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.add(HttpHeaders.CONTENT_TYPE, "application/json"); HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(null, httpHeaders); ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity("http://localhost:8090/actuator/bus-refresh", request, String.class); } }
|
order-test.yml (Github repo)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| eureka: client: service-url: defaultZone: http://localhost:8762/eureka/ server: port: 8080 spring: application: name: order datasource: driver-class-name: com.mysql.jdbc.Driver jpa: show-sql: true password: welcome123 url: jdbc:mysql://localhost:3306/sell?characterEncoding=utf-8&useSSL=false username: root env: test16 girl: name: gloria age: 18
|
手动测试配置刷新
1
| curl -v -X POST http://localhost/actuator/bus-refresh
|
Github webhooks
测试Github的webhooks时,网上说是要配置到/monitor
这个地址上,实测发现会报错,这里重写了refresh服务的接口,采用get方式接收webhooks调用,然后再转成post方式去请求真实的配置刷新接口:http://kwang.natapp1.cc/refresh (natapp是个内网穿透工具)

测试配置热更新
修改Github上的yaml文件,无需重启任何服务,即可实现配置热刷新。


消息与异步
RabbitMQ
pom.xml
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>
|
product-dev.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| spring: datasource: driver-class-name: com.mysql.jdbc.Driver username: root password: welcome123 url: jdbc:mysql://localhost:3306/sell?characterEncoding=utf-8&useSSL=false jpa: show-sql: true rabbitmq: host: 127.0.0.1 port: 5672 username: guest password: guest redis: host: localhost port: 6379 logging: level: org: hibernate: SQL: DEBUG
|
ProductServiceImpl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| package com.imooc.product.service.impl;
import com.imooc.product.common.DecreaseStockInput; import com.imooc.product.common.ProductInfoOutput; import com.imooc.product.dataobject.ProductInfo; import com.imooc.product.enums.ProductStatusEnum; import com.imooc.product.enums.ResultEnum; import com.imooc.product.exception.ProductException; import com.imooc.product.repository.ProductInfoRepository; import com.imooc.product.service.ProductService; import com.imooc.product.utils.JsonUtil; import org.springframework.amqp.core.AmqpTemplate; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;
import javax.transaction.Transactional; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.stream.Collectors;
@Service public class ProductServiceImpl implements ProductService {
@Autowired private ProductInfoRepository productInfoRepository;
@Autowired private AmqpTemplate amqpTemplate;
@Override public List<ProductInfo> findUpAll() { return productInfoRepository.findByProductStatus(ProductStatusEnum.UP.getCode()); }
@Override public List<ProductInfoOutput> findList(List<String> productIdList) { return productInfoRepository.findByProductIdIn(productIdList).stream() .map(e -> { ProductInfoOutput output = new ProductInfoOutput(); BeanUtils.copyProperties(e, output); return output; }) .collect(Collectors.toList()); }
@Override public void decreaseStock(List<DecreaseStockInput> decreaseStockInputList) { List<ProductInfo> productInfoList = decreaseStockProcess(decreaseStockInputList);
List<ProductInfoOutput> productInfoOutputList = productInfoList.stream().map(e -> { ProductInfoOutput output = new ProductInfoOutput(); BeanUtils.copyProperties(e, output); return output; }).collect(Collectors.toList()); amqpTemplate.convertAndSend("productInfo", JsonUtil.toJson(productInfoOutputList));
}
@Transactional public List<ProductInfo> decreaseStockProcess(List<DecreaseStockInput> decreaseStockInputList) { List<ProductInfo> productInfoList = new ArrayList<>(); for (DecreaseStockInput decreaseStockInput: decreaseStockInputList) { Optional<ProductInfo> productInfoOptional = productInfoRepository.findById(decreaseStockInput.getProductId()); if (!productInfoOptional.isPresent()){ throw new ProductException(ResultEnum.PRODUCT_NOT_EXIST); }
ProductInfo productInfo = productInfoOptional.get(); Integer result = productInfo.getProductStock() - decreaseStockInput.getProductQuantity(); if (result < 0) { throw new ProductException(ResultEnum.PRODUCT_STOCK_ERROR); }
productInfo.setProductStock(result); productInfoRepository.save(productInfo); productInfoList.add(productInfo); } return productInfoList; } }
|
ProductInfoReceiver.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| package com.imooc.order.message;
import com.fasterxml.jackson.core.type.TypeReference; import com.imooc.order.utils.JsonUtil; import com.imooc.product.common.ProductInfoOutput; import lombok.extern.slf4j.Slf4j; import org.springframework.amqp.rabbit.annotation.Queue; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component;
import java.util.List;
@Component @Slf4j public class ProductInfoReceiver {
private static final String PRODUCT_STOCK_TEMPLATE = "product_stock_%s";
@Autowired private StringRedisTemplate stringRedisTemplate;
@RabbitListener(queuesToDeclare = @Queue("productInfo")) public void process(String message) { log.info("队列【{}】接受到的消息: {}", "productInfo", message); ProductInfoOutput productInfoOutput = (ProductInfoOutput) JsonUtil.fromJson(message, ProductInfoOutput.class); log.info("队列消息转换后结果:{}", productInfoOutput);
stringRedisTemplate.opsForValue().set(String.format(PRODUCT_STOCK_TEMPLATE, productInfoOutput.getProductId()), String.valueOf(productInfoOutput.getProductStock()));
} }
|
Spring Cloud Stream
pom.xml
1 2 3 4
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> </dependency>
|
bootstrap.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| spring: application: name: order cloud: config: discovery: enabled: true service-id: config profile: test stream: bindings: orderTopicInput: group: order destination: order-topic content-type: application/json orderTopicOutput: group: order destination: order-topic content-type: application/json orderTopicInput2: group: order2 destination: order-topic2 content-type: application/json orderTopicOutput2: group: order2 destination: order-topic2 content-type: application/json eureka: client: service-url: defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
|
StreamClient.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| package com.imooc.order.message;
import org.springframework.cloud.stream.annotation.Input; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.SubscribableChannel;
public interface StreamClient {
String INPUT = "orderTopicInput"; String OUTPUT = "orderTopicOutput"; String INPUT2 = "orderTopicInput2"; String OUTPUT2 = "orderTopicOutput2";
@Input(StreamClient.INPUT) SubscribableChannel input();
@Output(StreamClient.OUTPUT) MessageChannel output();
@Input(StreamClient.INPUT2) SubscribableChannel input2();
@Output(StreamClient.OUTPUT2) MessageChannel output2(); }
|
StreamReceiver.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| package com.imooc.order.message;
import com.imooc.order.dto.OrderDTO; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.stream.annotation.EnableBinding; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.SendTo; import org.springframework.stereotype.Component;
@Component @EnableBinding({StreamClient.class}) @Slf4j public class StreamReceiver {
@StreamListener(StreamClient.INPUT) @SendTo(StreamClient.INPUT2) public String process(OrderDTO message) { log.info("StreamReceiver: {}", message); return "received."; }
@StreamListener(StreamClient.INPUT2) public void process2(String message) { log.info("StreamReceiver2: {}", message); } }
|

Zuul 服务网关


基本配置
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
| <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.2.RELEASE</version> <relativePath/> </parent> <groupId>com.imooc</groupId> <artifactId>api-gateway</artifactId> <version>0.0.1-SNAPSHOT</version> <name>api-gateway</name> <description>Demo project for Spring Boot</description>
<properties> <java.version>1.8</java.version> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> </properties>
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
<repositories> <repository> <id>spring-snapshots</id> <name>Spring Snapshots</name> <url>https://repo.spring.io/snapshot</url> <snapshots> <enabled>true</enabled> </snapshots> </repository> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>spring-snapshots</id> <name>Spring Snapshots</name> <url>https://repo.spring.io/snapshot</url> <snapshots> <enabled>true</enabled> </snapshots> </pluginRepository> <pluginRepository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> </pluginRepository> </pluginRepositories>
</project>
|
bootstrap.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| spring: application: name: api-gateway cloud: config: discovery: enabled: true service-id: config profile: dev eureka: client: service-url: defaultZone: http://eureka1:8761/eureka/,http://eureka2:8762/eureka/ server: port: 9000
|
api-gateway-dev.yml (config repo)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| zuul: sensitive-headers: routes: product: /myProduct/**
management: endpoints: web: exposure: include: "*"
|
热刷新 ZuulConfig.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.imooc.apigateway;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.cloud.netflix.zuul.filters.ZuulProperties; import org.springframework.stereotype.Component;
@Component public class ZuulConfig {
@ConfigurationProperties("zuul") @RefreshScope public ZuulProperties zuulProperties() { return new ZuulProperties(); } }
|
启动类 ApiGatewayApplication.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.imooc.apigateway;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@SpringBootApplication @EnableZuulProxy public class ApiGatewayApplication {
public static void main(String[] args) { SpringApplication.run(ApiGatewayApplication.class, args); }
}
|
过滤器
限流(令牌桶)
RateLimitFilter.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| package com.imooc.apigateway.filter;
import com.google.common.util.concurrent.RateLimiter; import com.imooc.apigateway.exception.RateLimitException; import com.netflix.zuul.ZuulFilter; import org.springframework.stereotype.Component;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE; import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SERVLET_DETECTION_FILTER_ORDER;
@Component public class RateLimitFilter extends ZuulFilter{
private static final RateLimiter RATE_LIMITER = RateLimiter.create(100);
@Override public String filterType() { return PRE_TYPE; }
@Override public int filterOrder() { return SERVLET_DETECTION_FILTER_ORDER - 1; }
@Override public boolean shouldFilter() { return true; }
@Override public Object run() { if (!RATE_LIMITER.tryAcquire()) { throw new RateLimitException(); }
return null; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| package com.imooc.apigateway.filter;
import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.POST_TYPE; import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SEND_RESPONSE_FILTER_ORDER;
@Component public class addResponseHeaderFilter extends ZuulFilter{ @Override public String filterType() { return POST_TYPE; }
@Override public int filterOrder() { return SEND_RESPONSE_FILTER_ORDER - 1; }
@Override public boolean shouldFilter() { return true; }
@Override public Object run() { RequestContext requestContext = RequestContext.getCurrentContext(); HttpServletResponse response = requestContext.getResponse(); response.setHeader("X-Foo", UUID.randomUUID().toString()); return null; } }
|
自定义条件拦截
TokenFilter.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| package com.imooc.apigateway.filter;
import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_DECORATION_FILTER_ORDER; import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
@Component public class TokenFilter extends ZuulFilter { @Override public String filterType() { return PRE_TYPE; }
@Override public int filterOrder() { return PRE_DECORATION_FILTER_ORDER - 1; }
@Override public boolean shouldFilter() { return true; }
@Override public Object run() { RequestContext requestContext = RequestContext.getCurrentContext(); HttpServletRequest request = requestContext.getRequest();
String token = request.getParameter("token"); if (StringUtils.isEmpty(token)) { requestContext.setSendZuulResponse(false); requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value()); } return null; } }
|
Login权限校验
买家过滤器
AuthBuyerFilter.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| package com.imooc.apigateway.filter;
import com.imooc.apigateway.constant.RedisConstant; import com.imooc.apigateway.utils.CookieUtil; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.http.HttpStatus; import org.springframework.util.StringUtils;
import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest;
public class AuthBuyerFilter extends ZuulFilter {
@Autowired private StringRedisTemplate stringRedisTemplate;
RequestContext requestContext = RequestContext.getCurrentContext(); HttpServletRequest request = requestContext.getRequest();
@Override public String filterType() { return null; }
@Override public int filterOrder() { return 0; }
@Override public boolean shouldFilter() {
if ("/order/order/create".equals(request.getRequestURI())) { return true; } return false; }
@Override public Object run() throws ZuulException {
Cookie cookie = CookieUtil.get(request, "openid"); if (cookie == null || StringUtils.isEmpty((cookie.getValue()))) { requestContext.setSendZuulResponse(false); requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value()); }
return null; } }
|
卖家过滤器
AuthSellerFilter.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| package com.imooc.apigateway.filter;
import com.imooc.apigateway.constant.RedisConstant; import com.imooc.apigateway.utils.CookieUtil; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.http.HttpStatus; import org.springframework.util.StringUtils;
import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest;
public class AuthSellerFilter extends ZuulFilter {
@Autowired private StringRedisTemplate stringRedisTemplate;
RequestContext requestContext = RequestContext.getCurrentContext(); HttpServletRequest request = requestContext.getRequest();
@Override public String filterType() { return null; }
@Override public int filterOrder() { return 0; }
@Override public boolean shouldFilter() {
if ("/order/order/finish".equals(request.getRequestURI())) { return true; } return false; }
@Override public Object run() throws ZuulException {
Cookie cookie = CookieUtil.get(request, "token"); if (cookie == null || StringUtils.isEmpty((cookie.getValue())) || StringUtils.isEmpty(stringRedisTemplate.opsForValue().get(String.format(RedisConstant.TOKEN_TEMPLATE, cookie.getValue())))) { requestContext.setSendZuulResponse(false); requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value()); }
return null; } }
|
其他
CookieUtil.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| package com.imooc.apigateway.utils;
import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
public class CookieUtil {
public static void set(HttpServletResponse response, String name, String value, int maxAge) { Cookie cookie = new Cookie(name, value); cookie.setPath("/"); cookie.setMaxAge(maxAge); response.addCookie(cookie); }
public static Cookie get(HttpServletRequest request, String name) { Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie cookie: cookies) { if (name.equals(cookie.getName())) { return cookie; } } }
return null; } }
|
CookieConstant.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.imooc.apigateway.constant;
public interface CookieConstant {
String TOKEN = "token";
String OPENID = "openid";
Integer expire = 7200; }
|
RedisConstant.java
1 2 3 4 5 6 7
| package com.imooc.apigateway.constant;
public interface RedisConstant {
String TOKEN_TEMPLATE = "token_%s";
}
|
LoginController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
| package com.imooc.user.controller;
import com.imooc.user.VO.ResultVO; import com.imooc.user.constant.CookieConstant; import com.imooc.user.constant.RedisConstant; import com.imooc.user.dataobject.UserInfo; import com.imooc.user.enums.ResultEnum; import com.imooc.user.enums.RoleEnum; import com.imooc.user.service.UserService; import com.imooc.user.utils.CookieUtil; import com.imooc.user.utils.ResultVOUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.UUID; import java.util.concurrent.TimeUnit;
@RestController @RequestMapping("/login") public class LoginController {
@Autowired private UserService userService;
@Autowired private StringRedisTemplate stringRedisTemplate;
@GetMapping("/buyer") public ResultVO buyer(@RequestParam("openid") String openid, HttpServletResponse response) { UserInfo userInfo = userService.findByOpenid(openid); if (userInfo == null) { return ResultVOUtil.error(ResultEnum.LOGIN_FAIL); }
if (RoleEnum.BUYER.getCode() != userInfo.getRole()) { return ResultVOUtil.error(ResultEnum.ROLE_ERROR); }
CookieUtil.set(response, CookieConstant.OPENID, openid, CookieConstant.expire);
return ResultVOUtil.success(); }
@GetMapping("/seller") public ResultVO seller(@RequestParam("openid") String openid, HttpServletRequest request, HttpServletResponse response) { Cookie cookie = CookieUtil.get(request, CookieConstant.TOKEN); if (cookie != null && !StringUtils.isEmpty(stringRedisTemplate.opsForValue().get(String.format(RedisConstant.TOKEN_TEMPLATE, cookie.getValue())))) { return ResultVOUtil.success(); }
UserInfo userInfo = userService.findByOpenid(openid); if (userInfo == null) { return ResultVOUtil.error(ResultEnum.LOGIN_FAIL); }
if (RoleEnum.SELLER.getCode() != userInfo.getRole()) { return ResultVOUtil.error(ResultEnum.ROLE_ERROR); }
String token = UUID.randomUUID().toString(); Integer expire = CookieConstant.expire; stringRedisTemplate.opsForValue().set(String.format(RedisConstant.TOKEN_TEMPLATE, token), openid, expire, TimeUnit.SECONDS);
CookieUtil.set(response, CookieConstant.TOKEN, token, CookieConstant.expire);
return ResultVOUtil.success(); } }
|
Hystrix 服务熔断与降级
Hystrix
pom.xml
1 2 3 4 5 6 7 8 9
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency>
|
bootstrap.yml
1 2 3 4 5 6 7 8 9 10 11 12
| hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 1000 getProductInfoList: execution: isolation: thread: timeoutInMilliseconds: 3000
|
OrderApplication.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package com.imooc.order;
import org.springframework.boot.SpringApplication; import org.springframework.cloud.client.SpringCloudApplication; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.ComponentScan;
@EnableFeignClients(basePackages = "com.imooc.product.client")
@SpringCloudApplication @EnableHystrixDashboard @ComponentScan(basePackages = "com.imooc") public class OrderApplication {
public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } }
|
HystrixController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| package com.imooc.order.controller;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate;
import java.util.Arrays;
@RestController @DefaultProperties(defaultFallback = "defaultFallback") public class HystrixController {
@HystrixCommand @GetMapping("/getProductInfoList") public String getProductInfoList(@RequestParam("number") Integer number) { if (number % 2 == 0) { return "success"; }
RestTemplate restTemplate = new RestTemplate(); return restTemplate.postForObject("http://127.0.0.1:8005/product/listForOrder", Arrays.asList("157875196366160022"), String.class);
}
private String fallback() { return "太拥挤了, 请稍后再试~~"; }
private String defaultFallback() { return "默认提示:太拥挤了, 请稍后再试~~"; } }
|
Hystrix Dashboard
控制台
http://localhost:8081/hystrix/

监控效果
http://localhost:8081/actuator/hystrix.stream

调用链路监控
sleuth
pom.xml
1 2 3 4 5
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin</artifactId> </dependency>
|
bootstrap.yml
1 2 3 4 5 6 7 8 9 10 11
| spring: zipkin: base-url: http://localhost:9411 sender: type: web sleuth: sampler: probability: 1 logging: level: org.springframework.cloud.openfeign: debug
|
zipkin
安装并运行zipkin
https://zipkin.io/pages/quickstart.html1
| docker run -d -p 9411:9411 openzipkin/zipkin
|
登录zipkin控制台
http://localhost:9411/zipkin/
测试
访问要测试的服务

登录zipkin控制台,看是否有调用链路记录生成。


