在上一篇文章:SpringCloud系列教程(一):服务的注册与发现(Eureka) 中,讲解了如何搭建一个Eureka服务注册与发现的注册中心,有注册那么就有服务提供者,有发现那么就有服务消费者,这篇文章我们会服务注册到Eureka和消费者从Eureka注册中心消费服务接口;
在一个大型的微服务项目中,很多个服务都会被拆分成一个个独立的服务,服务与服务之间通过RPC进行服务与服务之间的调用,并且采用HTTP+JSON协议进行通讯(Restful),SpringCloud中进行服务消费有两种方式调用:一种是rest+ribbon,另一种是fegin,feign默认集成并开启了Ribbon负载均衡,
其中rest方式由Spring web组件提供RestTemplate API方式来进行调用,其实rest方式底层原理是采用HttpClient技术进行调用,通过引入eureka组件的依赖后,分析依赖信息我们可以看出,其实在eureka其底层默认集成了Ribbon负载均衡器的,在使用rest方式进行服务消费调用时,ribbon负载均衡器默认会在本地通过负载均衡算法去实现负载均衡效果(默认算法是轮询机制);
Feign是一个声明式的伪Http客户端,Feign伪装成类似SpringMVC的Controller一样。无需自己拼接url,拼接参数,服务消费者无需知道服务提供者的ip和端口,只需要指定服务名,即可通过注册中心调用到目标服务,这一切工作都交给Feign去做;同时Feign提供了接口和注解方式进行调用。
以一个在电商项目中 "订单服务调取会员服务" 接口为例,将服务提供者(会员服务)注册到Eureka注册中心,服务消费者(订单服务) 从注册中心获取会员服务进行调用,具体调用关系如图所示:
这篇文章基于上一篇,需要用到eureka注册中心,在搭建服务提供者和服务消费者之前,需要先启动我们的eureka服务,然后新建三个工程:springcloud-ribbon(父工程,pom类型)、springcloud-app-member(服务提供)、springcloud-app-order(服务消费),具体创建项目过程这里不再累赘,我只贴出pom依赖信息
4.0.0
com.thinkingcao
springcloud-ribbon
0.0.1-SNAPSHOT
springcloud-ribbon
Demo project for Spring Boot
pom
springcloud-eureka-server
springcloud-app-member
springcloud-app-order
org.springframework.boot
spring-boot-starter-parent
2.0.3.RELEASE
com.thinkingcao
springcloud-app-member
0.0.1-SNAPSHOT
springcloud-app-member
SpringCloud整合Eureka组件之注册服务消费者
UTF-8
UTF-8
1.8
Finchley.RELEASE
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
org.springframework.boot
spring-boot-starter-web
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.boot
spring-boot-maven-plugin
##=========服务提供者-会员服务配置========
#服务端口号
server:
port: 8762
#定义服务名称(服务注册到eureka名称)
spring:
application:
name: app-thinkingcao-member
#在此指定服务注册中心地址,将当前会员服务注册到eureka注册中心上
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8000/eureka
#启动注册操作,该值默认为true。若设置为fasle将不会启动注册操作。是否需要去检索寻找服务,默认是true
register-with-eureka: true
#是否需要从eureka上获取注册信息
fetch-registry: true
##心跳检测与续约时间(测试环境和本地开发环境将值设置小一点,保证服务关闭后,注册中心能够及时踢出)
instance:
#客户端向Eureka注册中心发送心跳的时间间隔,单位为秒(默认为30s),(客户端会按照此规则向Eureka服务端发送心跳检测包)
lease-renewal-interval-in-seconds: 2
#Eureka注册中心在收到客户端最后一次心跳之后等待的时间上限,单位为秒(默认为90s),超过时间则剔除(客户端会按照此规则向Eureka服务端发送心跳检测包)
lease-expiration-duration-in-seconds: 2
会员服务接口这里我们返回端口号是为了后面演示订单以rest+ribbon方式调用会员服务时,当会员服务为2台集群环境,ribbon会在本地使用负载均衡算法,为我们自动做了负载均衡,默认是使用轮询机制;
package com.thinkingcao.api.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @desc: 会员服务接口
* @author: cao_wencao
* @date: 2020-02-19 18:12
*/
@RestController
public class MemberController {
@Value("${server.port}")
private String serverPort;
//会员服务接口
@RequestMapping("/getMember")
private String getMember() {
return "我是会员服务,订单服务调用会员服务成功啦, 端口号为: " + serverPort;
}
}
在springcloud-app-member工程的启动类中,通过@EnableEurekaClient 注解或者@EnableDiscoveryClient 注解,作用是表明启用向注册中心注册服务功能,
@EnableDiscoveryClient和@EnableEurekaClient共同点就是:都是能够让注册中心能够发现,扫描到改服务。
不同点:@EnableEurekaClient只适用于Eureka作为注册中心,@EnableDiscoveryClient 可以是其他注册中心,spring cloud中discovery service有许多种实现(eureka、consul、zookeeper等等),如果采用consul或者zookeeper作为注册中心,那么就是用@EnableDiscoveryClient注解,@EnableEurekaClient只是eureka的专属注解;
注意: 从SpringCloud Edgware开始,
@EnableDiscoveryClient
或@EnableEurekaClient可以
省略。只需加上相关注册中心依赖,并在application.yml进行相应配置,即可将微服务注册到服务发现组件上,上面我们yml中配置了,所以这里也可以省略@EnableEurekaClient注解。
AppMemberProvider.java启动类
package com.thinkingcao.api;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class AppMemberProvider {
public static void main(String[] args) {
SpringApplication.run(AppMemberProvider.class, args);
}
}
org.springframework.boot
spring-boot-starter-parent
2.0.3.RELEASE
com.thinkingcao
springcloud-app-order
0.0.1-SNAPSHOT
springcloud-app-order
SpringCloud整合Eureka组件之注册服务提供者
UTF-8
UTF-8
1.8
Finchley.RELEASE
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
org.springframework.boot
spring-boot-starter-web
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.boot
spring-boot-maven-plugin
##=========服务消费者-订单服务配置========
#服务端口号
server:
port: 8761
#定义服务名称(服务注册到eureka名称)
spring:
application:
name: app-thinkingcao-order
#在此指定服务注册中心地址,将当前订单服务注册到eureka注册中心上
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8000/eureka
#启动注册操作,该值默认为true。若设置为fasle将不会启动注册操作。是否需要去检索寻找服务,默认是true
register-with-eureka: true
#是否需要从eureka上获取注册信息
fetch-registry: true
##心跳检测与续约时间(测试环境和本地开发环境将值设置小一点,保证服务关闭后,注册中心能够及时踢出)
instance:
#客户端向Eureka注册中心发送心跳的时间间隔,单位为秒(默认为30s),(客户端会按照此规则向Eureka服务端发送心跳检测包)
lease-renewal-interval-in-seconds: 2
#Eureka注册中心在收到客户端最后一次心跳之后等待的时间上限,单位为秒(默认为90s),超过时间则剔除(客户端会按照此规则向Eureka服务端发送心跳检测包)
lease-expiration-duration-in-seconds: 2
1. 订单在调用会员服务的时候,采用RestTemplate调用,RestTemplate有3种调用方式,分别如下:
- 一:直接使用IP地址调用(但是不会走注册中心,这种方式不推荐)。
- 二:采用服务别名方式调用,需要依赖ribbon负载均衡器,因为在使用别名方式时,基于ribbon会实现负载均衡效果,使用别名方式会去注册中心上获取对应的服务调用地址,然后ribbon在本地使用负载均衡算法,做负载均衡,同样也是使用HttpClient;
- 三:利用LoadBalancerClient通过应用的服务名称获取调用地址URL+端口号,然后再使用restTemplate进行调用
第一种:以IP地址直接调用(不推荐)
1. 需要向ioc注入一个bean: restTemplate
package com.thinkingcao.api.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* @desc: 订单调用会员服务接口
* @author: cao_wencao
* @date: 2020-02-19 17:47
*/
@RestController
public class OrderController {
@Autowired
private RestTemplate restTemplate;
//订单服务(消费者)调用会员服务(生产者)接口
@RequestMapping("/getOrderToMember")
public String getOrderToMember() {
String url = "http://127.0.0.1:8762/getMember";
String result = restTemplate.getForObject(url, String.class);
return result;
}
// 有两种方式调用,一种是采用服务别名方式调用,使用别名去注册中心上获取对应的服务调用地址,另一种是直接调用
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}
第二种:采用服务别名方式调用
1. 向程序的ioc注入一个bean: restTemplate;并通过@LoadBalanced注解, 该注解表明这个restRemplate在底层使用HTTPClient调用时,本地会开启负载均衡的功能。
package com.thinkingcao.api.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* @desc: 订单调用会员服务接口
* @author: cao_wencao
* @date: 2020-02-19 17:47
*/
@RestController
public class OrderController {
@Autowired
private RestTemplate restTemplate;
//订单服务(消费者)调用会员服务(生产者)接口
@RequestMapping("/getOrderToMember")
public String getOrderToMember() {
String url = "http://app-thinkingcao-member/getMember";
String result = restTemplate.getForObject(url, String.class);
return result;
}
// 有两种方式调用,一种是采用服务别名方式调用,另一种是直接调用 使用别名去注册中心上获取对应的服务调用地址
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
第三种: 使用LoadBalancerClient获取服务调用地址
package com.thinkingcao.api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* @desc: 订单调用会员服务接口
* @author: cao_wencao
* @date: 2020-02-19 17:47
*/
@RestController
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private LoadBalancerClient loadBalancerClient;
//订单服务(消费者)调用会员服务(生产者)接口
@RequestMapping("/getOrderToMember")
public String getOrderToMember() {
//String url = "http://app-thinkingcao-member/getMember";
ServiceInstance serviceInstance = loadBalancerClient.choose("app-thinkingcao-member");
String response = String.format("http://%s:%s",serviceInstance.getHost(), serviceInstance.getPort()+"/getMember");
System.out.println("response= " + response);
return response;
}
// 有两种方式调用,一种是采用服务别名方式调用,另一种是直接调用 使用别名去注册中心上获取对应的服务调用地址
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
package com.thinkingcao.api;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class AppMemberProvider {
public static void main(String[] args) {
SpringApplication.run(AppMemberProvider.class, args);
}
}
上述springcloud-eureka-server的端口号为8000、springcloud-app-order为8761、springcloud-app-member为8762,首先我们启动eureka注册中心,然后在启动会员服务、然后再启动订单服务,顺序是:注册中心、服务提供者、服务消费者 , 上面说了我们要开启会员服务集群功能,测试ribbon本地是否做了负载均衡,所以我们这里为了简单就不新建会员项目了,直接启动会员服务多个实例: SpringBoot 系列教程(八十二):Intellij IDEA实现SpringBoot项目启动多个端口
第一步:首先设置允许多个实例运行
第二步:修改springcloud-app-membe端口号为8765,然后再次运行会员服务
访问Eureka注册中心,URL地址 : http://127.0.0.1:8000/, 两台会员服务和一台订单服务已经注册到Eureka注册中心了
观察Eureka注册中心服务列表,左侧代表服务的别名,右侧代表服务调用地址:
APP-THINKINGCAO-ORDER | n/a (1) | (1) | UP (1) - DESKTOP-JBFTEEM:app-thinkingcao-order:8761 |
APP-THINKINGCAO-ORDER: 表示会员服务别名
DESKTOP-JBFTEEM:app-thinkingcao-order:8761 : 表示会员服务别名所对应的服务真实调用地址
URL: http://127.0.0.1:8761/getOrderToMember ,在浏览器上多次访问该地址,会交替显示结果
我是会员服务,订单服务调用会员服务成功啦, 端口号为: 8765
我是会员服务,订单服务调用会员服务成功啦, 端口号为: 8762
从上一步说明当我们通过调用restTemplate.getForObject(“http://app-thinkingcao-member/getMember,String.class)方法时,ribbon在本地使用负载均衡算法已经做了负载均衡,访问了不同的端口的服务实例。
1. 项目源码: https://github.com/Thinkingcao/SpringCloudLearning/tree/master/springcloud-ribbon
1. SpringCloud系列教程(三): Spring Cloud系列教程(三) - 实现高可用集群环境搭建Eureka(Finchley版本)