现在服务提供方已经可以通过Eureka进行注册了,但对于服务的消费者,目前并没有处理,对于服务的消费方,也应该连接上eureka,进行服务的获取,这个时候就应该使用Ribbon这个组件了。
现在服务提供方已经可以通过Eureka进行注册了,但对于服务的消费者,目前并没有处理,对于服务的消费方,也应该连接上eureka,进行服务的获取,这个时候就应该使用Ribbon这个组件了。
代码Git地址:https://gitee.com/hankin_chj/springcloud-micro-service.git
一、Ribbon基本使用
1、Ribbon配置介绍
1.1、对应的pom文件如下
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
注意:如果是Edgware或之前的版本,ribbon文件如下:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency
在用到负载均衡之前,先要到eureka中获取相关的服务,所以我们这一块依然需要用到eureka,但eureka中已经内置集成了ribbon,所以在pom文件中,并不需要显示引入ribbon的依赖。
1.2、bootstrap.properties配置文件可以指定某个微服务配置
springcloud-service2020\springcloud-web\src\main\resources\bootstrap.properties
spring.application.name=micro-web
server.port=8083
#eureka.client.serviceUrl.defaultZone=http\://localhost\:8763/eureka/
eureka.client.serviceUrl.defaultZone=http://admin:admin@localhost:8763/eureka/
#服务续约,心跳的时间间隔
eureka.instance.lease-renewal-interval-in-seconds=30
#如果从前一次发送心跳时间起,90 秒没接受到新的心跳,剔除服务
eureka.instance.lease-expiration-duration-in-seconds=90
#表示 eureka client 间隔多久去拉取服务注册信息,默认为 30 秒
eureka.client.registry-fetch-interval-seconds=30
# 关闭ribbon访问注册中心Eureka Server发现服务,但是服务依旧会注册。
#true使用eureka false不使用
ribbon.eureka.enabled=true
#指定调用的节点 localhost:8001 localhost:8002 localhost:8003
micro-order.ribbon.listOfServers=localhost:8001,localhost:8002,localhost:8003
#单位ms ,请求连接超时时间
micro-order.ribbon.ConnectTimeout=1000
#单位ms ,请求处理的超时时间
micro-order.ribbon.ReadTimeout=2000
micro-order.ribbon.OkToRetryOnAllOperations=true
# 切换实例的重试次数
micro-order.ribbon.MaxAutoRetriesNextServer=2
#对当前实例的重试次数 当Eureka中可以找到服务,但是服务连不上时将会重试
micro-order.ribbon.MaxAutoRetries=2
#
micro-order.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
micro-order.ribbon.NFLoadBalancerPingClassName=com.netflix.loadbalancer.PingUrl
2、为consumer集成Ribbon
2.1、consumer服务修改pom文件,增加eureka的支持:
<!-- eureka中已经内置集成了ribbon,所以并不需要显示引入ribbon的依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.2、consumer服务修改RestConfig配置类
在获取RestTemplate对象的时候加入Ribbon的配置信息@LoadBalanced
@Configuration
public class RestConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
/**
* 服务提供方目前已经使用了密码验证,这个时候服务的消费方如果想直接访问就不可能了,
* 这个时候一个以头的信息进行处理,然后使用Base64进行加密处理后才能得到正确的访问路径
*/
@Bean
public HttpHeaders getHeaders() { // 要进行一个Http头信息配置
HttpHeaders headers = new HttpHeaders(); // 定义一个HTTP的头信息
String auth = "admin:admin"; // 认证的原始信息
byte[] encodedAuth = Base64.getEncoder()
.encode(auth.getBytes(Charset.forName("US-ASCII"))); // 进行一个加密的处理
String authHeader = "Basic " + new String(encodedAuth);
headers.set("Authorization", authHeader);
return headers;
}
}
2.3、consumer服务增加Eureka服务注册信息
consumer服务修改application.yml文件,增加Eureka服务注册相关信息
server:
port: 80
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://admin:admin@eureka1:7001/eureka,http://admin:admin@eureka2:7002/eureka,http://admin:admin@eureka3:7003/eureka
2.4、启动类增加Eureka客户端的配置注解
Consumer服务员修改项目启动类ConsumerApp,增加Eureka客户端的配置注解@EnableEurekaClient。
2.5、修改ConsumerProductController控制器
现在在eureka中注册的服务名称都是大写字母:SPRINGCLOUD-MICRO-PRODUCT
@RestController
@RequestMapping("/consumer")
public class ConsumerProductSecurityController {
// 普通方式访问product服务接口
// public static final String PRODUCT_GET_URL = "http://localhost:8080/prodcut/get/";
// public static final String PRODUCT_LIST_URL="http://localhost:8080/prodcut/list/";
// public static final String PRODUCT_ADD_URL = "http://localhost:8080/prodcut/add/";
// TODO 通过使用ribbon访问product服务
public static final String PRODUCT_GET_URL = "http://SPRINGCLOUD-MICRO-PRODUCT/prodcut/get/";
public static final String PRODUCT_LIST_URL="http://SPRINGCLOUD-MICRO-PRODUCT/prodcut/list/";
public static final String PRODUCT_ADD_URL = "http://SPRINGCLOUD-MICRO-PRODUCT/prodcut/add/";
@Resource
private RestTemplate restTemplate;
@Resource
private HttpHeaders httpHeaders;
/**
* 添加了spring security以后,访问接口需要密码验证,需要在请求头HttpHeaders中添加验证信息
*/
@RequestMapping("/product/get2")
public Object getProduct2(long id) {
Product product = restTemplate.exchange(PRODUCT_GET_URL + id,HttpMethod.GET,
new HttpEntity<Object>(httpHeaders), Product.class).getBody();
return product;
}
@RequestMapping("/product/list2")
public Object listProduct2() {
List<Product> list = restTemplate.exchange(PRODUCT_LIST_URL,HttpMethod.GET,
new HttpEntity<Object>(httpHeaders), List.class).getBody();
return list;
}
@RequestMapping("/product/add2")
public Object addPorduct2(Product product) {
Boolean result = restTemplate.exchange(PRODUCT_ADD_URL, HttpMethod.POST,
new HttpEntity<Object>(product,httpHeaders), Boolean.class).getBody();
return result;
}
}
启动服务,访问地址:http://localhost/consumer/product/list2
可以返回结果,这个时候Ribbon与Eureka已经整合成功。
[{"productId":null,"productName":"java编程","productDesc":"springcloud"},
{"productId":null,"productName":"Springboot","productDesc":"springcloud"},
{"productId":null,"productName":"西游记","productDesc":"springcloud"},
{"productId":null,"productName":"水浒传","productDesc":"springcloud"},
{"productId":null,"productName":"西厢记","productDesc":"springcloud"}]
3、ribbon服务调用restTemplate使用说明
3.1、restTemplate调用代码:
@Slf4j
@Service
@Scope(proxyMode = ScopedProxyMode.INTERFACES)
public class UserServiceImpl implements UserService {
private AtomicInteger s = new AtomicInteger();
private AtomicInteger f = new AtomicInteger();
public static String SERVIER_NAME = "micro-order";
@Autowired
private RestTemplate restTemplate;
@Override
public String queryContents() {
s.incrementAndGet();
//TODO 注意:getForObject是 getForEntity() 的封装,包含了请求头等信息
String results = restTemplate.getForObject("http://"
+ SERVIER_NAME + "/queryUser", String.class);
return results;
}
}
通过getForObject方法可以掉到用micro-order服务的,queryUser接口。然后在调用期间会存在负载均衡,micro-order服务对应有几个服务实例就会根据负载均衡算法选择某一个去调用。
3.2、Get请求说明:
getForEntity:此方法有三种重载形式,分别为:
getForEntity(String url, Class<T> responseType)
getForEntity(String url, Class<T> responseType, Object... uriVariables)
getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables)
getForEntity(URI url, Class<T> responseType)
注意:此方法返回的是一个包装对象ResponseEntity<T>其中T为responseType传入类型,想拿到返回类型需要使用这个包装类对象的getBody()方法。
getForObject:此方法也有三种重载形式,这点与 getForEntity 方法相同:
getForObject(String url, Class<T> responseType)
getForObject(String url, Class<T> responseType, Object... uriVariables)
getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables)
getForObject(URI url, Class<T> responseType)
注意:此方法返回的对象类型为 responseType 传入类型
3.2、Post请求
post请求 get请求都有*ForEntity 和*ForObject 方法,其中参数列表有些不同,除了这两个方法外,还有一个postForLocation方法,其中postForLocation以post请求提交资源,并返回新资源的URI
postForEntity:此方法有三种重载形式,分别为:
postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)
postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
postForEntity(URI url, Object request, Class<T> responseType)
注意:此方法返回的是一个包装对象ResponseEntity<T>其中T为responseType传入类型,想拿到返回类型需要使用这个包装类对象的getBody()方法。
postForObject:此方法也有三种重载形式,这点与 postForEntity 方法相同:
postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)
postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
postForObject(URI url, Object request, Class<T> responseType)
注意:此方法返回的对象类型为responseType传入类型
postForLocation:此方法中同样有三种重载形式,分别为:
postForLocation(String url, Object request, Object... uriVariables)
postForLocation(String url, Object request, Map<String, ?> uriVariables)
postForLocation(URI url, Object request)
注意:此方法返回的是新资源的URI,相比getForEntity、getForObject、postForEntity、postForObject方法不同的是这个方法中无需指定返回类型,因为返回类型就是URI,通过Object... uriVariables、Map<String, ?> uriVariables进行传参依旧需要占位符,参看postForEntity部分代码。
二、Ribbon负载均衡的实现
通过上面的代码发现我们用到了一个注解@LoadBalanced,根据这名字大概就能知道Ribbon是可以实现负载均衡的。
1、复制多个product服务实例
第一步:将springcloud-micro-product服务复制两份:
分别为springcloud-micro-product2与springcloud-micro-product3。
第二步:将springcloud数据库复制两份,分别为springcloud2与springcloud3数据库,里面分别执行spingcloud数据库的脚本。
1.1、product2服务修改application.yml文件如下:
注意:切记application:name: springcloud-micro-product这个不能修改,这样做的目的是同一个服务下面有多个服务实例。
server:
port: 8081
mybatis:
mapper-locations: # 所有的mapper映射文件
- classpath:mapping/*.xml
spring:
application:
name: springcloud-micro-product
datasource:
type