微服务(二)Eureka集群以及服务调用

前言

eureka来源于古希腊词汇,意为“发现了”。eureka分为两部分,Server端和Client端。server端一般就是作为注册中心,保存服务注册的信息。client端则是将注册信息推送过去或者拉取注册信息。这里注意,Eureka通信是基于http协议下的rest请求,所以在java中编写代码都需要contriller层,最后再到service。

Register服务注册
想要参与服务注册发现的实例首先需要向Eureka服务器注册信息
注册在第一次心跳发生时提交

Renew 续租,心跳
Eureka客户需要每30秒发送一次心跳来续租
10:00 00 第一次
10:00 30
10:01
10:01 30 最后
更新通知Eureka服务器实例仍然是活动的。如果服务器在90秒内没有看到更新,它将从其注册表中删除实例

Fetch Registry
Eureka客户端从服务器获取注册表信息并将其缓存在本地。
之后,客户端使用这些信息来查找其他服务。
通过获取上一个获取周期和当前获取周期之间的增量更新,可以定期(每30秒)更新此信息。
节点信息在服务器中保存的时间更长(大约3分钟),因此获取节点信息时可能会再次返回相同的实例。Eureka客户端自动处理重复的信息。
在获得增量之后,Eureka客户机通过比较服务器返回的实例计数来与服务器协调信息,如果由于某种原因信息不匹配,则再次获取整个注册表信息。

Cancel
Eureka客户端在关闭时向Eureka服务器发送取消请求。这将从服务器的实例注册表中删除实例,从而有效地将实例从通信量中取出。

Time Lag 同步时间延迟
来自Eureka客户端的所有操作可能需要一段时间才能反映到Eureka服务器上,然后反映到其他Eureka客户端上。这是因为eureka服务器上的有效负载缓存,它会定期刷新以反映新信息。Eureka客户端还定期地获取增量。因此,更改传播到所有Eureka客户机可能需要2分钟。

Communication mechanism 通讯机制
Http协议下的Rest请求
默认情况下Eureka使用Jersey和Jackson以及JSON完成节点间的通讯。

1 Eureka集群

集群有两种,一种是互相拉去信息,还有一种是不互相拉,controller不知道还有其他Eureka服务,所以controller既向eureka1注册也要向eureka2注册。

第二种方式:很清晰,集群内部没有数据的交互,主从节点宕机没什么影响。但是坏处:需要拉一个服务列表,需要找多台eureka,这里的请求就多了。

第一种方式会牺牲数据一致性,提供了更多的可用性。靠eureka与服务靠心跳(30s)去保持同步的。所以对服务列表允许有容错率。一般用第一种方式。
这里我用一个项目,开启了并行,运行可两台Eureka。
首先需要选上Eureka server,然后直接编写profiles,这里拆分了两个profiles,对应两台Eureka机器,并且在本机把hosts的域名映射改了两台,一台为euk1.com 一台为euk2.com。
下面是第一台profiles的配置,并命名为application-euk1.profiles.

eureka.client.service-url.defaultZone=http://euk2.com:7002/eureka/
eureka.instance.hostname=euk1.com

server.port=7001

下面是第二台profiles的配置,并命名为application-euk2.profiles.

eureka.client.service-url.defaultZone=http://euk1.com:7001/eureka/
eureka.instance.hostname=euk2.com
#tomcat端口
server.port=7002

然后在主配置文件分别调用两个配置,并运行。

// 主配置文件
spring.profiles.active=euk1
spring.application.name=EurekaServer

这里注意,为了让两台eureka在同一个集群中,也就是一个副本,需要配置spring.application.name=EurekaServer.或者可以配置eureka.instance.appname。但是需要两台的Eureka的名称都一样,为了防止副本不可用情况。

在这里插入图片描述
在这里插入图片描述

2 Eureka作为服务注册中心(服务发现与调用)

首先创建一个Eureka provider,选择web项目与eureka client。
在controller层编写代码,以供测试用。

@RestController
public class MainController {

    @GetMapping("/getHi")
    public String getHi(){
        return "Hi";
    }

为什么是controller呢?服务与服务的调用是基于rest的,走http的,然后再接到后台的service里。
它的properties是下面的:

# 应用名称
spring.application.name=provider
# 应用服务 WEB 访问端口
server.port=80

eureka.client.service-url.defaultZone=http://euk1.com:7001/eureka/

可以从eureka中看到服务已经注册上去了。
在这里插入图片描述

这里为了方便,没有设置多个provider,其实Eureka存在有意义需要provider多个,由consumer从eureka中找。

Eurekaconsumer作用,用client获取里面的数据。用client可以拉取服务的具体名字与端口号了,接下来可以调用了。这里利用了两个client,一个是springcloud提供的抽象类接口DiscoveryClient,还有一个是Eureka提供的具体类的接口EurekaClient。
所以下面是创建一个eureka consumer的过程。
首先是properties。

spring.application.name=consumer
# 应用服务 WEB 访问端口
server.port=90
eureka.client.service-url.defaultZone=http://euk1.com:7001/eureka/

先是注入的接口。

    @Autowired
    //抽象的,sprincloud提供的
    DiscoveryClient client;
    //eureka提供的
    @Autowired
    EurekaClient client2;

client1是获取服务列表。

    @GetMapping("/client1")
    public String client1(){
        List<String> services = client.getServices();
        for (String service : services) {
            System.out.println(service);
        }
        return "Hi";
    }

结果为:
在这里插入图片描述

client2是返回json格式的provider的信息。

@GetMapping("/client2")
    public Object client2(){
        return client.getInstances("provider");
    }

结果为:
在这里插入图片描述

client3是打印provider的信息,返回list集合(如果有多个相同的provider)。

    @GetMapping("/client3")
    public String client3(){
        List<ServiceInstance> instances = client.getInstances("provider");
        for (ServiceInstance instance : instances) {
            System.out.println(ToStringBuilder.reflectionToString(instance));
        }
        return "xxoo";
    }

结果是控制台上输出的和上面结果一样。

上面的client变量都是有抽象类的springcloud提供,由于方法较少,下面用Eureka提供的client具体类使用,也就是EurekaClient client2。
client4是作为consumer,从Eureka服务注册中心去拿服务,并用rest请求得到provider反响应,利用restTemplate去发送请求。

  @GetMapping("/client4")
    public String client4(){
        //具体的服务
        //List<InstanceInfo> instances = client2.getInstancesById("192.168.245.140:provider:80");
        //找多个服务
        List<InstanceInfo> instances = client2.getInstancesByVipAddress("provider", false);
        for (InstanceInfo instance : instances) {
            System.out.println(ToStringBuilder.reflectionToString(instance));
        }
        if(instances.size() > 0){
            //代表服务
            InstanceInfo instanceInfo = instances.get(0);
            if (instanceInfo.getStatus() == InstanceInfo.InstanceStatus.UP){
              String url = "http://" + instanceInfo.getHostName() + ":" + instanceInfo.getPort() + "/getHi";
                System.out.println("url" + url);

                //调用服务,调用
                RestTemplate restTemplate = new RestTemplate();
                String respSTR = restTemplate.getForObject(url, String.class);
                System.out.println("respStr" + respSTR);

            }
        }

结果为consumer控制台上从eureka中找到服务并调用成功,返回Hi。

client5是简单的用了ribbon去做,简化了client4的代码。
这里有个问题,为什么要有ribbon?provider有多个的时候,需要做负载均衡。

Ribbon的参与,做负载均衡简单的调用。//rebbon完成客户端的负载均衡,由于provider有多个,后面由策略选择,并且过滤掉挂的机器。
这里用到lb,ribbon在lb中。

    @Autowired
    LoadBalancerClient lb;
    @GetMapping("/client5")
    public String client5(){

        //rebbon完成客户端的负载均衡,由于provider有多个,后面由策略选择,并且过滤掉挂的机器。
        ServiceInstance instance = lb.choose("provider");
        String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/getHi";

        RestTemplate restTemplate = new RestTemplate();
        String resStr = restTemplate.getForObject(url,String.class);
        System.out.println("respStr:" + resStr);

        return "xxoo";
    }

结束。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值