SpringCloud:高级上篇,负载均衡Ribbon及策略,声明式服务调用Feign及性能优化,服务容错保护Hystrix雪崩Feign线程池与信号量隔离RabbitMQ监控,微服务设计模式及项目
SpringCloud高级部分(上篇)
目录
3 修改配置文件去掉与Eureka相关的配置,添加新配置项... 10
3 采用Http连接池,提升Feign的并发吞吐量... 20
4 将Feign的Http客户端工具修改为HttpClient 21
六、 查看微服务日志中记录每个接口URL,状态码和耗时信息... 23
3 Logback的输出日志级别需要时debug级别... 23
五、 可视化的数据监控Hystrix-dashboard. 33
2 修改pom文件添加Hystrix-dashboard坐标... 34
六、 使用Turbine在多个服务与集群情况下收集数据监控... 41
3 Mybatis的generatorSqlmapCustom工具使用... 7
第一章 负载均衡Ribbon
(Spring Cloud高级)
一、 Ribbon在微服务中的作用
1 什么是Ribbon
1.Ribbon 是一个基于Http和TCP的客服端负载均衡工具,它是基于Netflix Ribbon实现的。
2.它不像spring cloud服务注册中心、配置中心、API网关那样独立部署,但是它几乎存在于每个spring cloud 微服务中。包括feign提供的声明式服务调用也是基于该Ribbon实现的。
3.ribbon默认提供很多种负载均衡算法,例如 轮询、随机 等等。甚至包含自定义的负载均衡算法。
2 Ribbon解决了什么问题
他解决并提供了微服务的负载均衡的问题。
二、 集中式与进程内负载均衡的区别
1 负载均衡解决方案的分类
目前业界主流的负载均衡方案可分成两类:
第一类:集中式负载均衡, 即在consumer和provider之间使用独立的负载均衡设施(可以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把 访问请求 通过某种策略转发至provider;
第二类:进程内负载均衡,将负载均衡逻辑集成到consumer,consumer从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的provider。
Ribbon就属于后者,它只是一个类库,集成于consumer进程,consumer通过它来获取到provider的地址。
2 两种负载均衡方式结构图
三、 Ribbon的入门案例
Ribbon中对于集群的服务采用的负载均衡的策略默认的是轮询
1 Consumer
@Service public class UserService { @Autowired private LoadBalancerClient loadBalancerClient;//ribbon负载均衡器
public List<User> getUsers(){ //选择调用的服务的名称 //ServiceInstance 封装了服务的基本信息,如 IP,端口 ServiceInstance si = this.loadBalancerClient.choose("eureka-provider"); //拼接访问服务的URL StringBuffer sb = new StringBuffer(); //http://localhost:9090/user sb.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/user"); System.out.println(sb.toString()); //springMVC RestTemplate RestTemplate rt = new RestTemplate();
ParameterizedTypeReference<List<User>> type = new ParameterizedTypeReference<List<User>>() {};
//ResponseEntity:封装了返回值信息 ResponseEntity<List<User>> response = rt.exchange(sb.toString(),HttpMethod.GET, null, type); List<User> list =response.getBody(); return list; } } |
2 Consumer的配置文件
spring.application.name=eureka-consumer server.port=9091 #设置服务注册中心地址,指向另一个注册中心 eureka.client.serviceUrl.defaultZone=http://user:[email protected]:8761/eureka/,http://user:[email protected]:8761/eureka/ |
3 Provider的集群部署
3.1 将provider打包。部署到linux环境中
3.2 创建启动脚本server.sh
#!/bin/bash
cd `dirname $0`
CUR_SHELL_DIR=`pwd` CUR_SHELL_NAME=`basename ${BASH_SOURCE}`
JAR_NAME="springcloud-eureka-provider-0.0.1-SNAPSHOT.jar" JAR_PATH=$CUR_SHELL_DIR/$JAR_NAME
#JAVA_MEM_OPTS=" -server -Xms1024m -Xmx1024m -XX:PermSize=128m" JAVA_MEM_OPTS=""
#SPRING_PROFILES_ACTIV="-Dspring.profiles.active=eureka2" SPRING_PROFILES_ACTIV="" LOG_DIR=$CUR_SHELL_DIR/logs LOG_PATH=$LOG_DIR/${JAR_NAME%..log
echo_help() { echo -e "syntax: sh $CUR_SHELL_NAME start|stop" }
if [ -z $1 ];then echo_help exit 1 fi
if [ ! -d "$LOG_DIR" ];then mkdir "$LOG_DIR" fi
if [ ! -f "$LOG_PATH" ];then touch "$LOG_DIR" fi
if [ "$1" == "start" ];then
# check server PIDS=`ps --no-heading -C java -f --width 1000 | grep $JAR_NAME | awk '{print $2}'` if [ -n "$PIDS" ]; then echo -e "ERROR: The $JAR_NAME already started and the PID is ${PIDS}." exit 1 fi
echo "Starting the $JAR_NAME..."
# start nohup java $JAVA_MEM_OPTS -jar $SPRING_PROFILES_ACTIV $JAR_PATH >> $LOG_PATH 2>&1 &
COUNT=0 while [ $COUNT -lt 1 ]; do sleep 1 COUNT=`ps --no-heading -C java -f --width 1000 | grep "$JAR_NAME" | awk '{print $2}' | wc -l` if [ $COUNT -gt 0 ]; then break fi done PIDS=`ps --no-heading -C java -f --width 1000 | grep "$JAR_NAME" | awk '{print $2}'` echo "${JAR_NAME} Started and the PID is ${PIDS}." echo "You can check the log file in ${LOG_PATH} for details."
elif [ "$1" == "stop" ];then
PIDS=`ps --no-heading -C java -f --width 1000 | grep $JAR_NAME | awk '{print $2}'` if [ -z "$PIDS" ]; then echo "ERROR:The $JAR_NAME does not started!" exit 1 fi
echo -e "Stopping the $JAR_NAME..."
for PID in $PIDS; do kill $PID > /dev/null 2>&1 done
COUNT=0 while [ $COUNT -lt 1 ]; do sleep 1 COUNT=1 for PID in $PIDS ; do PID_EXIST=`ps --no-heading -p $PID` if [ -n "$PID_EXIST" ]; then COUNT=0 break fi done done
echo -e "${JAR_NAME} Stopped and the PID is ${PIDS}." else echo_help exit 1 fi |
4 启动Consumer
四、 Ribbon的常见负载均衡策略
id | 策略名称 | 策略对应的类名 | 实现原理 |
---|---|---|---|
1 | 轮询策略(默认) | RoundRobinRule | 轮询策略表示每次都顺序取下一个provider,比如一共有5个provider,第1次取第1个,第2次取第2个,第3次取第3个,以此类推 |
2 | 权重轮询策略 | WeightedResponseTimeRule | 1.根据每个provider的响应时间分配一个权重,响应时间越长,权重越小,被选中的可能性越低。 2.原理:一开始为轮询策略,并开启一个计时器,每30秒收集一次每个provider的平均响应时间,当信息足够时,给每个provider附上一个权重,并按权重随机选择provider,高权越重的provider会被高概率选中。 |
3 | 随机策略 | RandomRule | 从provider列表中随机选择一个provider |
4 | 最少并发数策略 | BestAvailableRule | 选择正在请求中的并发数最小的provider,除非这个provider在熔断中。 |
5 | 在“选定的负载均衡策略”基础上进行重试机制 | RetryRule | 1.“选定的负载均衡策略”这个策略是轮询策略RoundRobinRule 2.该重试策略先设定一个阈值时间段,如果在这个阈值时间段内当选择provider不成功,则一直尝试采用“选定的负载均衡策略:轮询策略”最后选择一个可用的provider |
6 | 可用性敏感策略 | AvailabilityFilteringRule: | 过滤性能差的provider,有2种: 第一种:过滤掉在eureka中处于一直连接失败provider 第二种:过滤掉高并发的provider |
7 | 区域敏感性策略 | ZoneAvoidanceRule: | 1.以一个区域为单位考察可用性,对于不可用的区域整个丢弃,从剩下区域中选可用的provider 2.如果这个ip区域内有一个或多个实例不可达或响应变慢,都会降低该ip区域内其他ip被选中的权重。 |
五、 Ribbon指定其他负载均衡策略
1 修改代码更换负载均衡策略
1.1 创建项目
1.2 在启动类中添加创建负载均衡策略对象的方法
@EnableEurekaClient @SpringBootApplication public class ConsumerApplication {
@Bean public RandomRule createRule(){ return new RandomRule(); } public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } |
2 修改配置文件更换负载均衡策略
#设置负载均衡策略 eureka-provider为调用的服务的名称 eureka-provider.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule |
六、 Ribbon的点对点直连
1 创建项目
2 去掉Eureka的坐标添加Ribbon坐标
<!-- ribbon坐标 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> |
3 修改配置文件去掉与Eureka相关的配置,添加新配置项
spring.application.name=eureka-consumer-LB server.port=9091 #禁用 eureka ribbon.eureka.enabled=false #指定具体的服务实例清单 eureka-provider.ribbon.listOfServers=192.168.70.137:9090 |
4 修改启动类去掉报错代码
@SpringBootApplication public class ConsumerApplication {
/*@Bean public RandomRule createRule(){ return new RandomRule(); }*/ public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } |
第二章 声明式服务调用Feign
(Spring Cloud高级)
一、 什么是Feign
Feign是一种声明式、模板化的HTTP客户端(仅在consumer中使用)。
二、 什么是声明式,有什么作用,解决什么问题?
声明式调用就像调用本地方法一样调用远程方法;无感知远程http请求。
1,Spring Cloud的声明式调用, 可以做到使用 HTTP请求远程服务时能就像调用本地方法一样的体验,开发者完全感知不到这是远程方法,更感知不到这是个HTTP请求。
2,它像Dubbo一样,consumer直接调用接口方法调用provider,而不需要通过常规的Http Client构造请求再解析返回数据。
3,它解决了让开发者调用远程接口就跟调用本地方法一样,无需关注与远程的交互细节,更无需关注分布式环境开发。
三、 编写Feign的入门案例
1 需求
实现Ego电商平台中的商品基本操作
2 项目设计
3 创建项目
3.1 创建Product-Service
3.1.1创建项目
3.1.2修改pom文件添加依赖
<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.bjsxt</groupId> <artifactId>springcloud-ego-product-service</artifactId> <version>0.0.1-SNAPSHOT</version>
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.13.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <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> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> </dependencies> </project> |
3.1.3创建Service接口
/** * Product服务接口 * @author Administrator * */ @RequestMapping("/product") public interface ProductService {
//查询所有商品 @RequestMapping(value="/findAll",method=RequestMethod.GET) public List<Product> findAll(); } |
3.1.4创建POJO类
public class Product {
private Integer id; private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Product(Integer id, String name) { super(); this.id = id; this.name = name; } public Product() { super(); // TODO Auto-generated constructor stub }
} |
3.2 创建Product-Provider
3.2.1创建项目
3.2.2修改pom文件添加依赖
<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.bjsxt</groupId> <artifactId>springcloud-ego-product-provider</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.13.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <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> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <!-- 添加product-service坐标 --> <dependency> <groupId>com.bjsxt</groupId> <artifactId>springcloud-ego-product-service</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> |
3.2.3添加全局配置文件
spring.application.name=ego-product-provider server.port=9001 #设置服务注册中心地址,指向另一个注册中心 eureka.client.serviceUrl.defaultZone=http://user:[email protected]:8761/eureka/,http://user:[email protected]:8761/eureka/ |
3.2.4编写Controller
/** * Product-Provider服务 * @author Administrator * */ @RestController public class ProductController implements ProductService { @Override public List<Product> findAll() { List<Product> list = new ArrayList<>(); list.add(new Product(1, "电视")); list.add(new Product(2, "电脑")); list.add(new Product(3, "冰箱")); list.add(new Product(4, "手电筒")); return list; } } |
3.2.5编写SpringBoot的启动类
@EnableEurekaClient @SpringBootApplication public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class, args); } } |
3.3 创建Product-consumer
3.3.1创建项目
3.3.2修改pom文件添加依赖
<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.bjsxt</groupId> <artifactId>springcloud-ego-product-consumer</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.13.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <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> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <!-- 添加Feign坐标 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> <!-- 添加product-service坐标 --> <dependency> <groupId>com.bjsxt</groupId> <artifactId>springcloud-ego-product-service</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> |
3.3.3添加全局配置文件
spring.application.name=ego-product-consumer server.port=9002 #设置服务注册中心地址,指向另一个注册中心 eureka.client.serviceUrl.defaultZone=http://user:[email protected]:8761/eureka/,http://user:[email protected]:8761/eureka/ |
3.3.4编写Controller
/** * Product Consumer 服务 * @author Administrator * */ @RestController public class ProductController { @Autowired ProductConsumerService consumerservice; /** * Consumer中的查询所有商品的方法 * @return */ @RequestMapping(value="/list",method=RequestMethod.GET) public List<Product> getAll(){ return this.consumerservice.findAll(); } } |
3.3.5编写Service接口
@FeignClient(name="ego-product-provider") public interface ProductConsumerService extends ProductService{ } |
3.3.6编写SpringBoot启动类
@EnableFeignClients @EnableDiscoveryClient @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } |
四、 Feign的请求参数处理
1 单个参数处理
1.1 修改Product-Service
/** * Product服务接口 * @author Administrator * */ @RequestMapping("/product") public interface ProductService {
//查询所有商品 @RequestMapping(value="/findAll",method=RequestMethod.GET) public List<Product> findAll();
//根据商品ID查询商品 @RequestMapping(value="/getProductById",method=RequestMethod.GET) public Product getProductById(@RequestParam("id") Integer id);
} |
1.2 修改Product-Provider
/** * Product-Provider服务 * @author Administrator * */ @RestController public class ProductController implements ProductService { @Override public List<Product> findAll() { List<Product> list = new ArrayList<>(); list.add(new Product(1, "电视")); list.add(new Product(2, "电脑")); list.add(new Product(3, "冰箱")); list.add(new Product(4, "手电筒")); return list; } @Override public Product getProductById(Integer id) { return new Product(id, "SpringCloud"); } } |
1.3 修改Product-Consumer
/** * Product Consumer 服务 * @author Administrator * */ @RestController public class ProductController { @Autowired ProductConsumerService consumerservice; /** * Consumer中的查询所有商品的方法 * @return */ @RequestMapping(value="/list",method=RequestMethod.GET) public List<Product> getAll(){ return this.consumerservice.findAll(); }
/** * Consumer中根据商品id查询商品 */ @RequestMapping(value="/get",method=RequestMethod.GET) public Product getProduct(@RequestParam("id") Integer id){ return this.consumerservice.getProductById(id); }
}
|
2 多个参数处理
2.1 方式一 GET提交方式
2.1.1修改Product-Service
/** * Product服务接口 * @author Administrator * */ @RequestMapping("/product") public interface ProductService {
//查询所有商品 @RequestMapping(value="/findAll",method=RequestMethod.GET) public List<Product> findAll();
//根据商品ID查询商品 @RequestMapping(value="/getProductById",method=RequestMethod.GET) public Product getProductById(@RequestParam("id") Integer id);
//添加商品传递多个参数 方式一 :GET方式 @RequestMapping(value="/add",method=RequestMethod.GET) public Product addProduct(@RequestParam("id") Integer id,@RequestParam("name") String name); } |
2.1.2修改Product-Provider
/** * Product-Provider服务 * @author Administrator * */ @RestController public class ProductController implements ProductService { @Override public List<Product> findAll() { List<Product> list = new ArrayList<>(); list.add(new Product(1, "电视")); list.add(new Product(2, "电脑")); list.add(new Product(3, "冰箱")); list.add(new Product(4, "手电筒")); return list; } @Override public Product getProductById(Integer id) { return new Product(id, "SpringCloud"); } @Override public Product addProduct(Integer id, String name) { return new Product(id, name); } |
2.1.3修改Product-Consumer
/** * Product Consumer 服务 * @author Administrator * */ @RestController public class ProductController { @Autowired ProductConsumerService consumerservice; /** * Consumer中的查询所有商品的方法 * @return */ @RequestMapping(value="/list",method=RequestMethod.GET) public List<Product> getAll(){ return this.consumerservice.findAll(); }
/** * Consumer中根据商品id查询商品 */ @RequestMapping(value="/get",method=RequestMethod.GET) public Product getProduct(@RequestParam("id") Integer id){ return this.consumerservice.getProductById(id); }
/** * 商品添加 传递多个参数。方式一:GET */ @RequestMapping(value="/add",method=RequestMethod.GET) public Product addProduct(Product product){ return this.consumerservice.addProduct(product.getId(), product.getName()); }
}
|
2.2 方式二 POST提交方式
2.2.1修改Product-Service
/** * Product服务接口 * @author Administrator * */ @RequestMapping("/product") public interface ProductService {
//查询所有商品 @RequestMapping(value="/findAll",method=RequestMethod.GET) public List<Product> findAll();
//根据商品ID查询商品 @RequestMapping(value="/getProductById",method=RequestMethod.GET) public Product getProductById(@RequestParam("id") Integer id);
//添加商品传递多个参数 方式一 :GET方式 @RequestMapping(value="/add",method=RequestMethod.GET) public Product addProduct(@RequestParam("id") Integer id,@RequestParam("name") String name);
//添加商品传递多个参数 方式二 :POST方式 @RequestMapping(value="/add",method=RequestMethod.POST) public Product addProduct2(@RequestBody Product product); }
|
2.2.2修改Product-Provider
/** * Product-Provider服务 * @author Administrator * */ @RestController public class ProductController implements ProductService { @Override public List<Product> findAll() { List<Product> list = new ArrayList<>(); list.add(new Product(1, "电视")); list.add(new Product(2, "电脑")); list.add(new Product(3, "冰箱")); list.add(new Product(4, "手电筒")); return list; } @Override public Product getProductById(Integer id) { return new Product(id, "SpringCloud"); } @Override public Product addProduct(Integer id, String name) { return new Product(id, name); } @Override public Product addProduct2(@RequestBody Product product) { return product; } }
|
2.2.3修改Product-Consumer
/** * Product Consumer 服务 * @author Administrator * */ @RestController public class ProductController { @Autowired ProductConsumerService consumerservice; /** * Consumer中的查询所有商品的方法 * @return */ @RequestMapping(value="/list",method=RequestMethod.GET) public List<Product> getAll(){ return this.consumerservice.findAll(); }
/** * Consumer中根据商品id查询商品 */ @RequestMapping(value="/get",method=RequestMethod.GET) public Product getProduct(@RequestParam("id") Integer id){ return this.consumerservice.getProductById(id); }
/** * 商品添加 传递多个参数。方式一:GET */ @RequestMapping(value="/add",method=RequestMethod.GET) public Product addProduct(Product product){ return this.consumerservice.addProduct(product.getId(), product.getName()); }
/** * 商品添加 传递多个参数。方式二:POST */ @RequestMapping(value="/add2",method=RequestMethod.GET) public Product addProduct2(Product product){ return this.consumerservice.addProduct2(product); }
}
|
五、 Feign的性能优化
1 通过Gzip压缩算法,提升网络通信速度
1.1 gzip介绍
gzip介绍:gzip是一种数据格式,采用用deflate算法压缩data;gzip是一种流行的文件压缩算法,应用十分广泛,尤其是在Linux平台。
gzip能力:当Gzip压缩到一个纯文本文件时,效果是非常明显的,大约可以减少70%以上的文件大小。
gzip作用:网络数据经过压缩后实际上降低了网络传输的字节数,最明显的好处就是可以加快网页加载的速度。网页加载速度加快的好处不言而喻,除了节省流量,改善用户的浏览体验外,另一个潜在的好处是Gzip与搜索引擎的抓取工具有着更好的关系。例如 Google就可以通过直接读取gzip文件来比普通手工抓取 更快地检索网页。
1.2 HTTP协议中关于压缩传输的规定
第一:客户端向服务器请求中带有:Accept-Encoding:gzip, deflate 字段,向服务器表示,客户端支持的压缩格式(gzip或者deflate),如果不发送该消息头,服务器是不会压缩的。
第二:服务端在收到请求之后,如果发现请求头中含有Accept-Encoding字段,并且支持该类型的压缩,就对响应报文压缩之后返回给客户端,并且携带Content-Encoding:gzip消息头,表示响应报文是根据该格式压缩过的。
第三:客户端接收到请求之后,先判断是否有Content-Encoding消息头,如果有,按该格式解压报文。否则按正常报文处理。
2 编写支持Gzip压缩案例
2.1 创建项目
2.2 修改配置文件
2.2.1只配置Consumer通过Feign到Provider的请求与相应的Gzip压缩
#-----------------------------feign gzip #配置请求GZIP压缩 feign.compression.request.enabled=true #配置响应GZIP压缩 feign.compression.response.enabled=true #配置压缩支持的MIME TYPE feign.compression.request.mime-types=text/xml,application/xml,application/json #配置压缩数据大小的最小阀值,默认2048 feign.compression.request.min-request-size=512 |
2.2.2对客户端浏览器的请求以及Consumer对provider的请求与响应做Gzip压缩
#-----------------------------spring boot gzip #是否启用压缩 server.compression.enabled=true server.compression.mime-types=application/json,application/xml,text/html,text/xml,text/plain |
3 采用Http连接池,提升Feign的并发吞吐量
为什么http连接池能提升性能?
3.1 http的背景原理
a. 两台服务器建立http连接的过程是很复杂的一个过程,涉及到多个数据包的交换,并且也很耗时间。
b. Http连接需要的3次握手4次分手开销很大,这一开销对于大量的比较小的http消息来说更大。
3.2 优化解决方案
a. 如果我们直接采用http连接池,节约了大量的3次握手4次分手;这样能大大提升吞吐率。
b. feign的http客户端支持3种框架;HttpURLConnection、httpclient、okhttp;默认是HttpURLConnection。
c. 传统的HttpURLConnection是JDK自带的,并不支持连接池,如果要实现连接池的机制,还需要自己来管理连接对象。对于网络请求这种底层相对复杂的操作,如果有可用的其他方案,也没有必要自己去管理连接对象。
d. HttpClient相比传统JDK自带的HttpURLConnection,它封装了访问http的请求头,参数,内容体,响应等等;它不仅使客户端发送HTTP请求变得容易,而且也方便了开发人员测试接口(基于Http协议的),即提高了开发的效率,也方便提高代码的健壮性;另外高并发大量的请求网络的时候,还是用“连接池”提升吞吐量。
4 将Feign的Http客户端工具修改为HttpClient
4.1 创建项目
4.2 修改pom文件添加HttpClient的坐标
<!-- 使用Apache HttpClient替换Feign原生httpURLConnection --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> <dependency> <groupId>com.netflix.feign</groupId> <artifactId>feign-httpclient</artifactId> <version>8.17.0</version> </dependency> |
4.3 修改配置文件开启HttpClient的使用
#启用httpclient feign.httpclient.enabled=true |
注意:如果使用HttpClient作为Feign的客户端工具。那么在定义接口上的注解是需要注意,如果传递的是一个自定义的对象(对象会使用json格式来专递)。需要制定类型。
4.4 Product-Service
/** * Product服务接口 * @author Administrator * */ @RequestMapping("/product") public interface ProductService {
//查询所有商品 @RequestMapping(value="/findAll",method=RequestMethod.GET) public List<Product> findAll();
//根据商品ID查询商品 @RequestMapping(value="/getProductById",method=RequestMethod.GET) public Product getProductById(@RequestParam("id") Integer id);
//添加商品传递多个参数 方式一 :GET方式 @RequestMapping(value="/add",method=RequestMethod.GET) public Product addProduct(@RequestParam("id") Integer id,@RequestParam("name") String name); //----------------------Httpclient---------------------------------------------- //添加商品传递多个参数 方式二 :POST方式 @RequestMapping(value="/add2",method=RequestMethod.POST,consumes=MediaType.APPLICATION_JSON_VALUE) public Product addProduct2(@RequestBody Product product);
//使用HttpClient工具 添加商品传递多个参数 :基于GET方式 @RequestMapping(value="/add3",method=RequestMethod.GET,consumes=MediaType.APPLICATION_JSON_VALUE) public Product addProduct3(Product product); }
|
六、 查看微服务日志中记录每个接口URL,状态码和耗时信息
1 创建项目
2 添加logback.xml文件
3 Logback的输出日志级别需要时debug级别
<!-- 日志输出级别 --> <root level="DEBUG"> <appender-ref ref="Stdout" /> <appender-ref ref="RollingFile" /> </root> |
4 在启动类中添加一个方法
@EnableFeignClients @EnableDiscoveryClient @SpringBootApplication public class ConsumerApplication {
// NONE:不记录任何信息,默认值 // BASIC:记录请求方法、请求URL、状态码和用时 // HEADERS:在BASIC基础上再记录一些常用信息 // FULL:记录请求和相应的所有信息 @Bean public Logger.Level getLog(){ return Logger.Level.FULL; }
public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } }
|
七、 配置Feign负载均衡请求超时时间
Feign的负载均衡底层用的就是Ribbon
1 修改配置文件,设置超时时间
1.1 全局配置
#全局配置 # 请求连接的超时时间 默认的时间为1秒 ribbon.ConnectTimeout=5000 # 请求处理的超时时间 ribbon.ReadTimeout=5000 |
1.2 根据服务名称进行局部超时配置
#局部配置 # 对所有操作请求都进行重试 ego-product-provider.ribbon.OkToRetryOnAllOperations=true # 对当前实例的重试次数 ego-product-provider.ribbon.MaxAutoRetries=2 # 切换实例的重试次数 ego-product-providert.ribbon.MaxAutoRetriesNextServer=0 # 请求连接的超时时间 ego-product-provider.ribbon.ConnectTimeout=3000 # 请求处理的超时时间 ego-product-provider.ribbon.ReadTimeout=3000 |
第三章 服务容错保护Hystrix
(Spring Cloud高级)
一、 什么是灾难性的雪崩效应
二、 如何解决灾难性雪崩效应
降级
超时降级、资源不足时(线程或信号量)降级,降级后可以配合降级接口返回托底数据。实现一个fallback方法, 当请求后端服务出现异常的时候, 可以使用fallback方法返回的值.
隔离(线程池隔离和信号量隔离)
限制调用分布式服务的资源使用,某一个调用的服务出现问题不会影响其他服务调用。
熔断
当失败率(如因网络故障/超时造成的失败率高)达到阀值自动触发降级,熔断器触发的快速失败会进行快速恢复。
缓存
提供了请求缓存。
请求合并
提供请求合并。
1 降级
对服务做降级处理
1.1 创建项目
1.2 修改pom文件添加hystrix的坐标
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> |
1.3 修改配置文件
spring.application.name=eureka-consumer-ribbon-hystrix server.port=9010 #设置服务注册中心地址,指向另一个注册中心 eureka.client.serviceUrl.defaultZone=http://user:[email protected]:8761/eureka/,http://user:[email protected]:8761/eureka/ |
1.4 修改启动类开启熔断器
@EnableCircuitBreaker //开启熔断器 断路器 @EnableEurekaClient @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } |
1.5 在项目中添加Product实体类
public class Product {
private Integer id; private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Product(Integer id, String name) { super(); this.id = id; this.name = name; } public Product() { super(); // TODO Auto-generated constructor stub }
}
|
1.6 修改ProductService
@Service public class ProductService { @Autowired private LoadBalancerClient loadBalancerClient;// ribbon负载均衡器 @HystrixCommand(fallbackMethod="fallback") public List<Product> getUsers() { // 选择调用的服务的名称 // ServiceInstance 封装了服务的基本信息,如 IP,端口 ServiceInstance si = this.loadBalancerClient.choose("ego-product-provider"); // 拼接访问服务的URL StringBuffer sb = new StringBuffer(); // http://localhost:9001/product/findAll sb.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/product/findAll"); System.out.println(sb.toString()); // springMVC RestTemplate RestTemplate rt = new RestTemplate(); ParameterizedTypeReference<List<Product>> type = new ParameterizedTypeReference<List<Product>>() { }; // ResponseEntity:封装了返回值信息 ResponseEntity<List<Product>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type); List<Product> list = response.getBody(); return list; }
//返回托底数据的方法 public List<Product> fallback(){ List<Product> list = new ArrayList<>(); list.add(new Product(-1, "我是托底数据")); return list; } }
|
1.7 以下四种情况将触发getFallback调用
(1) 方法抛出非HystrixBadRequestException异常。
(2) 方法调用超时
(3) 熔断器开启拦截调用
(4) 线程池/队列/信号量是否跑满
2 请求缓存
Hystrix为了降低访问服务的频率,支持将一个请求与返回结果做缓存处理。如果再次请求的URL没有变化,那么Hystrix不会请求服务,而是直接从缓存中将结果返回。这样可以大大降低访问服务的压力。
Hystrix自带缓存。有两个缺点:
1. 是一个本地缓存。在集群情况下缓存是不能同步的。
2. 不支持第三方缓存容器。Redis,memcache不支持的。
可以使用spring的cache。
2.1 安装Redis
2.2 创建项目
2.3 修改pom文件添加springCache坐标
<?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.bjsxt</groupId> <artifactId>springcloud-eureka-consumer-ribbon-cache</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>springcloud-eureka-consumer</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.13.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <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> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> <!-- springCache --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
|
2.4 在配置文件中配置redis链接信息
spring.application.name=eureka-consumer-ribbon-cache server.port=9010 #设置服务注册中心地址,指向另一个注册中心 eureka.client.serviceUrl.defaultZone=http://user:[email protected]:8761/eureka/,http://user:[email protected]:8761/eureka/ # Redis spring.redis.database=0 #Redis服务器地址 spring.redis.host=192.168.70.139 #Redis服务器连接端口 spring.redis.port=6379 #Redis服务器连接密码(默认为空) spring.redis.password= #连接池最大连接数(负值表示没有限制) spring.redis.pool.max-active=100 #连接池最大阻塞等待时间(负值表示没有限制) spring.redis.pool.max-wait=3000 #连接池最大空闭连接数 spring.redis.pool.max-idle=200 #连接汉最小空闲连接数 spring.redis.pool.min-idle=50 #连接超时时间(毫秒) spring.redis.pool.timeout=600 |
2.5 修改启动类开启缓存
@EnableCaching @EnableEurekaClient @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } |
2.6 修改ProductService
@CacheConfig(cacheNames={"com.bjsxt.ego.product"}) @Service public class ProductService { @Autowired private LoadBalancerClient loadBalancerClient;// ribbon负载均衡器 @HystrixCommand(fallbackMethod="fallback") public List<Product> getUsers() { // 选择调用的服务的名称 // ServiceInstance 封装了服务的基本信息,如 IP,端口 ServiceInstance si = this.loadBalancerClient.choose("ego-product-provider"); // 拼接访问服务的URL StringBuffer sb = new StringBuffer(); // http://localhost:9001/product/findAll sb.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/product/findAll"); System.out.println(sb.toString()); // springMVC RestTemplate RestTemplate rt = new RestTemplate(); ParameterizedTypeReference<List<Product>> type = new ParameterizedTypeReference<List<Product>>() { }; // ResponseEntity:封装了返回值信息 ResponseEntity<List<Product>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type); List<Product> list = response.getBody(); return list; }
//返回托底数据的方法 public List<Product> fallback(){ List<Product> list = new ArrayList<>(); list.add(new Product(-1, "我是托底数据")); return list; }
//根据ID查询商品 @Cacheable(key="'product' + #id") public Product getProductById(Integer id){ System.out.println("=========Get======"+id); return new Product(id, "新的商品"); }
//根据ID删除商品 @CacheEvict(key="'product' + #id") public void delProductById(Integer id){ System.out.println("=========Del======"+id); } }
|
2.7 修改ProductController
@RestController public class ProductController { @Autowired private ProductService userService; @RequestMapping("/consumer") public List<Product> getUsers() { return this.userService.getUsers(); }
@RequestMapping(value="/get",method=RequestMethod.GET) public Product get(Integer id){ return this.userService.getProductById(id); }
@RequestMapping(value="/del",method=RequestMethod.GET) public void del(Integer id){ this.userService.delProductById(id); } } |
3 请求合并
没合并的请求
请求合并
什么情况下使用请求合并
在微服务架构中,我们将一个项目拆分成很多个独立的模块,这些独立的模块通过远程调用来互相配合工作,但是,在高并发情况下,通信次数的增加会导致总的通信时间增加,同时,线程池的资源也是有限的,高并发环境会导致有大量的线程处于等待状态,进而导致响应延迟,为了解决这些问题,我们需要来了解Hystrix的请求合并。
请求合并的缺点
设置请求合并之后,本来一个请求可能5ms就搞定了,但是现在必须再等10ms看看还有没有其他的请求一起的,这样一个请求的耗时就从5ms增加到15ms了,不过,如果我们要发起的命令本身就是一个高延迟的命令,那么这个时候就可以使用请求合并了,因为这个时候时间窗的时间消耗就显得微不足道了,另外高并发也是请求合并的一个非常重要的场景。
3.1 创建项目
3.2 修改pom文件添加hystrix坐标
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> |
3.3 修改配置文件
spring.application.name=eureka-consumer-ribbon-batch server.port=9010 #设置服务注册中心地址,指向另一个注册中心 eureka.client.serviceUrl.defaultZone=http://user:[email protected]:8761/eureka/,http://user:[email protected]:8761/eureka/ |
3.4 修改ProductService
@Service public class ProductService { //利用hystrix合并请求 @HystrixCollapser(batchMethod = "batchProduct", scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL, collapserProperties = { //请求时间间隔在20ms之内的请求会被合并为一个请求,默认为10ms @HystrixProperty(name = "timerDelayInMilliseconds", value = "20"), //设置触发批处理执行之前,在批处理中允许的最大请求数。 @HystrixProperty(name = "maxRequestsInBatch", value = "200"), }) //consumer的controller调用的方法 该方法返回值必须要返回Future类型 public Future<Product> getProduct(Integer id){ System.out.println("=========="+id+"=========="); return null; }
@HystrixCommand //调用Provider服务的方法 public List<Product> batchProduct(List<Integer> ids){ for(Integer id:ids){ System.out.println(id); } //假设是调用provider服务后返回的list List<Product> list = new ArrayList<>(); list.add(new Product(1, "电视")); list.add(new Product(2, "电脑")); list.add(new Product(3, "冰箱")); list.add(new Product(4, "手电筒")); list.add(new Product(100,"list............")); System.out.println("ddddddddddddddddddddddd"); return list; } }
|
3.5 修改Controller
@RestController public class ProductController { @Autowired private ProductService userService;
@RequestMapping("/consumer") public void getUsers() throws Exception{ Future<Product> p1 = this.userService.getProduct(1); Future<Product> p2 = this.userService.getProduct(2); Future<Product> p3 = this.userService.getProduct(3); System.out.println(p1.get().toString()); System.out.println(p2.get().toString()); System.out.println(p3.get().toString()); } } |
3.6 请求合并参数介绍
4 服务熔断
4.1 创建项目
4.2 修改pom文件添加Hystrix坐标
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> |
4.3 修改配置文件
spring.application.name=eureka-consumer-ribbon-breaker server.port=9010 #设置服务注册中心地址,指向另一个注册中心 eureka.client.serviceUrl.defaultZone=http://user:[email protected]:8761/eureka/,http://user:[email protected]:8761/eureka/ |
4.4 修改启动类
@EnableCircuitBreaker //开启熔断器 断路器 @EnableEurekaClient @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } |
4.5 修改ProductService
@Service public class ProductService { @Autowired private LoadBalancerClient loadBalancerClient;// ribbon负载均衡器 @HystrixCommand(fallbackMethod = "fallback", commandProperties = { //默认20个;10s内请求数大于20个时就启动熔断器,当请求符合熔断条件时将触发getFallback()。 @HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value="10"), //请求错误率大于50%时就熔断,然后for循环发起请求,当请求符合熔断条件时将触发getFallback()。 @HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value="50"), //默认5秒;熔断多少秒后去尝试请求 @HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value="5000"), }) public List<Product> getUsers(int flag) {
System.out.println(flag); if(flag == 1){ throw new RuntimeException(); } // 选择调用的服务的名称 // ServiceInstance 封装了服务的基本信息,如 IP,端口 ServiceInstance si = this.loadBalancerClient.choose("ego-product-provider"); // 拼接访问服务的URL StringBuffer sb = new StringBuffer(); // http://localhost:9001/product/findAll sb.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/product/findAll"); System.out.println(sb.toString()); // springMVC RestTemplate RestTemplate rt = new RestTemplate(); ParameterizedTypeReference<List<Product>> type = new ParameterizedTypeReference<List<Product>>() { }; // ResponseEntity:封装了返回值信息 ResponseEntity<List<Product>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type); List<Product> list = response.getBody(); return list; }
//返回托底数据的方法 public List<Product> fallback(int flag){ List<Product> list = new ArrayList<>(); list.add(new Product(-1, "我是托底数据")); return list; } }
|
4.6 修改ProductController
@RestController public class ProductController { @Autowired private ProductService userService;
@RequestMapping("/consumer") public List<Product> getUsers(@RequestParam("flag") Integer flag){ return this.userService.getUsers(flag); } } |
4.7 熔断参数
5 隔离
5.1 线程池隔离
5.1.1创建项目
5.1.2修改pom添加hystrix坐标
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> |
5.1.3修改配置文件
spring.application.name=eureka-consumer-ribbon-threadpool server.port=9010 #设置服务注册中心地址,指向另一个注册中心 eureka.client.serviceUrl.defaultZone=http://user:[email protected]:8761/eureka/,http://user:[email protected]:8761/eureka/ |
5.1.4修改启动类
@EnableCircuitBreaker //开启熔断器 断路器 @EnableEurekaClient @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } |
5.1.5修改ProductService
@Service public class ProductService { @Autowired private LoadBalancerClient loadBalancerClient;// ribbon负载均衡器 @HystrixCommand(groupKey="ego-product-provider", commandKey = "getUsers", threadPoolKey="ego-product-provider", threadPoolProperties = { @HystrixProperty(name = "coreSize", value = "30"),//线程池大小 @HystrixProperty(name = "maxQueueSize", value = "100"),//最大队列长度 @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),//线程存活时间 @HystrixProperty(name = "queueSizeRejectionThreshold", value = "15")//拒绝请求 }, fallbackMethod = "fallback") public List<Product> getUsers() { System.out.println(Thread.currentThread().getName()); // 选择调用的服务的名称 // ServiceInstance 封装了服务的基本信息,如 IP,端口 ServiceInstance si = this.loadBalancerClient.choose("ego-product-provider"); // 拼接访问服务的URL StringBuffer sb = new StringBuffer(); // http://localhost:9001/product/findAll sb.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/product/findAll"); System.out.println(sb.toString()); // springMVC RestTemplate RestTemplate rt = new RestTemplate(); ParameterizedTypeReference<List<Product>> type = new ParameterizedTypeReference<List<Product>>() { }; // ResponseEntity:封装了返回值信息 ResponseEntity<List<Product>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type); List<Product> list = response.getBody(); return list; }
//返回托底数据的方法 public List<Product> fallback(){ System.out.println(Thread.currentThread().getName()); List<Product> list = new ArrayList<>(); list.add(new Product(-1, "我是托底数据")); return list; }
public void showThread(){ System.out.println(Thread.currentThread().getName()); } }
|
5.1.6修改ProductController
@RestController public class ProductController { @Autowired private ProductService userService;
@RequestMapping("/consumer") public List<Product> getUsers(){ return this.userService.getUsers(); }
@RequestMapping("/consumer1") public void getUsers1(){ this.userService.showThread(); } }
|
5.1.7线程池隔离参数
5.2 信号量隔离
5.2.1创建项目
5.2.2修改pom文件添加hystrix的坐标
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> |
5.2.3修改配置文件
spring.application.name=eureka-consumer-ribbon-semaphore server.port=9010 #设置服务注册中心地址,指向另一个注册中心 eureka.client.serviceUrl.defaultZone=http://user:[email protected]:8761/eureka/,http://user:[email protected]:8761/eureka/ |
5.2.4修改启动类
@EnableCircuitBreaker //开启熔断器 断路器 @EnableEurekaClient @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } }
|
5.2.5修改ProductService
@Service public class ProductService { @Autowired private LoadBalancerClient loadBalancerClient;// ribbon负载均衡器 @HystrixCommand(fallbackMethod = "fallback", commandProperties = { @HystrixProperty(name=HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY, value="SEMAPHORE"),// 信号量 隔离 @HystrixProperty (name=HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value="100")//信号量最大并度 }) public List<Product> getUsers() { System.out.println(Thread.currentThread().getName()); // 选择调用的服务的名称 // ServiceInstance 封装了服务的基本信息,如 IP,端口 ServiceInstance si = this.loadBalancerClient.choose("ego-product-provider"); // 拼接访问服务的URL StringBuffer sb = new StringBuffer(); // http://localhost:9001/product/findAll sb.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/product/findAll"); System.out.println(sb.toString()); // springMVC RestTemplate RestTemplate rt = new RestTemplate(); ParameterizedTypeReference<List<Product>> type = new ParameterizedTypeReference<List<Product>>() { }; // ResponseEntity:封装了返回值信息 ResponseEntity<List<Product>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type); List<Product> list = response.getBody(); return list; }
//返回托底数据的方法 public List<Product> fallback(){ System.out.println(Thread.currentThread().getName()); List<Product> list = new ArrayList<>(); list.add(new Product(-1, "我是托底数据")); return list; }
public void showThread(){ System.out.println(Thread.currentThread().getName()); } }
|
5.2.6信号量隔离参数
三、 线程池隔离和信号量隔离的区别
四、 Feign的雪崩处理
1 Feign的服务降级处理
1.1 创建项目
1.2 修改配置文件开启对hystrix的支持
spring.application.name=springcloud-eureka-consumer-feign-fallback server.port=9020 #设置服务注册中心地址,指向另一个注册中心 eureka.client.serviceUrl.defaultZone=http://user:[email protected]:8761/eureka/,http://user:[email protected]:8761/eureka/ #Feign默认是不开启Hystrix的。默认为:false feign.hystrix.enabled=true |
1.3 修改ProductConsumerService
@FeignClient(name="ego-product-provider",fallback=ProductServiceFallback.class) public interface ProductConsumerService { //查询所有商品 @RequestMapping(value="/product/findAll",method=RequestMethod.GET) public List<Product> findAll(); } |
1.4 添加ProductServiceFallback类
@Component public class ProductServiceFallback implements ProductConsumerService { //能够返回托底数据的fallback方法 @Override public List<Product> findAll() { List<Product> list = new ArrayList<>(); list.add(new Product(-1, "我是托底数据")); return list; } }
|
1.5 修改ProductController
/** * Product Consumer 服务 * @author Administrator * */ @RestController public class ProductController { @Autowired ProductConsumerService consumerservice; /** * Consumer中的查询所有商品的方法 * @return */ @RequestMapping(value="/list",method=RequestMethod.GET) public List<Product> getAll(){ return this.consumerservice.findAll(); } }
|
2 降级后的异常记录
2.1 创建项目
2.2 添加ProductServiceFallbackFactory类
@Component public class ProductServiceFallbackFactory implements FallbackFactory<ProductConsumerService> { Logger logger = LoggerFactory.getLogger(ProductServiceFallbackFactory.class); @Override public ProductConsumerService create(final Throwable arg0) {
return new ProductConsumerService() {
//能够返回托底数据的fallback方法 @Override public List<Product> findAll() { logger.warn("Fallback Exception: ",arg0); List<Product> list = new ArrayList<>(); list.add(new Product(-1, "我是托底数据")); return list; } }; } }
|
2.3 修改ProductConsumerService
@FeignClient(name="ego-product-provider",fallbackFactory=ProductServiceFallbackFactory.class) public interface ProductConsumerService { //查询所有商品 @RequestMapping(value="/product/findAll",method=RequestMethod.GET) public List<Product> findAll(); } |
五、 可视化的数据监控Hystrix-dashboard
Hystrix-dashboard是一款针对Hystrix进行实时监控的工具,通过Hystrix Dashboard我们可以在直观地看到各Hystrix Command的请求响应时间, 请求成功率等数据。
1 创建项目
2 修改pom文件添加Hystrix-dashboard坐标
<?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.bjsxt</groupId> <artifactId>springcloud-eureka-consumer-ribbon-dashboard</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>springcloud-eureka-consumer</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.13.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <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> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</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-hystrix-dashboard</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
|
3 修改启动类
@EnableCircuitBreaker //开启熔断器 断路器 @EnableEurekaClient @SpringBootApplication @EnableHystrix @EnableHystrixDashboard public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } |
4 Hystrix-dashboard监控中心
4.1 创建项目
4.2 修改pom文件添加Hystrix-dashboard坐标
<?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.bjsxt</groupId> <artifactId>springcloud-eureka-consumer-ribbon-dashboard</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>springcloud-eureka-consumer</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.13.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <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> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</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-hystrix-dashboard</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
|
4.3 修改启动类
@EnableCircuitBreaker //开启熔断器 断路器 @EnableEurekaClient @SpringBootApplication @EnableHystrix @EnableHystrixDashboard public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } |
4.4 启动顺序
先启动服务,在启动监控中心
4.5 访问监控中心
4.6 首页介绍
4.7 监控中心图解
六、 使用Turbine在多个服务与集群情况下收集数据监控
Turbine**是聚合服务器发送事件流数据的一个工具,hystrix的监控中,只能监控单个节点,实际生产中都为集群,因此可以通过turbine来监控集群服务。**
1 创建Turbine项目
1.1 修改pom文件添加turbine坐标
<?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.bjsxt</groupId> <artifactId>springcloud-eureka-consumer-turbine</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>springcloud-eureka-consumer</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.13.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <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> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</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-hystrix-dashboard</artifactId> </dependency> <!-- 添加turbine坐标 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-turbine</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-netflix-turbine</artifactId> </dependency>
</dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> |
1.2 修改配置文件
spring.application.name=eureka-consumer-hystrix-turbine server.port=1002 #设置服务注册中心地址,指向另一个注册中心 eureka.client.serviceUrl.defaultZone=http://user:[email protected]:8761/eureka/,http://user:[email protected]:8761/eureka/ #---------------------------------------turbine-------------------------- #配置Eureka中的serviceId列表,表明监控哪些服务 turbine.appConfig=eureka-consumer-ribbon-threadpool,springcloud-eureka-consumer-feign-fallback #指定聚合哪些集群,多个使用","分割,默认为default。可使用http://.../turbine.stream?cluster={clusterConfig之一}访问 turbine.aggregator.clusterConfig= default # 1. clusterNameExpression指定集群名称,默认表达式appName;此时:turbine.aggregator.clusterConfig需要配置想要监控的应用名称; # 2. 当clusterNameExpression: default时,turbine.aggregator.clusterConfig可以不写,因为默认就是default; # 3. 当clusterNameExpression: metadata['cluster']时,假设想要监控的应用配置了eureka.instance.metadata-map.cluster: ABC, # 则需要配置,同时turbine.aggregator.clusterConfig: ABC turbine.clusterNameExpression="default" |
1.3 修改启动类
@SpringBootApplication @EnableTurbine public class HystrixTurbineApplication { public static void main(String[] args) { SpringApplication.run(HystrixTurbineApplication.class, args); } }
|
2 使用Turbine聚合多个服务
2.1 修改被聚合的项目的pom文件添加dashboard坐标
<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.bjsxt</groupId> <artifactId>springcloud-eureka-consumer-feign-fallback</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.13.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <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> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <!-- 添加Feign坐标 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> <!-- 添加product-service坐标 --> <dependency> <groupId>com.bjsxt</groupId> <artifactId>springcloud-ego-product-service</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</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-hystrix-dashboard</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> |
2.2 修改被监控的服务的启动类
@EnableFeignClients @EnableDiscoveryClient @SpringBootApplication @EnableHystrixDashboard @EnableCircuitBreaker //开启熔断器 断路器 public class ConsumerApplication {
public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } |
2.3 查看turbine整合服务后的监控数据地址
http://localhost:1002/turbine.stream
3 监控集群
七、 采用RabbitMQ,收集监控数据
1 创建Consumer服务
2 修改Consumer服务的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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.bjsxt</groupId> <artifactId>springcloud-eureka-consumer-ribbon-dashboard-mq</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>springcloud-eureka-consumer</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.13.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <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> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</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-hystrix-dashboard</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-netflix-hystrix-stream</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
|
3 修改Consumer的配置文件
spring.application.name=eureka-consumer-ribbon-dashboard server.port=9010 #设置服务注册中心地址,指向另一个注册中心 eureka.client.serviceUrl.defaultZone=http://user:[email protected]:8761/eureka/,http://user:[email protected]:8761/eureka/ spring.rabbitmq.host=192.168.70.139 spring.rabbitmq.port=5672 spring.rabbitmq.username=oldlu spring.rabbitmq.password=123456 spring.rabbitmq.virtualHost=/ |
4 修改Consumer启动类
@EnableCircuitBreaker //开启熔断器 断路器 @EnableEurekaClient @SpringBootApplication @EnableHystrix @EnableHystrixDashboard public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } |
5 创建Turbine项目
6 修改Turbine的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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.bjsxt</groupId> <artifactId>springcloud-eureka-consumer-turbine-mq</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>springcloud-eureka-consumer</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.13.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <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> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-turbine-stream</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
|
7 修改Turbine的配置文件
spring.application.name=eureka-consumer-hystrix-turbine server.port=1002 #设置服务注册中心地址,指向另一个注册中心 eureka.client.serviceUrl.defaultZone=http://user:[email protected]:8761/eureka/,http://user:[email protected]:8761/eureka/ spring.rabbitmq.host=192.168.70.139 spring.rabbitmq.port=5672 spring.rabbitmq.username=oldlu spring.rabbitmq.password=123456 spring.rabbitmq.virtualHost=/ |
8 修改Turbine的启动类
@SpringBootApplication @EnableTurbineStream public class HystrixTurbineApplication { public static void main(String[] args) { SpringApplication.run(HystrixTurbineApplication.class, args); } } |
第四章 如何设计微服务
(Spring Cloud高级)
一、 微服务架构的六种常用设计模式
1 代理设计模式
2 聚合设计模式
3 链条设计模式
4 聚合链条设计模式
5 数据共享设计模式
6 异步消息设计模式
二、 微服务设计模式实战
1 需求分析
2 数据库设计
2.1 User表
CREATE DATABASE; USE `book-user`; /*Table structure for table `user` */ DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` INT(10) NOT NULL AUTO_INCREMENT COMMENT '自增主键', `user_name` VARCHAR(50) DEFAULT NULL COMMENT '用户名', `password` VARCHAR(50) NOT NULL COMMENT '密码', `email` VARCHAR(50) NOT NULL COMMENT 'email', `deleted` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0' COMMENT '删除标志,默认0不删除,1删除', `create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`) ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户表'; INSERT INTO `user`(`id`,`user_name`,`password`,`email`,`deleted`,`create_time`,`update_time`) VALUES (1,'admin','admin','[email protected]',0,'2016-02-02 12:56:31','2016-02-02 14:47:25');
|
2.2 Product表
CREATE DATABASE ; USE `book-product`; DROP TABLE IF EXISTS `product`; CREATE TABLE `product` ( `id` INT(10) NOT NULL AUTO_INCREMENT, `name` VARCHAR(100) DEFAULT NULL COMMENT '产品名称', `status` TINYINT(4) NOT NULL DEFAULT '0' COMMENT '产品状态:0待审,1上架,2下架,3停售,4测试', `price` INT(10) NOT NULL COMMENT '产品价格 单位分', `detail` TEXT COMMENT '产品详情', `deleted` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0' COMMENT '删除标志,默认0不删除,1删除', `create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`) ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='产品信息'; INSERT INTO `product` (`id`, `name`, `status`, `price`, `detail`, `deleted`, `create_time`, `update_time`) VALUES('1','尚学堂-实战java程序设计','1','100','尚学堂-实战java程序设计','0','2017-12-03 09:08:12','2017-12-17 16:44:39'); INSERT INTO `product` (`id`, `name`, `status`, `price`, `detail`, `deleted`, `create_time`, `update_time`) VALUES('2','尚学堂-百战程序员经典案例','1','200','尚学堂-百战程序员经典案例','0','2017-12-03 09:08:12','2017-12-17 16:44:41'); INSERT INTO `product` (`id`, `name`, `status`, `price`, `detail`, `deleted`, `create_time`, `update_time`) VALUES('3','尚学堂-人工智能基础教程','1','300','尚学堂-人工智能基础教程','0','2017-12-17 16:44:35','2017-12-17 16:45:15');
|
2.3 Orders表
CREATE DATABASE ; USE `book-order`; DROP TABLE IF EXISTS `orders`; CREATE TABLE `orders` ( `id` INT(10) NOT NULL AUTO_INCREMENT, `product_id` INT(10) NOT NULL DEFAULT '0' COMMENT '产品ID', `price` INT(10) DEFAULT '0' COMMENT '价格', `user_id` INT(10) DEFAULT '0' COMMENT '用户账号ID', `trade_id` INT(10) DEFAULT '0' COMMENT '交易号ID', `trade_status` TINYINT(1) DEFAULT '0' COMMENT '支付状态 0=未支付 1=已支付', `deleted` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0' COMMENT '删除标志,默认0不删除,1删除', `create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`) ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; |
2.4 Trade表
CREATE DATABASE; USE `book-trade`; DROP TABLE IF EXISTS `trade`; CREATE TABLE `trade` ( `id` INT(10) NOT NULL AUTO_INCREMENT COMMENT 'IID', `order_id` INT(10) NOT NULL COMMENT '订单ID', `user_id` INT(10) NOT NULL COMMENT '用户ID', `price` INT(10) NOT NULL COMMENT '支付价', `pay_status` TINYINT(4) NOT NULL COMMENT '1 未付款 2 付款中 3 付款失败 4 付款完成', `pay_type` TINYINT(4) NOT NULL COMMENT '支付类型:1-支付宝支付,2-网银在线,3-银联,4-微信支付', `gateway_pay_num` VARCHAR(30) DEFAULT NULL COMMENT '网关支付流水号', `gateway_pay_time` DATETIME DEFAULT NULL COMMENT '网关支付时间', `gateway_pay_price` INT(10) NOT NULL DEFAULT '0' COMMENT '网关实际支付金额', `deleted` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0' COMMENT '删除标志,默认0不删除,1删除', `create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`) ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='交易'; |
3 Mybatis的generatorSqlmapCustom工具使用
4 设计product服务
4.1 将sql脚本导入到数据库中
4.2 创建Product服务
4.3 修改Product服务的pom文件
<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.bjsxt</groupId> <artifactId>e-book-product-provider</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.13.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <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> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <!-- 添加product-service坐标 --> <dependency> <groupId>com.bjsxt</groupId> <artifactId>e-book-product-service</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> |
4.4 修改Product服务的配置文件
spring.application.name=e-book-product-provider server.port=9001 #设置服务注册中心地址,指向另一个注册中心 eureka.client.serviceUrl.defaultZone=http://user:[email protected]:8761/eureka/,http://user:[email protected]:8761/eureka/ #--------------db---------------- mybatis.type-aliases-package=com.book.product.pojo mybatis.mapper-locations=classpath:com/book/product/mapper/*.xml spring.datasource.driverClassName=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/book-product?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull spring.datasource.username=root spring.datasource.password=root |
4.5 创建Product-Service服务
4.6 使用工具生成Pojo、Mpper、映射配置文件,添加到项目当中
4.6.1 Product-Provider
4.6.2 Product-Service
4.7 编写一个业务逻辑-查询所有商品信息
4.7.1在Product-Service项目中添加一个ProductService接口
@RequestMapping("/product") public interface ProductService {
//查询所有商品 @RequestMapping(value="/findAll",method=RequestMethod.GET) public List<Product> findAll(); } |
4.7.2在Product-Provider服务中添加ProductServiceImpl类
@Service public class ProductServiceImpl {
@Autowired private ProductMapper productMapper;
/** * 查询所有商品 * @return */ public List<Product> findProductAll(){ ProductExample example = new ProductExample(); List<Product> list = this.productMapper.selectByExampleWithBLOBs(example); return list; } } |
4.7.3在Product服务中添加controller
@RestController public class ProductServiceController implements ProductService { @Autowired private ProductServiceImpl productServiceImple;
@Override public List<Product> findAll() { return this.productServiceImple.findProductAll(); } } |
4.7.4修改Product-provider启动类
@EnableEurekaClient @SpringBootApplication @MapperScan("com.book.product.mapper") public class ProductApplication { public static void main(String[] args) { SpringApplication.run(ProductApplication.class, args); } } |
4.7.5访问服务
5 设计user服务
5.1 将sql脚本导入到数据库中
5.2 创建User-Provider服务
5.3 修改User-Provider服务的pom文件
<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.bjsxt</groupId> <artifactId>e-book-user-provider</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.13.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <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> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <!-- 添加product-service坐标 --> <dependency> <groupId>com.bjsxt</groupId> <artifactId>e-book-user-service</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> |
5.4 修改User-Provider服务的配置文件
spring.application.name=e-book-user-provider server.port=9002 #设置服务注册中心地址,指向另一个注册中心 eureka.client.serviceUrl.defaultZone=http://user:[email protected]:8761/eureka/,http://user:[email protected]:8761/eureka/ #--------------db---------------- mybatis.type-aliases-package=com.book.user.pojo mybatis.mapper-locations=classpath:com/book/user/mapper/*.xml spring.datasource.driverClassName=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/book-user?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull spring.datasource.username=root spring.datasource.password=root |
5.5 创建User-Service服务
5.6 使用工具生成Pojo、Mpper、映射配置文件,添加到项目当中
5.6.1User-Provider
5.6.2User-Service
5.7 编写一个业务逻辑-用户登录
5.7.1在User-service项目中添加一个UserService接口
@RequestMapping("/user") public interface UserService { // 用户登录 @RequestMapping(value = "/login", method = RequestMethod.GET) public User login(@RequestParam("userName") String userName, @RequestParam("password") String password); } |
5.7.2在User-provider服务中添加UserServiceImpl类
@Service public class UserServiceImpl { @Autowired private UserMapper userMapper; /** * 用户登录 * @param userName * @param password * @return */ public User userLogin(String userName ,String password){ UserExample example = new UserExample(); Criteria cri = example.createCriteria(); cri.andUserNameEqualTo(userName); cri.andPasswordEqualTo(password); List<User> list=this.userMapper.selectByExample(example); return list.get(0); } }
|
5.7.3在User-Provider服务中添加controller
@RestController public class UserController implements UserService { @Autowired private UserServiceImpl userServiceImpl; @Override public User login(String userName, String password) { return this.userServiceImpl.userLogin(userName, password); } } |
5.7.4修改User-Provider启动类
@EnableEurekaClient @SpringBootApplication @MapperScan("com.book.user.mapper") public class UserApplication { public static void main(String[] args) { SpringApplication.run(UserApplication.class, args); } } |
5.7.5访问服务
6 设计Order服务
6.1 将sql脚本导入到数据中
6.2 创建order-provider服务
6.3 修该Order-Provider的pom文件
<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.bjsxt</groupId> <artifactId>e-book-order-provider</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.13.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <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> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <!-- 添加product-service坐标 --> <dependency> <groupId>com.bjsxt</groupId> <artifactId>e-book-order-service</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> |
6.4 修改Order-Provider的配置文件
spring.application.name=e-book-order-provider server.port=9003 #设置服务注册中心地址,指向另一个注册中心 eureka.client.serviceUrl.defaultZone=http://user:[email protected]:8761/eureka/,http://user:[email protected]:8761/eureka/ #--------------db---------------- mybatis.type-aliases-package=com.book.order.pojo mybatis.mapper-locations=classpath:com/book/order/mapper/*.xml spring.datasource.driverClassName=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/book-order?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull spring.datasource.username=root spring.datasource.password=root |
6.5 创建Order-Service服务
6.6 使用工具生成Pojo、Mpper、映射配置文件,添加到项目当中
6.6.1Order-Provider
6.6.2Order-Service
6.7 编写一个业务逻辑-查询所有订单
6.7.1在Order-Service项目中添加一个OrderService接口
/** * Order服务接口 * @author Administrator * */ @RequestMapping("/order") public interface OrderService { //查询所有订单 @RequestMapping(value="/findAll",method=RequestMethod.GET) public List<Orders> findAll(); } |
6.7.2在Order-Provider服务中添加OrderServiceImpl类
@Service public class OrderServiceImpl { @Autowired private OrdersMapper orderMapper;
/** * 查询所有订单 */ public List<Orders> findOrderAll(){ OrdersExample example = new OrdersExample(); return this.orderMapper.selectByExample(example); } } |
6.7.3在Order-Provider服务中添加一个controller
@RestController public class OrderController implements OrderService { @Autowired private OrderServiceImpl orderServiceImpl;
@Override public List<Orders> findAll() { return this.orderServiceImpl.findOrderAll(); } } |
6.7.4修改Order-Provider启动类
@EnableEurekaClient @SpringBootApplication @MapperScan("com.book.order.mapper") public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } } |
6.7.5访问服务
7 设计Consumer
7.1 创建项目
7.2 修改pom文件
<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.bjsxt</groupId> <artifactId>e-book-consumer</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.13.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <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> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <!-- 添加Feign坐标 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> <!-- 添加e-book-user-service坐标 --> <dependency> <groupId>com.bjsxt</groupId> <artifactId>e-book-user-service</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <!-- 添加e-book-product-service坐标 --> <dependency> <groupId>com.bjsxt</groupId> <artifactId>e-book-product-service</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <!-- 添加e-book-trade-service坐标 --> <dependency> <groupId>com.bjsxt</groupId> <artifactId>e-book-trade-service</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <!-- 添加e-book-order-service坐标 --> <dependency> <groupId>com.bjsxt</groupId> <artifactId>e-book-order-service</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> |
7.3 修改配置文件
spring.application.name=e-book-consumer server.port=9010 #设置服务注册中心地址,指向另一个注册中心 eureka.client.serviceUrl.defaultZone=http://user:[email protected]:8761/eureka/,http://user:[email protected]:8761/eureka/ |
7.4 修改启动类
@EnableFeignClients @EnableDiscoveryClient @SpringBootApplication public class ConsumerApplication {
public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } }
|
8 添加业务逻辑
模拟内容: 登录 查看产品 下订单
* 1.测试登录 账号 admin admin
* 2.查看所有产品列表
* 3.选第一款产品,下订单
* 4.实现订单交易支付
* 5.查看所有的订单信息
8.1 创建Controller
/** * 创建订单 * @author Administrator * */ @RestController public class ConsumerController { @Autowired private ConsumerOrderService orderService;
@Autowired private ConsumerProductService productService;
@Autowired private ConsumerTradeService tradeService;
@Autowired private ConsumerUserService userService;
/** * 模拟内容: 登录 查看产品 下订单 * 1.测试登录 账号 admin admin * 2.查看所有产品列表 * 3.选第一款产品,下订单 * 4.实现订单交易支付 * 5.查看所有的订单信息 */ @RequestMapping(value="/create",method=RequestMethod.GET) public List<Orders> createOrder(){ return null; }
/** * 用户登录 */ private Integer login(){ return null; } }
|
8.2 创建ConsumerOrderService
/** * 调用订单服务 * @author Administrator * */ @FeignClient("e-book-order-provider") public interface ConsumerOrderService extends OrderService { } |
8.3 创建ConsumerUserService
/** * 调用User服务 * @author Administrator * */ @FeignClient("e-book-user-provider") public interface ConsumerUserService extends UserService { } |
8.4 创建ConsumerProductService
/** * 调用product服务 * @author Administrator * */ @FeignClient("e-book-product-provider") public interface ConsumerProductService extends ProductService { } |
8.5 创建ConsumerTradeService
/** * 调用交易服务 * @author Administrator * */ @FeignClient("e-book-trade-provider") public interface ConsumerTradeService extends TradeService { } |
9 业务逻辑实现
模拟用户登录、查询商品
9.1 模拟用户登录
9.1.1在User-Serivce中添加用户登录接口
/** * User服务接口 * * @author Administrator * */ @RequestMapping("/user") public interface UserService { // 用户登录 @RequestMapping(value = "/login", method = RequestMethod.GET) public User login(@RequestParam("userName") String userName, @RequestParam("password") String password); } |
9.1.2在User-Provider中添加接口实现
@RestController public class UserController implements UserService { @Autowired private UserServiceImpl userServiceImpl; @Override public User login(String userName, String password) { return this.userServiceImpl.userLogin(userName, password); } } |
9.1.3在UserServiceImpl类中完成对数据库的操作
@Service public class UserServiceImpl { @Autowired private UserMapper userMapper; /** * 用户登录 * @param userName * @param password * @return */ public User userLogin(String userName ,String password){ UserExample example = new UserExample(); Criteria cri = example.createCriteria(); cri.andUserNameEqualTo(userName); cri.andPasswordEqualTo(password); List<User> list=this.userMapper.selectByExample(example); if(list != null && list.size() > 0){ return list.get(0); }else{ return null; } } } |
9.2 查询商品
9.2.1在Product-Service中添加查询所有商品接口
/** * Product服务接口 * @author Administrator * */ @RequestMapping("/product") public interface ProductService {
//查询所有商品 @RequestMapping(value="/findAll",method=RequestMethod.GET) public List<Product> findAll(); } |
9.2.2在Product-Provider中添加接口实现
@RestController public class ProductServiceController implements ProductService { @Autowired private ProductServiceImpl productServiceImple;
@Override public List<Product> findAll() { return this.productServiceImple.findProductAll(); } } |
9.2.3在ProductServiceImpl类中完成对数据库的操作
@Service public class ProductServiceImpl {
@Autowired private ProductMapper productMapper;
/** * 查询所有商品 * @return */ public List<Product> findProductAll(){ ProductExample example = new ProductExample(); List<Product> list = this.productMapper.selectByExampleWithBLOBs(example); return list; } }
|
9.3 创建订单
创建订单
9.3.1在Order-Service中添加创建订单接口
/** * Order服务接口 * @author Administrator * */ @RequestMapping("/order") public interface OrderService { //查询所有订单 @RequestMapping(value="/findAll",method=RequestMethod.GET) public List<Orders> findAll();
//添加订单 @RequestMapping(value="/create",method=RequestMethod.POST,consumes=MediaType.APPLICATION_JSON_VALUE) public Integer addOrder(@RequestBody Orders order); }
|
9.3.2在Order-Provider中添加接口实现
@RestController public class OrderController implements OrderService { @Autowired private OrderServiceImpl orderServiceImpl;
@Override public List<Orders> findAll() { return this.orderServiceImpl.findOrderAll(); } @Override public Integer addOrder(@RequestBody Orders order) { return this.orderServiceImpl.createOrder(order); } }
|
9.3.3在OrderServiceImpl类中完成数据库操作
@Service public class OrderServiceImpl { @Autowired private OrdersMapper orderMapper;
/** * 查询所有订单 */ public List<Orders> findOrderAll(){ OrdersExample example = new OrdersExample(); return this.orderMapper.selectByExample(example); }
/** * 添加订单 */ public Integer createOrder(Orders order){ this.orderMapper.insert(order); return order.getId(); } }
|
9.4 完成交易
9.4.1在Trade-Service中添加创建交易接口
/** * Trade服务接口 * * @author Administrator * */ @RequestMapping("/trade") public interface TradeService { // 查询所有交易信息 @RequestMapping(value = "/findAll", method = RequestMethod.GET) public List<Trade> findAll();
@RequestMapping(value="/create" ,method=RequestMethod.POST,consumes=MediaType.APPLICATION_JSON_VALUE) public void addTrade(@RequestBody Trade trade); } |
9.4.2在Trade-Provider中添加接口实现
@RestController public class TradeController implements TradeService { @Autowired private TradeServiceImpl tradeServiceImpl;
@Override public List<Trade> findAll() { return this.tradeServiceImpl.findTradeAll(); } @Override public void addTrade(@RequestBody Trade trade) { this.tradeServiceImpl.addTrade(trade); } }
|
9.4.3在TradeServiceImpl类中完成对数据库的操作
@Service public class TradeServiceImpl { @Autowired private TradeMapper tradeMapper;
/** * 查询所有交易信息 */ public List<Trade> findTradeAll(){ TradeExample example = new TradeExample(); return this.tradeMapper.selectByExample(example); }
/** * 添加交易信息 */ public void addTrade(Trade trade){ this.tradeMapper.insert(trade); } } |
9.5 在交易中更新订单信息
修改Trade的pom文件添加Order-Service、Feign的坐标
<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.bjsxt</groupId> <artifactId>e-book-trade-provider</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.13.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <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> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <!-- 添加product-service坐标 --> <dependency> <groupId>com.bjsxt</groupId> <artifactId>e-book-trade-service</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
<!-- 添加order-service坐标 --> <dependency> <groupId>com.bjsxt</groupId> <artifactId>e-book-order-service</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <!-- 添加Feign坐标 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency>
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> |
9.5.1修改启动类
@EnableFeignClients @EnableEurekaClient @SpringBootApplication @MapperScan("com.book.trade.mapper") public class TradeApplication { public static void main(String[] args) { SpringApplication.run(TradeApplication.class, args); } } |
9.5.2在Trade-Provider服务中添加接口
@FeignClient("e-book-order-provider") public interface ProviderOrderService extends OrderService { } |
9.5.3修改Order-Service添加方法
/** * Order服务接口 * @author Administrator * */ @RequestMapping("/order") public interface OrderService { //查询所有订单 @RequestMapping(value="/findAll",method=RequestMethod.GET) public List<Orders> findAll();
//添加订单 @RequestMapping(value="/create",method=RequestMethod.POST,consumes=MediaType.APPLICATION_JSON_VALUE) public Integer addOrder(@RequestBody Orders order);
//根据订单ID查询订单 @RequestMapping(value="/findOrderById",method=RequestMethod.GET) public Orders findOrderById(@RequestParam("orderid") Integer orderid);
//更新订单 @RequestMapping(value="/updateOrder",method=RequestMethod.POST,consumes=MediaType.APPLICATION_JSON_VALUE) public void updateOrder(@RequestBody Orders order); } |
9.5.4修改Order-Provider的OrderServiceImpl完成对数据库的操作
/** * 根据ID查询订单 */ public Orders findOrderById(Integer id){ return this.orderMapper.selectByPrimaryKey(id); }
/** * 更新订单 */ public void updateOrder(Orders order){ this.orderMapper.updateByPrimaryKey(order); } |
9.5.5修改Order-provider的controller
@RestController public class OrderController implements OrderService { @Autowired private OrderServiceImpl orderServiceImpl;
@Override public List<Orders> findAll() { return this.orderServiceImpl.findOrderAll(); } @Override public Integer addOrder(@RequestBody Orders order) { return this.orderServiceImpl.createOrder(order); } @Override public Orders findOrderById(Integer orderid) { return this.orderServiceImpl.findOrderById(orderid); } @Override public void updateOrder(@RequestBody Orders order) { this.orderServiceImpl.updateOrder(order); } } |
9.5.6在Trade-provider的controller中完成业务实现
@RestController public class TradeController implements TradeService { @Autowired private TradeServiceImpl tradeServiceImpl;
@Autowired private ProviderOrderService providerOrderService;
@Override public List<Trade> findAll() { return this.tradeServiceImpl.findTradeAll(); } @Override public void addTrade(@RequestBody Trade trade) { //添加交易信息 this.tradeServiceImpl.addTrade(trade); //根据ID查询订单 Orders order = this.providerOrderService.findOrderById(trade.getOrderId()); order.setTradeId(trade.getId()); //更新订单 this.providerOrderService.updateOrder(order); } } |
9.6 切记让所有的pojo都实现序列化接口