微服务的消费模式
基于http
的客户端经常被用作微服务的消费者,因为http
本身是平台无关的、语言无关的,所以基于http
的客户端往往会被广大的社区支持
服务直连模式
特点:
- 简洁明了,只要传入一个
URL
,就能直接连过去,获取到资源 - 平台语言无关性,非常直白,不需要特定框架、技术,能实现平台无关、语言无关
- 无法保证服务的可用性,当需要链接某个
IP
下的某个资源时,如果这个IP
地址或这个IP
地址所绑定的主机宕机了,这个资源就无法拿到,因为服务直连模式无法做到负载均衡,也就无法保证服务的可用性 - 生产环境比较少用
客户端发现模式
- 服务实例启动后,将自己的位置信息提交到服务注册表
- 客户端从服务注册表进行查询,来获取可用的服务实例
- 客户端自行使用负载均衡算法从多个服务实例中选择出一个
服务端发现模式
与客户端发现模式最大的区别在于:负载均衡不是由客户端来做,而是在服务端实现,负载均衡器是独立部署在服务端的
常见微服务的消费者
Apache HttpClient
用来提供高效、功能丰富的http
协议的客户端工具包,能支持http
协议最新的版本和建议。Apache HttpClient
能对JDK
提供一些非常好的补充,方便开发人员测试基于http
的接口,从而提高开发效率及代码健壮性
Apache HttpClient的用法
添加依赖
//依赖关系
dependencies {
//添加Apache HttpClient依赖
compile('org.apache.httpcomponents:httpclient:4.5.6')
}
注入restTemplate
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
//rest配置类,配置restTemplate
public class RestConfiguration {
@Autowired
private RestTemplateBuilder builder;
@Bean
public RestTemplate restTemplate() {
return builder.build();
}
}
使用
@Service
public class WeatherDataServiceImpl implements WeatherDataService {
@Autowired
private RestTemplate restTemplate;
private WeatherResponse doGetWeather(String uri) {
ResponseEntity<String> respString = restTemplate.getForEntity(uri, String.class);
//...
}
//...
}
Ribbon
Ribbon
是Spring Cloud
中的一个组件,是基于Netflix Ribbon
实现客户端负载均衡的一个工具,基于http
和TCP
来实现客户端的负载均衡
Ribbon
的每个负载均衡器一起协作,可以根据需要与远程服务器进行交互,来获取包含命名客户端名词的集合,Ribbon
经常与Eureka
结合使用,在典型的分布式部署中,Eureka
为所有的微服务实例提供服务注册,Ribbon
提供服务消费的客户端
Ribbon
有很多负载均衡的算法
Ribbon的用法
添加依赖
//依赖关系
dependencies {
//添加Spring Cloud Starter Netflix Ribbon依赖
compile('org.springframework.cloud:spring-cloud-starter-netflix-ribbon')
}
build.gradle
完整代码如下:
//buildscript代码块中脚本优先执行
buildscript {
//ext用于定义动态属性
ext {
springBootVersion = '2.0.0.M3'
}
//使用了Maven的中央仓库及Spring自己的仓库(也可以指定其他仓库)
repositories {
//mavenCentral()
maven{ url "https://repo.spring.io/snapshot" }
maven{ url "https://repo.spring.io/milestone" }
maven{ url "http://maven.aliyun.com/nexus/content/groups/public/" }
}
//依赖关系
dependencies {
//classpath声明了在执行其余的脚本时,ClassLoader可以使用这些依赖项
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
//使用插件
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
//指定了生成的编译文件的版本,默认是打成了jar包
group = 'com.study.spring.cloud'
version = '1.0.0'
//指定编译.java文件的JDK版本
sourceCompatibility = 1.8
//使用了Maven的中央仓库及Spring自己的仓库(也可以指定其他仓库)
repositories {
//mavenCentral()
maven{ url "https://repo.spring.io/snapshot" }
maven{ url "https://repo.spring.io/milestone" }
maven{ url "http://maven.aliyun.com/nexus/content/groups/public/" }
}
ext {
springCloudVersion = 'Finchley.M2'
}
//依赖关系
dependencies {
//该依赖用于编译阶段
compile('org.springframework.boot:spring-boot-starter-web')
//Eureka Client
compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client')
//添加Spring Cloud Starter Netflix Ribbon依赖
compile('org.springframework.cloud:spring-cloud-starter-netflix-ribbon')
//该依赖用于测试阶段
testCompile('org.springframework.boot:spring-boot-starter-test')
}
//Spring Cloud依赖管理
dependencyManagement{
imports{
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
注入
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
@RibbonClient(name = "ribbon-client",configuration = RibbonConfiguration.class)
public class RestConfiguration {
@Autowired
private RestTemplateBuilder builder;
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return builder.build();
}
}
配置
import com.netflix.loadbalancer.IPing;
import com.netflix.loadbalancer.PingUrl;
import org.springframework.cloud.netflix.ribbon.ZonePreferenceServerListFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RibbonConfiguration {
@Bean
public ZonePreferenceServerListFilter serverListFilter(){
ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
filter.setZone("myZone");
return filter;
}
@Bean
public IPing ribbonPing(){
return new PingUrl();
}
}
使用
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class CityController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/cities")
public String listCity(){
//通过应用名词来查找
String body=restTemplate.getForEntity("http://msa-weather-city-eureka/cities", String.class).getBody();
return body;
}
}
应用配置application.properties
#应用名称
spring.application.name=micro-weather-eureka-client-ribbon
#指定Eureka服务器地址
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
Feign
Feign
是一款声明式的Web
服务客户端,这使得编写Web
服务客户端更容易,同时它具有可拔插的注释支持,Spring Cloud
对Feign
有集成,在使用Feign
的时候,Spring Cloud
同时会集成Ribbon
和Eureka
来提供负载均衡http
客户端
集成Feign
开发环境
JDK8+
Gradle4+
Redis 3.2.100
Spring Boot 2.0.0.M3
Spring Cloud Starter Netflix Eureka Client Finchley.M2
Spring Cloud Starter OpenFeign Finchley.M2
创建项目
复制之前的micro-weather-eureka-client
项目,将副本改名为micro-weather-eureka-client-feign
修改源码
修改build.gradle
配置,添加Feign
依赖:
//依赖关系
dependencies {
//Eureka Client
compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client')
//Feign
compile('org.springframework.cloud:spring-cloud-starter-openfeign:2.0.0.M3')
//该依赖用于测试阶段
testCompile('org.springframework.boot:spring-boot-starter-test')
}
此处要注意Feign
依赖的版本,如果不加版本号,将默认下载2.0.0.M2
版本,源码中是没有LoadBalancedRetryFactory
的,运行会导致以下报错:
java.lang.ClassNotFoundException: org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory
修改com.study.spring.cloud.weather
包下的Application
类,加入@EnableFeignClients
注解:
package com.study.spring.cloud.weather;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
/*
* @SpringBootApplication注解声明Spring Boot应用
* 作用等同于@Configuration, @EnableAutoConfiguration, @ComponentScan,
* 简化Spring配置
*/
@SpringBootApplication
//启用可发现的客户端
@EnableDiscoveryClient
//启用Feign
@EnableFeignClients
//Application类一定要处于整个工程的根目录下,这样它才能根据配置去扫描子节点下的Spring的Bean
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
新建com.study.spring.cloud.weather.service
包,在包下新建接口CityClient
:
package com.study.spring.cloud.weather.service;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
//指明服务地址
@FeignClient("mas-weather-city-eureka")
public interface CityClient {
@GetMapping("/cities")
String listCity();
}
在com.study.spring.cloud.weather.controller
包下新建类CityController
:
package com.study.spring.cloud.weather.controller;
import com.study.spring.cloud.weather.service.CityClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
//用于处理rest请求的controller
@RestController
public class CityController {
@Autowired
private CityClient cityClient;
@GetMapping("/cities")
public String listCity() {
//通过Feign客户端来查找
String body=cityClient.listCity();
return body;
}
}
修改application.properties
配置文件:
#应用名称
spring.application.name=micro-weather-eureka-client-feign
#注册服务器的URL
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
#请求服务时的超时时间
feign.client.config.feignName.connect-timeout=5000
#读数据时的超时时间
feign.client.config.feignName.read-timeout=5000
运行
- 先在
IDE
上运行micro-weather-eureka-server
- 再通过命令行运行
msa-weather-city-eureka
:
因为之前已经编译过,直接进入E:\workspace\workspace-study\springcloud-study\imooc-spring-cloud-study\msa-weather-city-eureka\build\libs
目录,
再通过命令java -jar msa-weather-city-eureka-1.0.0.jar --server.port=8081
运行jar
包即可 - 在
IDE
上运行micro-weather-eureka-client-feign
,运行结果如下:
- 访问
http://localhost:8761
页面,可以看到Eureka
的管理页面:
- 访问
http://localhost:8080/cities
页面: