SpringCloud(1)---入门篇
SpringCloud理解篇
一、微服务概述
1、什么是微服务
目前的微服务并没有一个统一的标准,一般是以业务来划分将传统的一站式应用,拆分成一个个的服务,彻底去耦合,一个微服务就是单功能业务,只做一件事。
与微服务相对的叫巨石 。
2、微服务与微服务架构
- 微服务是一种架构模式或者一种架构风格,提倡将单一应用程序划分成一组小的服务==独立部署==,服务之间相互配合、相互协调,每个服务运行于自己的==进程==中。
- 服务与服务间采用轻量级通讯,如HTTP的RESTful API等
- 避免统一的、集中式的服务管理机制
3、微服务的优缺点
优点
- 每个服务足够内聚,足够小,比较容易聚焦
- 开发简单且效率高,一个服务只做一件事情
- 开发团队小,一般2-5人足以(当然按实际为准)
- 微服务是松耦合的,无论开发还是部署都可以独立完成
- 微服务能用不同的语言开发
- 易于和第三方集成,微服务允许容易且灵活的自动集成部署(持续集成工具有Jenkins,Hudson,bamboo等)
- 微服务易于被开发人员理解,修改和维护,这样可以使小团队更加关注自己的工作成果,而无需一定要通过合作才能体现价值
- 微服务允许你融合最新的技术
- ==微服务只是业务逻辑的代码,不会和HTML,CSS或其他界面组件融合==。
- ==每个微服务都可以有自己的存储能力,数据库可自有也可以统一,十分灵活==。
缺点
- 开发人员要处理分布式系统的复杂性
- 多服务运维难度,随着服务的增加,运维的压力也会增大
- 依赖系统部署
- 服务间通讯的成本
- 数据的一致性
- 系统集成测试
- 性能监控的难度
4、微服务的技术栈
微服务条目 | 落地技术 |
---|---|
服务开发 | SpringBoot,Spring,SpringMVC |
服务配置与管理 | Netflix公司的Archaius、阿里的Diamond等 |
服务注册与发现 | Eureka、Consul、Zookeeper等 |
服务调用 | Rest、RPC、gRPC |
服务熔断器 | Hystrix、Envoy等 |
负载均衡 | Ribbon、Nginx等 |
服务接口调用(客户端调用服务的简化工具) | Feign等 |
消息队列 | Kafka、RabbitMQ、ActiveMQ等 |
服务配置中心管理 | SpringCloudConfig、Chef等 |
服务路由(API网关) | Zuul等 |
服务监控 | Zabbix、Nagios、Metrics、Specatator等 |
全链路追踪 | Zipkin、Brave、Dapper等 |
服务部署 | Docker、OpenStack、Kubernetes等 |
数据流操作开发包 | SpringCloud Stream(封装与Redis,Rabbit,Kafka等发送接收消息) |
事件消息总线 | SpringCloud Bus |
二、SpringCloud入门概述
Spring的三大模块:SpringBoot(构建),Spring Cloud(协调),Spring Cloud Data Flow(连接)
1、SpringCloud是什么
- 分布式系统的简化版(官方介绍)
- SpringCloud基于SpringBoot提供了一整套微服务的解决方案,包括服务注册与发现,配置中心,全链路监控,服务网关,负载均衡,熔断器等组件,除了基于Netflix的开源组件做高度抽象封装之外,还有一些选型中立的开源组件
- SpringCloud利用SpringBoot的开发便利性巧妙地简化了分布式系统的基础设施开发,SpringCloud为开发人员提供了快速构建分布式系统的一些工具,包括配置管理、服务发现、断路器、路由、微代理、事件总线,全局所、决策精选、分布式会话等等,他们都可以用SpringBoot的开发风格做到一键启动和部署。
- ==一句话概括:SpringCloud是分布式微服务架构下的一站式解决方案,是各个微服务架构落地技术的几何体,俗称微服务全家桶==
2、SpringCloud和SpringBoot的关系
SpringBoot:专注于快速方便的开发单个个体微服务(关注微观)
SpringCloud:关注全局的微服务协调治理框架,将SpringBoot开发的一个个单体微服务组合并管理起来(关注宏观)
注意:SpringBoot可以离开SpringCloud独立使用,但是SpringCloud不可以离开SpringBoot,属于依赖关系
3、Dubbo和SpringCloud比较
Dubbo | Spring | |
---|---|---|
服务注册中心 | Zookeeper | Spring Cloud Netfilx Eureka |
服务调用方式 | RPC | REST API |
服务监控 | Dubbo-monitor | Spring Boot Admin |
断路器 | 不完善 | Spring Cloud Netflix Hystrix |
服务网关 | 无 | Spring Cloud Netflix Zuul |
分布式配置 | 无 | Spring Cloud Config |
服务跟踪 | 无 | Spring Cloud Sleuth |
消息总线 | 无 | Spring Cloud Bus |
数据流 | 无 | Spring Cloud Stream |
批量任务 | 无 | Spring Cloud Task |
最大区别
(1)Spring Cloud抛弃了RPC通讯,采用基于HTTP的REST方式。Spring Cloud牺牲了服务调用的性能,但是同时也避免了原生RPC带来的问题。REST比RPC更为灵活,不存在代码级别的强依赖,在强调快速演化
的微服务环境下,显然更合适。
(2)Dubbo像组装机,Spring Cloud像一体机
(3)社区的支持与力度:Dubbo曾经停运了5年,虽然重启了,但是对于技术发展的新需求,还是需要开发者自行去拓展,对于中小型公司,显然显得比较费时费力,也不一定有强大的实力去修改源码
总结
解决的问题域不一样:Dubbo的定位是一款RPC框架,Spring Cloud的目标是微服务架构下的一站式解决方案
Eureka服务注册与发现
GitHub地址:https://github.com/yudiandemingzi/spring-cloud-study
一、Eureka概述
1、Eureka特点
(1) Eureka是一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移。
(2) Eureka 主管服务注册与发现,在微服务中,以后了这两者,只需要使用服务的标识符(==就是那个在每个服务的yml文件中取得服务名称==),
就可以访问到服务,不需要修改服务调用的配置文件。
(3) Eureka遵循AP原则(高可用,分区容错性),因为使用了自我保护机制所以保证了高可用。
2、Eureka两大组件
两大组件:Eureka Server(提供注册服务)、 Eureka Client(JAVA客户端,负责发送心跳)
系统中的其他微服务使用Eureka客户端连接到Eureka服务端维持心跳连接(即注册)。SpringCloud的其他模块可以通过Eureka Server 来发现系统中的微服务并加以调用
3、Eureka三大角色
Eureka Server:提供服务注册和发现
Service Provider:服务提供方,将自身服务注册到Eureka,从而使服务消费方能够找到
Service Consumer:服务消费方,从Eureka获取注册服务列表,从而能够消费服务。
二、Eureka Server服务注册中心
1、pom.xml
<!--注册服务中心的jar要多个-server--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency>
2、application.yml
server: port: 7001 eureka: instance: hostname: localhost client: #声明自己是个服务端 registerWithEureka: false #false表示不向注册中心注册自己 fetchRegistry: false #false表示自己就是注册中心,职责是维护实例,不参加检索 serviceUrl: #设置eureka server的交互地址,即对外暴露的地址 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
3、启动类
//注意:要在类前加@EnableEurekaServer标注 @SpringBootApplication @EnableEurekaServer public class Eureka7001_APP { public static void main(String[] args) { SpringApplication.run(Eureka7001_APP.class,args); } }
运行结果:输入:http://localhost:7001/
三、Service Provider服务提供方
假设这个商品微服务。
1、pom.xml
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
2、application.yml
server: port: 8001 #指定注册中心地址 eureka: client: serviceUrl: defaultZone: http://localhost:7001/eureka/ #服务的名称 spring: application: name: product-service
3、启动类
@SpringBootApplication public class ProductApplication { public static void main(String[] args) { SpringApplication.run(ProductApplication.class, args); } }
4、启动后查看服务注册中心
发现在服务注册中心已经注册了一个服务
5、换端口号再启动一个
6、在看服务中心
这就是搭建了商品微服务集群。
四、Service Consumer服务消费方
其实服务方和消费在配置时候没有任何区别,它们都属于Eureka Client组件。只是涉及服务间的调用,所以就把被调方称为提供方,调用方称为消费方。就好比订单微服务,
订单服务肯定需要去调商品微服务,所以这个订单微服务对于商品来讲可以理解服务提供方。一个微服务即可以是服务方也同时是提供方。
1、pom.xml
<!--这个对于每个不是注册中心的微服务都要添加--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
2、application.yml
server: port: 9001 #指定注册中心地址 eureka: client: serviceUrl: defaultZone: http://localhost:7001/eureka/ #服务的名称 spring: application: name: order-service
3、启动类
@SpringBootApplication public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } }
4、查看注册中心
发现订单微服务也成功注册到注册中心
至于订单微服务如何调商品微服务呢,下一遍博客在写咯。
SpringCloud(4)---Ribbon
本篇模拟订单服务调用商品服务,同时商品服务采用集群部署。
注册中心服务端口号7001,订单服务端口号9001,商品集群端口号:8001、8002、8003。
各服务的配置文件这里我这边不在显示了,和上篇博客配置一样。博客地址:SpringCloud(3)---Eureka服务注册与发现
一、商品中心服务端
1、pom.xml
<?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.jincou</groupId> <artifactId>product</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>product</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.4.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> <!--定义当前springcloud版本--> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> </properties> <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> <!--表明是Eureka Client客户端--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
2、Product商品实体类
@Data @NoArgsConstructor @AllArgsConstructor public class Product implements Serializable { private int id; //商品名称 private String name; //价格,分为单位 private int price; //库存 private int store; }
3、ProductService商品接口
public interface ProductService { //查找所有商品 List<Product> listProduct(); //根据商品ID查找商品 Product findById(int id); }
4、ProductServiceImpl商品实现类
@Service public class ProductServiceImpl implements ProductService { private static final Map<Integer, Product> daoMap = new HashMap<>(); //模拟数据库商品数据 static { Product p1 = new Product(1, "苹果X", 9999, 10); Product p2 = new Product(2, "冰箱", 5342, 19); Product p3 = new Product(3, "洗衣机", 523, 90); Product p4 = new Product(4, "电话", 64345, 150); daoMap.put(p1.getId(), p1); daoMap.put(p2.getId(), p2); daoMap.put(p3.getId(), p3); daoMap.put(p4.getId(), p4); } @Override public List<Product> listProduct() { Collection<Product> collection = daoMap.values(); List<Product> list = new ArrayList<>(collection); return list; } @Override public Product findById(int id) { return daoMap.get(id); } }
5、ProductController
@RestController @RequestMapping("/api/v1/product") public class ProductController { //集群情况下,用于订单服务查看到底调用的是哪个商品微服务节点 @Value("${server.port}") private String port; @Autowired private ProductService productService; //获取所有商品列表 @RequestMapping("list") public Object list(){ return productService.listProduct(); } //根据id查找商品详情 @RequestMapping("find") public Object findById(int id){ Product product = productService.findById(id); Product result = new Product(); BeanUtils.copyProperties(product,result); result.setName( result.getName() + " data from port="+port ); return result; } }
6、测下该服务接口是否成功
二、订单中心服务端
1、pom.xml
<?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.jincou</groupId> <artifactId>order</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>order</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.4.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> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> </properties> <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-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
2、ProductOrder商品订单实体
@Data @AllArgsConstructor @NoArgsConstructor public class ProductOrder implements Serializable { //订单ID private int id; // 商品名称 private String productName; //订单号 private String tradeNo; // 价格,分 private int price; //订单创建时间 private Date createTime; //用户id private int userId; //用户名 private String userName; }
3、ProductOrderService订单接口
/** * 订单业务类 */ public interface ProductOrderService { //下单接口 ProductOrder save(int userId, int productId); }
4、ProductOrderServiceImpl订单实现类
@Service public class ProductOrderServiceImpl implements ProductOrderService { @Autowired private RestTemplate restTemplate; @Override public ProductOrder save(int userId, int productId) { //product-service是微服务名称(这里指向的商品微服务名称),api/v1/product/find?id=? 就是商品微服务对外的接口 Map<String, Object> productMap = restTemplate.getForObject("http://product-service/api/v1/product/find?id=" + productId, Map.class); ProductOrder productOrder = new ProductOrder(); productOrder.setCreateTime(new Date()); productOrder.setUserId(userId); productOrder.setTradeNo(UUID.randomUUID().toString()); //获取商品名称和商品价格 productOrder.setProductName(productMap.get("name").toString()); productOrder.setPrice(Integer.parseInt(productMap.get("price").toString())); //因为在商品微服务配置了集群,所以这里打印看下调用了是哪个集群节点,输出端口号。 System.out.println(productMap.get("name").toString()); return productOrder; } }
5、OrderController类
@RestController @RequestMapping("api/v1/order") public class OrderController { @Autowired private ProductOrderService productOrderService; @RequestMapping("save") public Object save(@RequestParam("user_id")int userId, @RequestParam("product_id") int productId){ return productOrderService.save(userId, productId); } }
6、SpringBoot启动类
@SpringBootApplication public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } //当添加@LoadBalanced注解,就代表启动Ribbon,进行负载均衡 @LoadBalanced @Bean public RestTemplate restTemplate() { return new RestTemplate(); } }
7、接口测试
多调几次接口,看后台打印
发现订单服务去掉商品服务的时候,不是固定节点,而且集群的每个节点都有可能。所以通过Ribbon实现了负载均衡。
三、Ribbon源码分析
1、@LoadBalanced注解作用
在springcloud中,引入Ribbon来作为客户端时,负载均衡使用的是被@LoadBalanced
修饰的RestTemplate
对象。
RestTemplate 是Spring自己封装的http请求的客户端,也就是说它只能发送一个正常的Http请求,这跟我们要求的负载均衡是有出入的,还有就是这个请求的链接上的域名
是我们微服的一个服务名,而不是一个真正的域名,那它是怎么实现负载均衡功能的呢?
我们来看看RestTemplate的父类InterceptingHttpAccessor。
从源码我们可以知道InterceptingHttpAccessor中有一个拦截器列表List<ClientHttpRequestInterceptor>,如果这个列表为空,则走正常请求流程,如果不为空则走
拦截器,所以只要给RestTemplate添加拦截器,而这个拦截器中的逻辑就是Ribbon的负载均衡的逻辑。通过为RestTemplate配置添加拦截器。
具体的拦截器的生成在LoadBalancerAutoConfiguration这个配置类中,所有的RestTemplate的请求都会转到Ribbon的负载均衡器上
(当然这个时候如果你用RestTemplate发起一个正常的Http请求时走不通,因为它找不到对应的服务。)这样就实现了Ribbon的请求的触发。
2.拦截器都做了什么?
上面提到过,发起http后请求后,请求会到达到达拦截器中,在拦截其中实现负载均衡,先看看代码:
我们可以看到在intercept()方法中实现拦截的具体逻辑,首先会根据传进来的请求链接,获取微服的名字serviceName,然后调用LoadBalancerClient的
execute(String serviceId, LoadBalancerRequest<T> request)方法,这个方法直接返回了请求结果,所以正真的路由逻辑在LoadBalancerClient的实现类中,
而这个实现类就是RibbonLoadBalancerClient,看看execute()的源码:
首先是获得均衡器ILoadBalancer这个类上面讲到过这是Netflix Ribbon中的均衡器,这是一个抽象类,具体的实现类是ZoneAwareLoadBalancer上面也讲到过,
每一个微服名对应一个均衡器,均衡器中维护者微服名下所有的服务清单。getLoadBalancer()方法通过serviceId获得对应的均衡器,getServer()方法通过对应的均衡器
在对应的路由的算法下计算得到需要路由到Server,Server中有该服务的具体域名等相关信息。得到了具体的Server后执行正常的Http请求,整个请求的负载均衡逻辑就完成了。
在微服中Ribbon和 Hystrix通常是一起使用的,其实直接使用Ribbon和Hystrix实现服务间的调用并不是很方便,通常在Spring Cloud中我们使用Feign完成服务间的调用,
而Feign是对Ribbon和Hystrix做了进一步的封装方便大家使用,对Ribbon的学习能帮你更好的完成Spring Cloud中服务间的调用。
SpringCloud(5)---Feign服务调用
上一篇写了通过Ribbon进行服务调用,这篇其它都一样,唯一不一样的就是通过Feign进行服务调用。
注册中心和商品微服务不变,和上篇博客一样,具体参考:SpringCloud(4)---Ribbon服务调用,源码分析
这边只重写订单微服务。
项目代码GitHub地址:https://github.com/yudiandemingzi/spring-cloud-study
一、OrderService 订单微服务
1、pom.xml
这里相对于上一篇的订单微服务只要新添加一个jar包
<!--feign依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
2、application.yml
server: port: 9001 #指定注册中心地址 eureka: client: serviceUrl: defaultZone: http://localhost:7001/eureka/ #服务的名称 spring: application: name: order-service #自定义负载均衡策略(一般不用配用默认的) product-service: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
3、SpringBoot启动类
@SpringBootApplication //添加@EnableFeignClients注解 @EnableFeignClients public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } }
4、ProductOrderServiceImpl订单接口实现类
订单实体类和订单接口这里就不写类,和上篇一样
@Service public class ProductOrderServiceImpl implements ProductOrderService { @Autowired private ProductClient productClient; @Override public ProductOrder save(int userId, int productId) { //获取json格式的字符串数据 String response = productClient.findById(productId); //Json字符串转换成JsonNode对象 JsonNode jsonNode = JsonUtils.str2JsonNode(response); //将数据封装到订单实体中 ProductOrder productOrder = new ProductOrder(); productOrder.setCreateTime(new Date()); productOrder.setUserId(userId); productOrder.setTradeNo(UUID.randomUUID().toString()); //获取商品名称和商品价格 productOrder.setProductName(jsonNode.get("name").toString()); productOrder.setPrice(Integer.parseInt(jsonNode.get("price").toString())); //因为在商品微服务配置了集群,所以这里打印看下调用了是哪个集群节点,输出端口号。 System.out.println(jsonNode.get("name").toString()); return productOrder; } }
5、ProductClient类
可以把这里类理解成,就是你需要调用的微服务的controller层(这里指商品微服务),这样相对于Ribbon来讲代码的可读性就高多了。
/** * 商品服务客户端 * name = "product-service"是服务端名称 */ @FeignClient(name = "product-service") public interface ProductClient { //这样组合就相当于http://product-service/api/v1/product/find @GetMapping("/api/v1/product/find") String findById(@RequestParam(value = "id") int id); }
6、JsonUtils工具类
/** * json工具类 */ public class JsonUtils { private static final ObjectMapper objectMappper = new ObjectMapper(); //json字符串转JsonNode对象的方法 public static JsonNode str2JsonNode(String str){ try { return objectMappper.readTree(str); } catch (IOException e) { return null; } } }
7、OrderController类
@RestController @RequestMapping("api/v1/order") public class OrderController { @Autowired private ProductOrderService productOrderService; @RequestMapping("save") public Object save(@RequestParam("user_id")int userId, @RequestParam("product_id") int productId){ return productOrderService.save(userId, productId); } }
8、查看运行结果
同时同样它可以达到负载均衡的效果。
二、概念讲解
在使用Feign的时候,要注意使用requestBody,应该使用@PostMapping
1、执行流程
总到来说,Feign的源码实现的过程如下:
(1)首先通过@EnableFeignCleints注解开启FeignCleint
(2)根据Feign的规则实现接口,并加@FeignCleint注解
(3)程序启动后,会进行包扫描,扫描所有的@ FeignCleint的注解的类,并将这些信息注入到ioc容器中。
(4)当接口的方法被调用,通过jdk的代理,来生成具体的RequesTemplate
(5)RequesTemplate在生成Request
(6)Request交给Client去处理,其中Client可以是HttpUrlConnection、HttpClient也可以是Okhttp
(7)最后Client被封装到LoadBalanceClient类,这个类结合类Ribbon做到了负载均衡。
2、Feign和Ribbon比较优点
(1) feign本身里面就包含有了ribbon,只是对于ribbon进行进一步封装
(2) feign自身是一个声明式的伪http客户端,写起来更加思路清晰和方便
(3) fegin是一个采用基于接口的注解的编程方式,更加简便
最后推荐一篇对源码解析不错的博客:深入理解Feign之源码解析。