1.SpringCloud-Eureka集群搭建-负载均衡,API简单使用,JRebel热部署安装

1. 微服务

1. Spring Cloud全家桶

  1. 面向单体应用开发

    1.SpringCloud-Eureka集群搭建-负载均衡,API简单使用,JRebel热部署安装_第1张图片

    将两个项目打成一个war包,包含了项目中所有需要的资源。

  2. SOA,将两个项目分别打包,形成单独的服务

    当线上的时候,本地依赖失效,当财务系统需要共同依赖中的资源时,此时就需要RPC(远程服务调用),调用共同依赖中的依赖。

    财务服务调用商城服务:webservice(soap、wsdl、cxf),在解决了这个依赖问题,此时又有新的问题:财务向商城服务发起请求时,怎么知道商城的服务挂掉没,或是网络问题导致响应延时??等一系列问题。而这种就是一种强依赖问题。依赖包调来调去。

    1.SpringCloud-Eureka集群搭建-负载均衡,API简单使用,JRebel热部署安装_第2张图片

  3. 将这些依赖根据功能抽取为模块,只具备单一功能,比如订单服务,支付服务等,将整体一个大的模块拆分成更小的功能模块,这种服务就叫微服务。

    1.SpringCloud-Eureka集群搭建-负载均衡,API简单使用,JRebel热部署安装_第3张图片

由此,微服务可以分为三个方向拆分:

  1. 面向需求维度:应用维度;

  2. 面向技术栈;

    看这样一个服务流程:

    用户在前端页面发起请求,前端充当传话的角色,向后台请求,即真正干事的是后台服务。当一个服务忙不过来,就需要有多个的后台服务,前端可以向多个后台发起请求,轮着干活,这就叫做负载均衡,也称之为客户端的负载均衡。取决权在与客户端

    1.SpringCloud-Eureka集群搭建-负载均衡,API简单使用,JRebel热部署安装_第4张图片

那这三个服务想要做负载均衡(红色框中前端是支付,复制忘改了),客户端首先要获取服务列表才可以,每个服务都有其名字,但是获取到列表还不行,需要对应的ip、端口号——主机名+ip+端口号。而帮助我们注册这些信息的就是Eureka框架。eureka会从服务列表中拿到服务器名注册,然后通过Netflix Fegin(还有其他工具)远程调用服务器;但是前面提到过,服务器中总有些罢工的,所以此时就不能一个劲儿的发请求,硬刚。所以需要客户端的负载均衡Ribbon去判断,然后调用可用的服务。然后再将结果返回,View渲染。

1.SpringCloud-Eureka集群搭建-负载均衡,API简单使用,JRebel热部署安装_第5张图片

实际微服务中,一个controller肯定是不可能的,所以也应该有多个controller,而且同样需要实现负载均衡,所以不应该直接将controller端口暴露出来,所以添加网关NetFlixZuul,可以实现url拦截,权限控制等。NetFlixZuul内部集成springOAuth2.0权限控制。

客户端负载均衡调用服务还涉及到两个问题:链路追踪、分布式事物,在判断服务之前,ribbon还会去做熔断、降级。Hystrix会记录每个微服务状态,当服务down了,客户端就不会再连接它,直到它上线(这称之为熔断),那这个状态又是怎么更新的呢?即每个服务节点都会跟一个Actuator,实时上报服务节点状态。降级是表示将服务节点数量减少。这些服务节点的状态都是同Actuator记录,一是提交给Hystrix,另一个是提交给springCloud Admin统一化管理。

1.SpringCloud-Eureka集群搭建-负载均衡,API简单使用,JRebel热部署安装_第6张图片

同时还有消息管理组件spring Cloud Stream;Spring Integration和Apache Camel是一个级别,Spring Message次之。不过这个一般是非常大的项目才用的到。

SpringMessage比较简单:对单机消息中间件的包装,包装成统一的标准,屏蔽了框架之间的差异。其目的是为了集成Spring Integration

Spring Integration:消息集合、过滤等

Spring Cloud Stream:相当于对Spring Integration的加强,再一次包装,与SpringBoot做一次整合,为了实现Spring

1.SpringCloud-Eureka集群搭建-负载均衡,API简单使用,JRebel热部署安装_第7张图片

至此,一个框架模型大致出来了。

2. 亿级流量 多级缓存 高可用 高并发 异构系统 分布式 微服务系统架构图

  1. 来一张“一鸣大佬的神图”

  2. 搭建Eureka服务

    步骤如下图

    1.SpringCloud-Eureka集群搭建-负载均衡,API简单使用,JRebel热部署安装_第8张图片

  3. 导入成功之后,配置eureka

    application.properties

    #是否将自己注册到Eureka Server,默认为true,由于当前就是server,故而设置成false,表明该服务不会向eureka注册自己的信息
    eureka.client.register-with-eureka=false
    #是否从eureka server获取注册信息,由于单节点,不需要同步其他节点数据,用false
    eureka.client.fetch-registry=false
    #设置服务注册中心的URL,用于client和server端交流
    eureka.client.service-url.defaultZone=http://localhost:7900/eureka/
    

    添加@EnableEurekaServer注解

    package com.anzhi;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
    
    @EnableEurekaServer
    @SpringBootApplication
    public class EurekaserverApplication {
           
    
        public static void main(String[] args) {
           
            SpringApplication.run(EurekaserverApplication.class, args);
        }
    
    }
    
  4. 启动如图:

    1.SpringCloud-Eureka集群搭建-负载均衡,API简单使用,JRebel热部署安装_第9张图片

3. 搭建集群

  1. Eureka集群分为两种情况:

    1. 两个服务器互为客户端/服务端,然后对外提供服务

      1.SpringCloud-Eureka集群搭建-负载均衡,API简单使用,JRebel热部署安装_第10张图片

    2. 分别注册服务,两个服务没有依赖关系

      1.SpringCloud-Eureka集群搭建-负载均衡,API简单使用,JRebel热部署安装_第11张图片

    3. 两种方式各有什么优缺点:

      1. 第一种

        1. 优点:服务器内部有交互,所以只需要访问某一台,就可以拉取到所有服务列表信息,以及提交自身信息注册,不需要广播;
        2. 缺点:数据访问存在延时,节点与节点之间的数据并不能及时同步,方式二也存在这样问题。
      2. 第二种:

        1. 优点:服务之间内部没有交互,即使服务器宕机,只有其他服务正常,就不影响业务
        2. 坏处:每个客户端在获取服务列表的时候,或者提交自身信息的时候,并不是单独向某个服务请求,因为,而是向整个服务列表中的服务发起请求获取列表,如果后期业务量增加,这样导致请求量极大,维护困难。
      3. 小结:

        Eureka保证了AP,无法保证数据的一致性。其高可用体现在,及时Eureka服务节点都挂掉了,他仍然可以使用,和其他客户端进行通信,即通过本地缓存列表,进行交互。

1. 搭建

  1. 添加eureka域名,将ip与eureka绑定,修改host文件,添加如下:

    文件路径:win10: C:\Windows\System32\drivers\etc

    # config eureka anzhi
    127.0.0.1 eureka1.com
    127.0.0.1 eureka2.com
    

    win10不让修改,点击文件属性,安全,编辑,为当前用户赋予修改权限即可。

    # 在dos下ping
    ping eureka1.com
    ping eureka2.com
    
  2. 添加eureka工程的配置文件

    1. 使用properties配置的参考这篇博客:https://blog.csdn.net/weixin_43155866/article/details/100937429

    2. 我使用第二种方法:

      将application.properties修改为application.yml,配置如下:

      spring:
        application:
          name: eureka
      
      eureka:
        client:
          serviceUrl:
            register-with-eureka: false
            fetch-registry: false
            #注册的服务地址,端口
            defaultZone: https://eureka1.com:7900/eureka/,https://eureka2.com:7900/eureka/
        server:
          enable-self-preservation: false
          renewal-percent-threshold: 0.85
          eviction-interval-timer-in-ms: 1000
          use-read-only-response-cache: false
      ---
      spring:
        profiles: 7900
      server:
        port: 7900
      eureka:
        instance:
        	# 主机名
          hostname: eureka1.com    
      ---
      spring:
        profiles: 7901
      server:
        port: 7901
      eureka:
        instance:
        	# 主机名
          hostname: eureka2.com   
      
    3. 配置相应的编译器

      1.SpringCloud-Eureka集群搭建-负载均衡,API简单使用,JRebel热部署安装_第12张图片

    4. 然后启动,结果如下

      1.SpringCloud-Eureka集群搭建-负载均衡,API简单使用,JRebel热部署安装_第13张图片

      1.SpringCloud-Eureka集群搭建-负载均衡,API简单使用,JRebel热部署安装_第14张图片

      这是配置问题,页面爆红:

      Eureka 访问页面出现红色字体提醒
      系统在三种情况下会出现红色加粗的字体提示:
      1.在配置上,自我保护机制关闭

      RENEWALS ARE LESSER THAN THE THRESHOLD. THE SELF PRESERVATION MODE IS TURNED OFF.THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.
      2.自我保护机制开启了

      EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE

      NOT BEING EXPIRED JUST TO BE SAFE.
      3.在配置上,自我保护机制关闭了,但是一分钟内的续约数没有达到85% , 可能发生了网络分区,会有如下提示

      THE SELF PRESERVATION MODE IS TURNED OFF.THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.

      更改配置如下

      # 全局的服务器名,保证一致
      spring:
        application:
          name: eureka
      
      
      logging:
        level:
          root: info
      
      ---
      spring:
        # 在启动时指定的配置文件
        profiles: 7900
      server:
        port: 7900
      eureka:
        instance:
          # 与主机域名一致
          hostname: eureka1.com
        client:
          service-url:
            # 要注册到拿一台服务器
            defaultZone: http://eureka2.com:7901/eureka/
      ---
      spring:
        profiles: 7901
      server:
        port: 7901
      eureka:
        instance:
          hostname: eureka2.com
        client:
          service-url:
            defaultZone: http://eureka1.com:7900/eureka/
      

      idea不同,设置参数也不一样,我的是2018.3.6,所以指定profiles的时候,直接添我们配置的profiles名称

      1.SpringCloud-Eureka集群搭建-负载均衡,API简单使用,JRebel热部署安装_第15张图片

    5. 最终启动成功界面如下:

      1.SpringCloud-Eureka集群搭建-负载均衡,API简单使用,JRebel热部署安装_第16张图片
      1.SpringCloud-Eureka集群搭建-负载均衡,API简单使用,JRebel热部署安装_第17张图片

4. Eureka组成

eureka来源于古希腊词汇,意为“发现了”

eureka分为两部分,Server端和Client端

Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能。

1. Register

服务注册

想要参与服务注册发现的实例首先需要向Eureka服务器注册信息

注册在第一次心跳发生时提交

2. Renew

续租,心跳

Eureka客户需要每30秒发送一次心跳来续租

10:00 00 第一次

10:00 30

10:01

10:01 30 最后

更新通知Eureka服务器实例仍然是活动的。如果服务器在90秒内没有看到更新,它将从其注册表中删除实例

3. Fetch Registry

Eureka客户端从服务器获取注册表信息并将其缓存在本地。

之后,客户端使用这些信息来查找其他服务。

通过获取上一个获取周期和当前获取周期之间的增量更新,可以定期(每30秒)更新此信息。

节点信息在服务器中保存的时间更长(大约3分钟),因此获取节点信息时可能会再次返回相同的实例。Eureka客户端自动处理重复的信息。

在获得增量之后,Eureka客户机通过比较服务器返回的实例计数来与服务器协调信息,如果由于某种原因信息不匹配,则再次获取整个注册表信息。

4. Cancel

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

5. Time Lag

同步时间延迟

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

6. Communication mechanism

通讯机制

Http协议下的Rest请求

默认情况下Eureka使用Jersey和Jackson以及JSON完成节点间的通讯

7. 服务注册

新建一个web项目,引入starterspring-cloud-starter-netflix-eureka-client

8. 客户端配置选项

#续约发送间隔默认30秒,心跳间隔
eureka.instance.lease-renewal-interval-in-seconds=5
#表示eureka client间隔多久去拉取服务注册信息,默认为30秒,对于api-gateway,如果要迅速获取服务注册状态,可以缩小该值,比如5秒
eureka.client.registry-fetch-interval-seconds=5
# 续约到期时间(默认90秒)
eureka.instance.lease-expiration-duration-in-seconds=60

9. 服务器端配置选项

#关闭自我保护模式
eureka.server.enable-self-preservation=false
#失效服务间隔
eureka.server.eviction-interval-timer-in-ms=3000

5. 创建服务端工程连接eureka服务器

  1. 我直接重新创建了一个工程,加入eureka client插件,spring web,

    修改工程application.properties为application.yml

    eureka:
      client:
        serviceUrl:
          defaultZone: http://eureka1.com:7900/eureka/
    server:
      port: 80
    spring:
      application:
        name: eurekaclient
    

    建立controller层,controller层要放入application的同级目录,否则找不到,网页请求无响应

    MainController

    package com.anzhi;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class MainController {
           
        @GetMapping("/getHi")
        public String getHi(){
           
            return "get HI";
        }
    }
    

启动前面的7900服务,再启动客户服务,如下:

1.SpringCloud-Eureka集群搭建-负载均衡,API简单使用,JRebel热部署安装_第18张图片

6. Eureka单独使用

Rest服务调用

官方文档

https://github.com/Netflix/eureka/wiki/Eureka-REST-operations

Operation HTTP action Description
Register new application instance POST /eureka/v2/apps/appID Input: JSON/XMLpayload HTTPCode: 204 on success
De-register application instance DELETE /eureka/v2/apps/appID/instanceID HTTP Code: 200 on success
Send application instance heartbeat PUT /eureka/v2/apps/appID/instanceID HTTP Code: * 200 on success * 404 if instanceIDdoesn’t exist
Query for all instances GET /eureka/v2/apps HTTP Code: 200 on success Output: JSON/XML
Query for all appID instances GET /eureka/v2/apps/appID HTTP Code: 200 on success Output: JSON/XML
Query for a specific appID/instanceID GET /eureka/v2/apps/appID/instanceID HTTP Code: 200 on success Output: JSON/XML
Query for a specific instanceID GET /eureka/v2/instances/instanceID HTTP Code: 200 on success Output: JSON/XML
Take instance out of service PUT /eureka/v2/apps/appID/instanceID/status?value=OUT_OF_SERVICE HTTP Code: * 200 on success * 500 on failure
Move instance back into service (remove override) DELETE /eureka/v2/apps/appID/instanceID/status?value=UP (The value=UP is optional, it is used as a suggestion for the fallback status due to removal of the override) HTTP Code: * 200 on success * 500 on failure
Update metadata PUT /eureka/v2/apps/appID/instanceID/metadata?key=value HTTP Code: * 200 on success * 500 on failure
Query for all instances under a particular vip address GET /eureka/v2/vips/vipAddress * HTTP Code: 200 on success Output: JSON/XML * 404 if the vipAddressdoes not exist.
Query for all instances under a particular secure vip address GET /eureka/v2/svips/svipAddress * HTTP Code: 200 on success Output: JSON/XML * 404 if the svipAddressdoes not exist.

/eureka/status 服务状态

使用浏览器请求url会返回服务器状态信息


  
    test
    16
    526mb
    183mb (34%)
    00:00
  
  
    
    
    
  
  
    localhost
    localhost
    UNKNOWN
    192.168.29.1
    UP
    UNKNOWN
    8080
    443
    1
    
      MyOwn
    
    
      30
      90
      0
      0
      0
      0
    
    
      8080
      7649
    
    http://localhost:8080/
    http://localhost:8080/actuator/info
    http://localhost:8080/actuator/health
    unknown
    unknown
    false
    1586328420409
    1586328420519
  

如果需要json格式 可以加个请求头Accept:application/json

{
    "generalStats": {
        "environment": "test",
        "num-of-cpus": "16",
        "total-avail-memory": "517mb",
        "current-memory-usage": "45mb (8%)",
        "server-uptime": "00:03"
    },
    "applicationStats": {
        "registered-replicas": "",
        "available-replicas": "",
        "unavailable-replicas": ""
    },
    "instanceInfo": {
        "instanceId": "localhost",
        "hostName": "localhost",
        "app": "UNKNOWN",
        "ipAddr": "192.168.29.1",
        "status": "UP",
        "overriddenStatus": "UNKNOWN",
        "port": {
            "$": 8080,
            "@enabled": "true"
        },
        "securePort": {
            "$": 443,
            "@enabled": "false"
        },
        "countryId": 1,
        "dataCenterInfo": {
            "@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
            "name": "MyOwn"
        },
        "leaseInfo": {
            "renewalIntervalInSecs": 30,
            "durationInSecs": 90,
            "registrationTimestamp": 0,
            "lastRenewalTimestamp": 0,
            "evictionTimestamp": 0,
            "serviceUpTimestamp": 0
        },
        "metadata": {
            "management.port": "8080",
            "jmx.port": "7649"
        },
        "homePageUrl": "http://localhost:8080/",
        "statusPageUrl": "http://localhost:8080/actuator/info",
        "healthCheckUrl": "http://localhost:8080/actuator/health",
        "vipAddress": "unknown",
        "secureVipAddress": "unknown",
        "isCoordinatingDiscoveryServer": "false",
        "lastUpdatedTimestamp": "1586328420409",
        "lastDirtyTimestamp": "1586328420519"
    }

注册到eureka的服务信息查看

get: {ip:port}/eureka/apps

注册到eureka的具体的服务查看

get: {ip:port}/eureka/apps/{appname}/{id}

服务续约

put:{ip:port}/eureka/apps/{appname}/{id}?lastDirtyTimestamp={}&status=up

更改服务状态

put:{ip:port}/eureka/apps/{appname}/{id}/status?lastDirtyTimestamp={}&value={UP/DOWN} 对应eureka源码的:InstanceResource.statusUpdate

删除状态更新

delete:{ip:port}/eureka/apps/{appname}/{id}/status?lastDirtyTimestamp={}&value={UP/DOWN}

删除服务

delete: {ip:port}/eureka/apps/{appname}/{id}

元数据

Eureka的元数据有两种:标准元数据和自定义元数据。 标准元数据:主机名、IP地址、端口号、状态页和健康检查等信息,这些信息都会被发布在服务注册表中,用于服务之间的调用。 自定义元数据:可以使用eureka.instance.metadata-map配置,这些元数据可以在远程客户端中访问,但是一般不改变客户端行为,除非客户端知道该元数据的含义。

可以在配置文件中对当前服务设置自定义元数据,可后期用户个性化使用

元数据可以配置在eureka服务器和eureka的客户端上

eureka.instance.metadata-map.dalao=mashibing

服务端:http://localhost:7900/eureka/apps

1.SpringCloud-Eureka集群搭建-负载均衡,API简单使用,JRebel热部署安装_第19张图片

客户端:

{
    "applications": {
        "versions__delta": "1",
        "apps__hashcode": "UP_2_",
        "application": [
            {
                "name": "EUREKA-CONSUMER",
                "instance": [
                    {
                        "instanceId": "localhost:Eureka-Consumer:9001",
                        "hostName": "localhost",
                        "app": "EUREKA-CONSUMER",
                        "ipAddr": "192.168.29.1",
                        "status": "UP",
                        "overriddenStatus": "UNKNOWN",
                        "port": {
                            "$": 9001,
                            "@enabled": "true"
                        },
                        "securePort": {
                            "$": 443,
                            "@enabled": "false"
                        },
                        "countryId": 1,
                        "dataCenterInfo": {
                            "@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
                            "name": "MyOwn"
                        },
                        "leaseInfo": {
                            "renewalIntervalInSecs": 30,
                            "durationInSecs": 90,
                            "registrationTimestamp": 1586331982283,
                            "lastRenewalTimestamp": 1586331982283,
                            "evictionTimestamp": 0,
                            "serviceUpTimestamp": 1586331982283
                        },
                        "metadata": {
                            "dalao": "mashibing666",
                            "management.port": "9001",
                            "jmx.port": "10158"
                        },
                        "homePageUrl": "http://localhost:9001/",
                        "statusPageUrl": "http://localhost:9001/actuator/info",
                        "healthCheckUrl": "http://localhost:9001/actuator/health",
                        "vipAddress": "Eureka-Consumer",
                        "secureVipAddress": "Eureka-Consumer",
                        "isCoordinatingDiscoveryServer": "false",
                        "lastUpdatedTimestamp": "1586331982283",
                        "lastDirtyTimestamp": "1586331982260",
                        "actionType": "ADDED"
                    },
                    {
                        "instanceId": "localhost:Eureka-Consumer:9000",
                        "hostName": "localhost",
                        "app": "EUREKA-CONSUMER",
                        "ipAddr": "192.168.29.1",
                        "status": "UP",
                        "overriddenStatus": "UNKNOWN",
                        "port": {
                            "$": 9000,
                            "@enabled": "true"
                        },
                        "securePort": {
                            "$": 443,
                            "@enabled": "false"
                        },
                        "countryId": 1,
                        "dataCenterInfo": {
                            "@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
                            "name": "MyOwn"
                        },
                        "leaseInfo": {
                            "renewalIntervalInSecs": 30,
                            "durationInSecs": 90,
                            "registrationTimestamp": 1586331637223,
                            "lastRenewalTimestamp": 1586332057220,
                            "evictionTimestamp": 0,
                            "serviceUpTimestamp": 1586331637223
                        },
                        "metadata": {
                            "dalao": "mashibing",
                            "management.port": "9000",
                            "jmx.port": "10000"
                        },
                        "homePageUrl": "http://localhost:9000/",
                        "statusPageUrl": "http://localhost:9000/actuator/info",
                        "healthCheckUrl": "http://localhost:9000/actuator/health",
                        "vipAddress": "Eureka-Consumer",
                        "secureVipAddress": "Eureka-Consumer",
                        "isCoordinatingDiscoveryServer": "false",
                        "lastUpdatedTimestamp": "1586331637223",
                        "lastDirtyTimestamp": "1586331637182",
                        "actionType": "ADDED"
                    }
                ]
            }
        ]
    }
}

7. EurekaClient

EurekaClient 可以在客户端获取eureka服务器上的注册者信息

org.springframework.cloud.client.discovery与com.netflix.discovery.DiscoveryClient

org.springframework.cloud.client.discovery是SpringCloud对注册中心client的抽象封装,提供公用功能

org.springframework.cloud.client.discovery定义用来服务发现的客户端接口,是客户端进行服务发现的核心接口,是spring cloud用来进行服务发现的顶级接口,在common中可以看到其地位。在Netflix Eureka和Consul中都有具体的实现类。

代表通用于服务发现的读操作,例如在 eureka或consul中有:

String description();//获取实现类的描述。
List getServices();//获取所有服务实例id。
List getInstances(String serviceId);//通过服务id查询服务实例信息列表。

com.netflix.discovery.DiscoveryClient为Eureka注册中心客户端的接口,功能更丰富

测试:点对点的consumer与provider,eureka是没有意义的,直接多搭几个

@RestController
public class MainController {
     

    @Autowired
    DiscoveryClient client;  //spring Cloud自定义的抽象接口

    @Autowired
    EurekaClient client2;

    @Autowired
    LoadBalancerClient lb;


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

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

    @GetMapping("/client2")
    public Object client2(){
     
        //获取服务提供方
        return client.getInstances("provider");
    }

    @GetMapping("/client3")
    public Object client3(){
     
        //获取服务提供方
        List<ServiceInstance> instances = client.getInstances("provider");
        for (ServiceInstance instance : instances) {
     
            System.out.println(ToStringBuilder.reflectionToString(instance));
        }
        return "anzhi";
    }

    @GetMapping("/client4")
    public Object client4(){
     
        //获取服务提供方
        //List instances = client2.getInstancesById("localhost: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);
            }
        }
        return "anzhi";
    }


    @GetMapping("/client5")
    public Object client5(){
     
        //ribbon 完成客户端的负载均衡,过滤掉down了的节点
        ServiceInstance instance = lb.choose("provider");
        String url = "http://"+instance.getHost()+":"+instance.getPort()+"/getHi";
        //发起请求
        RestTemplate restTemplate = new RestTemplate();
        String respStr = restTemplate.getForObject(url, String.class);
        System.out.println("respStr: "+respStr);

        return "anzhi";
    }
}

1. 自我保护机制

1. 机制

Eureka在CAP理论当中是属于AP , 也就说当产生网络分区时,Eureka保证系统的可用性,但不保证系统里面数据的一致性

默认开启,服务器端容错的一种方式,即短时间心跳不到达仍不剔除服务列表里的节点

EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.

默认情况下,Eureka Server在一定时间内,没有接收到某个微服务心跳,会将某个微服务注销(90S)。但是当网络故障时,微服务与Server之间无法正常通信,上述行为就非常危险,因为微服务正常,不应该注销。

Eureka Server通过自我保护模式来解决整个问题,当Server在短时间内丢失过多客户端时,那么Server会进入自我保护模式,会保护注册表中的微服务不被注销掉。当网络故障恢复后,退出自我保护模式。

思想:宁可保留健康的和不健康的,也不盲目注销任何健康的服务。

2. 自我保护触发

客户端每分钟续约数量小于客户端总数的85%时会触发保护机制

自我保护机制的触发条件: (当每分钟心跳次数( renewsLastMin ) 小于 numberOfRenewsPerMinThreshold 时,并且开启自动保护模式开关( eureka.server.enable-self-preservation = true ) 时,触发自我保护机制,不再自动过期租约。) numberOfRenewsPerMinThreshold = expectedNumberOfRenewsPerMin * 续租百分比( eureka.server.renewalPercentThreshold, 默认0.85 ) expectedNumberOfRenewsPerMin = 当前注册的应用实例数 x 2 为什么乘以 2: 默认情况下,注册的应用实例每半分钟续租一次,那么一分钟心跳两次,因此 x 2 。

服务实例数:10个,期望每分钟续约数:10 * 2=20,期望阈值:20*0.85=17,自我保护少于17时 触发。

1. 使用Spring Boot2.x Actuator监控应用
1. 开启监控
<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-actuator</artifactId>
 </dependency>
2. 默认端点

Spring Boot 2.0 的Actuator只暴露了health和info端点,提供的监控信息无法满足我们的需求

在1.x中有n多可供我们监控的节点,官方的回答是为了安全….

开启所有端点

在application.yml中加入如下配置信息

*代表所有节点都加载,yml配置

#开启所有端点
management:
  endpoints:
    web:
      exposure:
        include: '*'

所有端点都开启后的api列表,http://localhost:7900/actuator 本地+端口号/actuator

{
     "_links":{
     "self":{
     "href":"http://localhost:7900/actuator","templated":false},"beans":{
     "href":"http://localhost:7900/actuator/beans","templated":false},"caches-cache":{
     "href":"http://localhost:7900/actuator/caches/{cache}","templated":true},"caches":{
     "href":"http://localhost:7900/actuator/caches","templated":false},"health":{
     "href":"http://localhost:7900/actuator/health","templated":false},"health-path":{
     "href":"http://localhost:7900/actuator/health/{*path}","templated":true},"info":{
     "href":"http://localhost:7900/actuator/info","templated":false},"conditions":{
     "href":"http://localhost:7900/actuator/conditions","templated":false},"configprops":{
     "href":"http://localhost:7900/actuator/configprops","templated":false},"env-toMatch":{
     "href":"http://localhost:7900/actuator/env/{toMatch}","templated":true},"env":{
     "href":"http://localhost:7900/actuator/env","templated":false},"loggers":{
     "href":"http://localhost:7900/actuator/loggers","templated":false},"loggers-name":{
     "href":"http://localhost:7900/actuator/loggers/{name}","templated":true},"heapdump":{
     "href":"http://localhost:7900/actuator/heapdump","templated":false},"threaddump":{
     "href":"http://localhost:7900/actuator/threaddump","templated":false},"metrics-requiredMetricName":{
     "href":"http://localhost:7900/actuator/metrics/{requiredMetricName}","templated":true},"metrics":{
     "href":"http://localhost:7900/actuator/metrics","templated":false},"scheduledtasks":{
     "href":"http://localhost:7900/actuator/scheduledtasks","templated":false},"mappings":{
     "href":"http://localhost:7900/actuator/mappings","templated":false},"refresh":{
     "href":"http://localhost:7900/actuator/refresh","templated":false},"features":{
     "href":"http://localhost:7900/actuator/features","templated":false},"serviceregistry":{
     "href":"http://localhost:7900/actuator/serviceregistry","templated":false}}}

shutdown节点显示:

management:
  endpoints:
    web:
      exposure:
        include: '*'
  endpoint:
    shutdown:
      enabled: true

剔除:

AbstractInstanceRegistry

    public void evict(long additionalLeaseMs) {
     
    logger.debug("Running the evict task");

    if (!isLeaseExpirationEnabled()) {
     
        logger.debug("DS: lease expiration is currently disabled.");
        return;
    }
    此代码意思:if中判断为true,不走此逻辑,走下面的剔除。如果iffalse。走此逻辑,不剔除。
        PeerAwareInstanceRegistryImpl

        @Override
        public boolean isLeaseExpirationEnabled() {
     
        if (!isSelfPreservationModeEnabled()) {
     
            //如果打开自我保护,不进入此逻辑。
            // The self preservation mode is disabled, hence allowing the instances to expire.
            return true;
        }
        return numberOfRenewsPerMinThreshold > 0 && getNumOfRenewsInLastMin() > numberOfRenewsPerMinThreshold;
    }

4. 关闭自我保护

eureka.server.enable-self-preservation=false

关闭后会提示

tupian

5. 清理时间

默认60秒

eureka.server.eviction-interval-timer-in-ms=3000

8.Eureka 健康检查

由于server和client通过心跳保持 服务状态,而只有状态为UP的服务才能被访问。看eureka界面中的status。

比如心跳一直正常,服务一直UP,但是此服务DB连不上了,无法正常提供服务。

此时,我们需要将 微服务的健康状态也同步到server。只需要启动eureka的健康检查就行。这样微服务就会将自己的健康状态同步到eureka。配置如下即可。

1. 开启手动控制

在client端配置:将自己真正的健康状态传播到server。

eureka:
  client:
    healthcheck:
      enabled: true

2. Client端配置Actuator

		
			org.springframework.boot
			spring-boot-starter-actuator
		

3. 改变健康状态的Service

@Service
public class HealthStatusService implements HealthIndicator{

	private Boolean status = true;

	public void setStatus(Boolean status) {
		this.status  = status;
	}

	@Override
	public Health health() {
		// TODO Auto-generated method stub
		if(status)
		return new Health.Builder().up().build();
		return new Health.Builder().down().build();
	}

	public String getStatus() {
		// TODO Auto-generated method stub
		return this.status.toString();
	}

测试用的Controller

	@GetMapping("/health")
	public String health(@RequestParam("status") Boolean status) {
		
		healthStatusSrv.setStatus(status);
		return healthStatusSrv.getStatus();
	}

访问接口,发送fales和true观察服务状态

9 安全管理

1. spring cloud 的security安全管理

导入依赖


<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-securityartifactId>
dependency>

配置密码:

eureka:
  client:
    serviceUrl:
      defaultZone: http://eureka1.com:7900/eureka/
    register-with-eureka: false
    fetch-registry: false


server:
  port: 7900
spring:
  application:
    name: provider
  security:
    user:
      name: anzhi
      password: 123

management:
  endpoints:
    web:
      exposure:
        include: '*'
  endpoint:  # 远程关闭
    shutdown:
      enabled: true

如果服务注册报错

Root name 'timestamp' does not match expected ('instance') for type [simple

是默认开启了防止跨域攻击

手动关闭

在服务端增加配置类

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // TODO Auto-generated method stub
        http.csrf().disable();
        super.configure(http);
    }

}

10. 服务间调用

微服务中,很多服务系统都在独立的进程中运行,通过各个服务系统之间的协作来实现一个大项目的所有业务功能。服务系统间 使用多种跨进程的方式进行通信协作,而RESTful风格的网络请求是最为常见的交互方式之一。

spring cloud提供的方式:

  1. RestTemplate
  2. Feign

RestTemplate自由,方便调用别的第三方的http服务。feign也可以,更面向对象一些,更优雅一些,就是需要配置。

负载均衡图解:

1.SpringCloud-Eureka集群搭建-负载均衡,API简单使用,JRebel热部署安装_第20张图片

Ribbon&&RestTemplate图解

1.SpringCloud-Eureka集群搭建-负载均衡,API简单使用,JRebel热部署安装_第21张图片

1. 负载均衡实现

一个服务端:

yml

eureka:
  client:
    serviceUrl:
      defaultZone: http://eureka1.com:7900/eureka/
    # 自己注册到eureka server,但是本身就是server,所以设置为false,自己不注册自己,默认为true
    register-with-eureka: false
    fetch-registry: false


server:
  port: 7900
spring:
  application:
    name: eurekaserver

management:
  endpoints:
    web:
      exposure:
        include: '*'
  endpoint:  # 远程关闭
    shutdown:
      enabled: true

MainController

import java.util.List;

@RestController
public class MainController {
     

    @Autowired
    RestTemplate restTemplate;


    @GetMapping("/client6")
    public Object client6(){
     
        //ribbon 完成客户端的负载均衡,过滤掉down了的节点
        ServiceInstance instance = lb.choose("provider");
        String url = "http://"+instance.getHost()+":"+instance.getPort()+"/getHi";
        String respStr = restTemplate.getForObject(url, String.class);
        System.out.println("respStr: "+respStr);

        return "anzhi";
    }

}

EurekaproviderApplication.java

@EnableEurekaServer
@SpringBootApplication
public class EurekaproviderApplication {

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

    @Bean
    RestTemplate getRestTemplate(){
        return new RestTemplate();
    }

}

两个服务端

@RestController
public class MainController {
     

    @Autowired
    DiscoveryClient client;  //spring Cloud自定义的抽象接口

    @Autowired
    EurekaClient client2;

    @Autowired
    LoadBalancerClient lb;

    @Autowired
    HealthStatusService healthStatusSrv;

    @Autowired
    RestTemplate restTemplate;

    @Value("${server.port}")
    String port;

    @GetMapping("/getHi")
    public String getHi(){
     
        return "我的端口号是:"+port;
    }

    @GetMapping("/client6")
    public Object client6(){
     
        //ribbon 完成客户端的负载均衡,过滤掉down了的节点
        ServiceInstance instance = lb.choose("provider");
        String url = "http://"+instance.getHost()+":"+instance.getPort()+"/getHi";
        String respStr = restTemplate.getForObject(url, String.class);
        System.out.println("respStr: "+respStr);

        return respStr;
    }
}

yml

eureka:
  client:
    healthcheck:
      enabled: true
    serviceUrl:
      defaultZone: http://eureka1.com:7900/eureka/
server:
  port: 81  # 启动第二个时改变端口
spring:
  application:
    name: provider

再起一个客户端,并改变名称,还是同一个客户端,只是端口好,名称改变

eureka:
  client:
    healthcheck:
      enabled: true
    serviceUrl:
      defaultZone: http://eureka1.com:7900/eureka/
server:
  port: 90  # 启动第二个时改变端口
spring:
  application:
    name: consumer

然后访问http://localhost:90/client6,页面的端口号会发生变化81和80变化

1. 切换负载均衡策略

在服务端配置

EurekaproviderApplication.java

@Bean
public IRule myRule(){
     
    //return new RoundRobinRule();
    return new RandomRule();
    //return new RetryRule();
}

2. 手动负载均衡

在90客户端实现

@GetMapping("/client7")
public Object client7(){
     
    List<ServiceInstance> instances = discoveryClient.getInstances("provider");
    // 自定义的轮询算法
    // 随机
    int nextInt = new Random().nextInt(instances.size());
    ServiceInstance instance = instances.get(nextInt);

    // 轮询
    AtomicInteger atomicIntege = new AtomicInteger();
    int i = atomicIntege.getAndIncrement();
    instances.get(i%instances.size());

    String url = "http://"+instance.getHost()+":"+instance.getPort()+"/getHi";
    String respStr = restTemplate.getForObject(url, String.class);

    return respStr;
}

3. 配置文件

配置文件

注释掉服务器中EurekaclientApplication中定义的负载均衡策略,7900端口

针对服务定ribbon策略:客户端

provider:
      ribbon:
        NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule:
@GetMapping("/client8")
public Object client8(){
     

    ServiceInstance instance = lb.choose("provider");

    String url = "http://"+instance.getHost()+":"+instance.getPort()+"/getHi";
    String respStr = restTemplate.getForObject(url, String.class);

    return respStr;
}

给所有服务定ribbon策略:

ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule

属性配置方式优先级高于Java代码。

4. Ribbon脱离Eureka

ribbon.eureka.enabled=false
ribbon.listOfServers=localhost:80,localhost:81

为service-sms设置 请求的网络地址列表。

Ribbon可以和服务注册中心Eureka一起工作,从服务注册中心获取服务端的地址信息,也可以在配置文件中使用listOfServers字段来设置服务端地址。

5. 自动处理url

客户端改叫服务消费者更恰当:EurekaclientApplication

@Bean
@LoadBalanced
RestTemplate getRestTemplate(){
    return new RestTemplate();
}

MainController

@GetMapping("/client9")
public Object client9(){
     
    //自动处理URL
    String url = "http://provider/getHi";
    String respStr = restTemplate.getForObject(url,String.class);

    System.out.println(respStr);
    return respStr;
}

2. JRebel 热部署安装

摘自:https://blog.csdn.net/u014395955/article/details/106938527/

1.SpringCloud-Eureka集群搭建-负载均衡,API简单使用,JRebel热部署安装_第22张图片

你可能感兴趣的:(微服务,java,ribbon)