微服务远程调用——提供者和消费者
服务提供者:一次业务中,被其他微服务调用的的服务。(提供接口给其他微服务)
服务消费者:一次业务中,调用其他微服务的服务。
消费者和提供者
消费者与提供者的关系是相对的,消费者可以是提供者,提供者也可以是消费者。
服务C既是提供者也是消费者
为什么要使用Eureka?什么是Eureka?
在使用Eureka之前,我们不同服务之间的调用是使用RestTemplate来实现的,RestTemplate需要指定请求服务的URL连接以及请求的参数。
String url = "http://127.0.0.1:8081/user/" + order.getUserId();
User user = restTemplate.getForObject(url, User.class);
但是在实际开发过程中,有时会遇到大量不同服务的URL请求以及同一服务多实例集群的负载均衡成为了一个问题。
如何在三台服务B之间实现负载均衡
这个时候,Eureka出现了
它充当了一个注册中心的作用,将所有服务的信息注册在这个中心里面。
注册完了后,服务消费者拉取提供者的信息,进行负载均衡地调用。这样的话RestTemplate只需要把URL地址改为注册的服务名就可以进行远程调用了。
String url = "http://ServiceB/user/" + order.getUserId();
User user = restTemplate.getForObject(url, User.class);
搭建Eureka注册中心
1.创建新的模块Eureka_Server
2.导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
3.在启动类上加上注解@EnableEurekaServer
@EnableEurekaServer
@SpringBootApplication
public class EruekaApplication {
public static void main(String[] args) {
SpringApplication.run(EruekaApplication.class,args);
}
}
4.在application.yml文件里面写入相关配置信息:
server:
port: 6689
spring:
application:
#配置注册中心的名称
name: EurekaServer
eureka:
client:
service-url:
#配置注册中心的地址,注意"/"后面只能是eureka,不能是别的,否则会报错
defaultZone: http://127.0.0.1:6689/eureka
启动完成后会看到这样的界面
在启动的时候Springboot可能会报以下两个异常:
1.Request execution error
Request execution error. endpoint=DefaultEndpoint{ serviceUrl='http://127.0.0.1:6689/eureka/}, exception=java.net.ConnectException: Connection refused: no further information stacktrace=com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused: no further information
2.EurekaServer:6689 - was unable to refresh its cache!
DiscoveryClient_EUREKASERVER/host.docker.internal:EurekaServer:6689 - was unable to refresh its cache! This periodic background refresh will be retried in 30 seconds. status = Cannot execute request on any known server stacktrace = com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server
以上两个问题,第一个是因为Eureka在启动的时候会获取其他服务的信息,获取不到会报这个异常。第二个是因为,Eureka会把自己当作一个服务注册在注册中心里面。以上两个异常都不会影响程序的正常运行。如果需要修复,需要在配置文件里面加入:
server:
port: 6689
spring:
application:
name: EurekaServer
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:6689/eureka
register-with-eureka: false #Eureka不把自己注册为服务
fetch-registry: false #不拉取其他服务的信息
Eureka服务注册
我准备了两个Service来进行本次测试,分别准备了一个简单的Controller来查看调用结果:
ServiceA
@RestController
@RequestMapping("/serviceA")
public class ServiceAController {
@GetMapping("test")
public String ServiceATest(){
return "ServiceA Working";
}
}
ServiceB(查看是哪个服务被调用了)
@RestController
@RequestMapping("/serviceB")
@Slf4j
public class ServiceBController {
@GetMapping("test")
public String ServiceBTest(){
log.info("B被调用了");
return "ServiceB Working";
}
}
准备好了后就可以开始将他们注册进Eureka了。
所有步骤ServiceB同理,这里就只展示ServiceA的。
1.导入依赖:
server:
port: 8080
spring:
application:
name: ServiceA
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:6689/eureka/ #Eureka注册中心的地址
2. 在启动类上加入@EnabkeEurekaClient
@SpringBootApplication
@EnableEurekaClient
public class ServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class,args);
}
}
3.在启动之前务必先启动Eureka服务,否则会抛出异常。
打开Eureka管理页面,可以看到两个服务都成功地注册进来了。
我们可以多准备一个ServiceB来模拟负载均衡的效果:
启动后也成功注册进了Eureka
使用Eureka进行服务拉取
我们完成Eureka的服务注册以后,就可以在消费者上进行服务拉取。
1.添加RestTemplate的Bean,并为其添加@LoadBalanced(我这里为了方便加在了启动类里面)
@SpringBootApplication
@EnableEurekaClient
public class ServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class,args);
}
@Bean
@LoadBalanced //添加负载均衡
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
2.修改消费者的Controller,用RestTemplate对提供者进行调用
@RestController
@RequestMapping("/serviceA")
public class ServiceAController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("test")
public String ServiceATest(){
//这里的SERVICEB需要和Eureka注册中心的一致
String url = "http://SERVICEB/serviceB/test";
String forObject = restTemplate.getForObject(url, String.class);
return "ServiceA Working and " + forObject;
}
}
3.查看结果:
我们可以刷新4次,来查看负载均衡的结果。
可以看到4次请求被均衡到了两个服务上。
Ribbon负载均衡
1.负载均衡的原理
通过@LoadBalanced我们实现了负载均衡,但是这个负载均衡究竟是怎么做到的。
其实真正在做负载均衡的是Ribbon,Ribbon通过拦截我们的请求,找到我们对应的服务名。
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
URI originalUri = request.getURI();
//这里就取到了我们刚刚输入的SERVICEB
String serviceName = originalUri.getHost();
Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
}
LoadBalancerInterceptor.class
然后通过excute方法来进行服务的一个负载均衡。
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
//ILoadBalancer里面记录了我们的负载均衡的方法
ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
Server server = this.getServer(loadBalancer, hint);
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
} else {
RibbonServer ribbonServer = new RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
return this.execute(serviceId, (ServiceInstance)ribbonServer, (LoadBalancerRequest)request);
}
}
RibbonLoadBalancerClient.class
在BaseLoadBalancer里面的IRule接口的实现类就是当前负载均衡器的不同实现方法。
我们现在默认使用的轮询负载均衡,就是RoundRobinRule。
2.负载均衡策略的切换
(1).负载均衡的七种策略
Ribbon的负载均衡策略 有七种,默认使用的RoundRobinRule(轮询)。
剩下的分别是:
权重策略(WeightedResponseTimeRule)-根据每个服务提供者的响应时间分配一个权重,响应时间越长,权重越小,被选中的可能性也就越低。
随机策略(RandomRule)-从服务提供者的列表中随机选择一个服务实例。
最小连接数策略(BestAvailableRule)-也叫最小并发数策略,它是遍历服务提供者列表,选取连接数最小的⼀个服务实例。
重试策略(RetryRule)-按照轮询策略来获取服务,如果获取的服务实例为 null 或已经失效,则在指定的时间之内不断地进行重试来获取服务。
可用性敏感策略(AvailabilityFilteringRule)-先过滤掉非健康的服务实例,然后再选择连接数较小的服务实例。
区域敏感策略(ZoneAvoidanceRule)-据服务所在区域(zone)的性能和服务的可用性来选择服务实例。
(2).负载均衡策略的切换(在消费者处配置)
一.采用Bean注入的方式(对所有提供者生效)
@Bean
public IRule RandomRule(){
return new RandomRule();
}
此时策略切换生效,重启服务后刷新4次页面会发现
B1调用了一次,B2调用了3次,此时已经切换成了随机策略。
二.采用配置(对指定提供者生效)
SERVICEB: #提供者服务名(与Eureka注册中心一致)
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #填写策略类名
效果同上
3.Ribbon饥饿加载
Ribbon启动默认采用懒加载,即消费者去调用某个提供者的服务时才去创建LoadBalancerClient实例,这样的话会导致第一次的访问时间变长。
可以在消费者端的配置文件打开饥饿加载:
ribbon:
eager-load:
enabled: true
clients:
- SERVICEB #需要开启饥饿加载的服务名,与Eureka注册中心一致
开启前:
开启后:
以上就是关于Eureka的知识。
避坑:运行Eureka的时候请一定要把代理关了!!