(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(":").appen d(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:123456@eur eka1:8761/eureka/,http://user:123456@eureka2: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 的常见负载均衡策略
五、 Ribbon 指定其他负载均衡策略
1 修改代码更换负载均衡策略
1.1创建项目
1.2在启动类中添加创建负载均衡策略对象的方法
@EnableEurekaClient @SpringBootApplication public class ConsumerApplication { 7 区域敏感性策略 ZoneAvoidanceRule: 1.以一个区域为单位考察可 用性,对于不可用的区域整个丢 弃,从剩下区域中选可用的 provider 2.如果这个 ip 区域内有一个或多 个实例不可达或响应变慢,都会降 低该 ip 区域内其他 ip 被选中的权 重。 @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.netf lix.loadbalancer.RandomRule
六、 Ribbon 的点对点直连
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.GE T) 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-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.2.3添加全局配置文件
spring.application.name=ego-product-provider server.port=9001 #设置服务注册中心地址,指向另一个注册中心 eureka.client.serviceUrl.defaultZone=http://user:123456@eur eka1:8761/eureka/,http://user:123456@eureka2: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); } }