spring cloud / netflix eureka 服务发现与服务调用

本篇介绍如何借助spring cloud / netflix eureka实现服务发现和服务调用,具体包含以下内容

  1. 创建eureka server,作为注册中心
  2. 创建一个叫做myservice1的服务,启动三个节点并注册到eureka中
  3. 创建一个服务消费者,注册到eureka,并通过服务发现来轮询调用myservice1的服务实例

本篇涉及到的组件包括:eureka, Ribbon, Feign等。

创建eureka server,作为注册中心

访问https://start.spring.io,在dependencies中选择eureka server和web,然后下载示例代码。

pom.xml中包含下面两个依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

修改application.yml配置文件,增加几个必要的配置项

server:
  port: 8761

eureka:
  client:
    registerWithEureka: false
  	fetchRegistry: false
  server:
    waitTimeInMsWhenSyncEmpty:  5

在启动类中增加@EnableEurekaServer注解

package com.example.eurekaserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {

	public static void main(String[] args) {
		SpringApplication.run(EurekaServerApplication.class, args);
	}

}

启动服务,访问http://localhost:8761,就可以看到eureka的控制台了。

如何确定client是否正确地注入到eureka-server中

通过访问eureka server的 /eureka/apps 这个地址,可以看到所有的服务。
通过访问eureka server的 /eureka/apps/{appID} 这个地址,可以看到某个服务的具体信息。

其中,hostName属性决定了其它客户度如何调用该服务。也就是说,如果hostName是IP地址,客户端就会通过ip地址来访问这个服务,如果是主机名或域名,客户端就会通过主机名或域名来访问这个服务。

创建一个叫做myservice1的服务,启动三个节点并注册到eureka中

访问https://start.spring.io,在dependencies中选择eureka client和web,然后下载示例代码。

pom.xml中包含下面两个依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

修改applicatin.yml配置文件,增加必要的配置项

spring:
  application:
    name: myservice1

server:
  port: 8000

eureka:
  instance:
    preferIpAddress:  true
  client:
    registerWithEureka: true
    fetchRegistry:  true
    serviceUrl:
      defaultZone:  http://localhost:8761/eureka/

上面指定了eureka的实例地址,并在启动后进行注册。

接下来编写一个controller,对外提供一个接口

@RestController
public class MyService {

    @GetMapping("/service1")
    public String methord1(HttpServletRequest request) {
        String server = request.getServerName();
        int port = request.getServerPort();
        String instance = MessageFormat.format("{0}:{1}", server, String.valueOf(port));
        return "this is result from " + instance;
    }
}

使用mvn clean package编译项目,然后通过下面的命令来启动三个服务示例

java -Dserver.port=8000 -jar target/myservice1-0.0.1-SNAPSHOT.jar
java -Dserver.port=8001 -jar target/myservice1-0.0.1-SNAPSHOT.jar
java -Dserver.port=8002 -jar target/myservice1-0.0.1-SNAPSHOT.jar

通过-Dserver.port=8000的方式来指定服务启动的端口号,可以将这个服务启动多个实例。

三个服务实例启动后,会注册到eureka中,在eureka的控制台页面可以看到三个实例都是UP状态。

创建一个服务消费者

上面启动了myservice1的三个实例,并注册到了eureka中,接下来再创建一个服务,作为消费者来消费myservice1

访问https://start.spring.io,在dependencies中选择eureka client和web,然后下载示例代码。

修改applicatin.yml配置文件,增加必要的配置项

spring:
  application:
    name: service-consumer

server:
  port: 8000

eureka:
  instance:
    preferIpAddress:  true
  client:
    registerWithEureka: true
    fetchRegistry:  true
    serviceUrl:
      defaultZone:  http://localhost:8761/eureka/

使用Ribbon的方式

接下来,我们要利用netflix Ribbon的服务发现和客户端负载均衡机制,对myservice1进行调用,首先,使用@LoadBalanced注解创建一个RestTemplate实例,并使用该实例调用myservice1

@SpringBootApplication
public class EurekaClientApplication {

    public static void main(String[] args) {
        SpringApplication.run(EurekaClientApplication.class, args);
    }

    @LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @RestController
    public class MyController {

        @Autowired
        private RestTemplate restTemplate;

        @GetMapping("/call")
        public String call() {
            String url = "http://myservice1/service1";
            ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);
            return responseEntity.getBody();
        }
    }

}

这里面比较神奇的就是@LoadBalanced,只需要加这一个注解,就完成了服务发现和客户端负载均衡。

这里需要注意的是,启用了@LoadBalanced的RestTemplate,会对url做特殊的解析,上面代码中的地址是http://myservice1/service1,其中myservice1是服务的名字,后面是服务的接口路径,RestTemplate在发起http调用之前,会将服务名称替换为服务的物理地址,并且会轮询替换。启动它看下效果

$ curl http://localhost:8005/call
this is result from 192.168.1.6:8000
$ curl http://localhost:8005/call
this is result from 192.168.1.6:8002 
$ curl http://localhost:8005/call
this is result from 192.168.1.6:8001 
$ curl http://localhost:8005/call
this is result from 192.168.1.6:8000

可以看出myservice1的三个实例是被轮询调用的。

使用Feign的方式

除了Ribbon+RestTemplate的方式以外,还有一种基于netflix Feign的机制,简单来讲就是,开发人员定义一个接口,并指定一些描述信息,然后Feign会自动生成一个动态代理,来通过RestTemplate调用服务接口,其本质是对Ribbon+RestTemplate的一个封装。下面看一下简单的用法

首先,加入一个maven的依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
    <version>1.4.7.RELEASE</version>
</dependency>

然后在启动类中启用Feign

@SpringBootApplication
@EnableFeignClients
public class EurekaClientApplication {

public static void main(String[] args) {
    SpringApplication.run(EurekaClientApplication.class, args);
}

...

}

接下来定义一个客户端接口类

@FeignClient("myservice1")
public interface MyService1Client {

    @GetMapping("/service1")
    String service1();
}

需要制定是哪个服务的客户端代理,并且将每个方法都映射到指定服务的接口上。

把这个接口注入到代码中并调用接口的方法,动态代理会自动通过RestTemplate来轮序地调用指定服务的接口

@Autowired
MyService1Client myServicde1Client;

@GetMapping("/call2")
public String call2() {
    return myServicde1Client.service1();
}

他的效果和使用Ribbon的调用效果是一样的

客户端负载均衡

由于在配置文件中开启了client.fetchRegistry,所以使用Ribbon会获取一份完整的服务注册清单,并且缓存在本地,每次调用服务时,无需每次都从注册中心查询完整的服务注册清单,但会定期从注册中心同步注册清单,目前看来应该是每分钟同步一次。

除此之外,如果服务的某个实例连续失败三次的话,则客户端的负载均衡策略就会把这个客户端实例忽略掉,不再调用它,直到从注册中心获取到该实例重新UP以后才会再次调用它。

有了以上两个能力,服务的上线和下线就无需人为干预了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值