SpringCloud学习笔记(一) 搭建一个SpringCloud

简介

摘自百度百科:
SpringCloud是一系列框架的有序集合。它利用SpringBoot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用SpringBoot的开发风格做到一键启动和部署。SpringCloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过SpringBoot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。

反正SpringCloud是一个用来做微服务的框架,主要是基于SpringBoot来完成的,相关的微服务框架还有比较出名的Dubbo和DubboX。关于微服务是用来做什么的,有什么好处自行百度。

SpringCloud是Spring全家桶中的一员,所以SpringCloud对于Spring的其他组件都有着很好的支持。

SpringCloud主要采用的Eureka,Dubbo主要采用的是zookeeper。

上代码

provider 服务提供者

直接新建一个SpringBoot项目,由于是为了示例,本次示例中不使用数据库,只引入了spring-boot-starter-web

项目及其简单,只暴露了一个接口,用于测试,yml文件只配置了端口号为7900

这里写图片描述

consumer 服务消费者

同样新建一个SpringBoot项目,使其调用consumer的接口,同样只引入了spring-boot-starter-web,yml文件只是修改了端口号为7910。

这里写图片描述

这里解释一下RestTemplates这个类,关于这个类,这个老哥https://blog.csdn.net/u012702547/article/details/77917939的解释我觉得非常完整了,RestTemplates在SpringCloud中,消费者通过这个类访问生产者,@bean注解是为了实例化这个类,实例化之后通过@AutoWired注解引入,将其交给Spring进行管理。

分别启动两个类,分别访问localhost:7900/provider/demohttp://localhost:7910/consumer/demo应该都能得到ProviderDemo这个结果,说明consumer成功调用了provider中的方法。

问题:
这样的编码方式是将接口(http://localhost:7900/provider/demo)硬编码在代码中,但是项目发布之后,ip地址必然是变动的。而且,硬编码的方式肯定是无法实现负载均衡的,就是说如果同时启动多个provider服务,这种硬编码方式是无法根据负载均衡策略去调用服务的。

解决方法:
在Dubbo中使用的ZooKeeper作为服务注册与发现的容器,在Springcloud中使用的是Eureka作为服务注册与发现的容器。

Eureka

同样创建一个SPringBoot项目,下面是pom.xml文件,我是在idea中直接添加spring-cloud-starter-netflix-eureka-server这个依赖的。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.edu.nuaa</groupId>
    <artifactId>eureka-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>eureka-demo</name>
    <description>eureka-demo</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
    </properties>

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

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

然后在Springboot的启动类上添加@EnableEurekaServer或者@EnableDiscoveryClient,这两个注解的注解的区别主要是:

@EnableEurekaServer是基于 spring-cloud-netflix依赖,只能为eureka作用,是专门给Eureka用的
@EnableDiscoveryClient是基于 spring-cloud-commons依赖,并且在classpath中实现,是给比如zookeeper、consul使用的,
旧版本的@EnableEurekaServer的源码上面也是也是有@EnableDiscoveryClient注解的。
本示例是使用Eureka的,故推荐使用@EnableEurekaServer

@SpringBootApplication
@EnableEurekaServer
public class EurekaDemoApplication {

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

然后在yml文件中配置如下信息:

server:
  port: 8761
eureka:
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://localhost:8761/eureka/

这个端口号官网设置成8761,这里也设置成8761;

`register-with-eureka: false这个的默认值为true,设置为true不会对使用不会有很大的影响,但是在启动的时候会保下面的错误:
was unable to refresh its cache! status = Cannot execute request on any known server
是因为启动的时候自己注册了自己而引起的冲突

defaultZone配置eureka的地址,这个如果有多个注册中心,则用逗号隔开。
为了服务注册中心的安全考虑,很多时候我们都会为服务注册中心加入安全认证。通常与SpringSecurity整合,主要是SpringSecurity的内容,这里不做讨论(主要是我自己尝试的时候失败了,啊哈哈哈)。

配置信息完成之后,启动eureka项目,访问localhost:8761便可以得到下面的界面,目前上面两个写的provider和consumer还没有注册到该注册中心:
这里写图片描述

关于Eureka配置的含义及其默认值主要参考下表:

服务注册类配置

省略了eureka.client前缀

参数名说明默认值
enabled启用Eureka客户端true
registryFetchIntervalSeconds从Eureka服务端获取注册信息的间隔时间,单位为秒30
instanceInfoReplicationIntervalSeconds更新实例信息的变化到Eureka服务端的间隔时间,单位为秒30
initialInstanceInfoReplicationIntervalSeconds初始化实例信息到Eureka服务端的间隔时间,单位为秒40
eurekaServiceUrlPollIntervalSeconds轮询Eureka服务端地址更改的间隔时间,单位为秒。当我们与Spring CLoud Config整合,动态刷新Eureka的serviceURL地址时需要关注该参数300
eurekaServerReadTimeoutSeconds读取Eureka Server信息的超时时间,单位为秒8
eurekaServerConnectTimeoutSeconds链接Eureka Server的超时时间,单位为秒5
eurekaServerTotalConnections从Eureka客户端到所有Eureka服务端的连接总数200
eurekaServerTotalConnectionsPerHost从Eureka客户端到每个Eureka服务端主机的连接总数50
eurekaConnectionIdleTimeoutSecondsEureka服务端连接的空闲关闭时间,单位为秒30
heartbeatExecutorThreadPoolSize心跳连接池的初始化线程数2
heartbeatExecutorExponentialBackOffBound心跳超时重试延迟时间的最大乘数值10
cacheRefreshExecutorThreadPoolSize缓存刷新线程池的初始化线程数2
cacheRefreshExecutorExponentialBackOffBound缓存刷新重试延迟时间的最大乘数值10
useDnsForFetchingServiceUrls使用DNS来获取Eureka服务端的serviceUrlfalse
registerWithEureka是否要将自身的实例信息注册到Eureka服务端true
preferSameZoneEureka是否偏好使用处于相同Zone的Eureka服务端true
filterOnlyUpInstances获取实例时是否过滤,仅保留UP状态的实例true
fetchRegistry是否从Eureka服务端获取注册信息true
服务实例类配置

eureka.instance.instanceId为实例的id,可以自定义,默认值为

${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}}

省略了eureka.instance为前缀

参数名说明默认值
preferIpAddress是否优先使用IP地址作为主机名的标识false
leaseRenewalIntervalInSecondsEureka客户端向服务端发送心跳的时间间隔,单位为秒30
leaseExpirationDurationInSecondsEureka服务端在收到最后一次心跳之后等待的时间上限,单位为秒。超过该时间之后服务端会将该服务实例从服务清单中剔除,从而禁止服务调用请求被发送到该实例上90
nonSecurePort非安全的通信端口号80
securePort安全的通信端口号443
nonSecurePortEnabled是否启用非安全的通信端口号true
securePortEnabled是否启用安全的通信端口号
appname服务名,默认取spring.application.name的配置值,如果没有则为unknown
hostname主机名,不配置的时候讲根据操作系统的主机名来获取

注册服务到Eureka

回到之前写的provider-demo项目中,在项目中添加下面这个依赖,注意version:

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

在之前的SpringCloud版本中,还有出现过下面的依赖,但是目前最新的版本是上面这个,亲测,在本示例中两个都是可以的。

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

然后在项目的启动类上添加@EnableEurekaClient注解

@SpringBootApplication
@EnableEurekaClient
public class ProviderDemoApplication {

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

从依赖的名字spring-cloud-starter-netflix-eureka-client的名字就知道这个依赖是用来做eureka的客户端,然后注册服务到eureka上的。然后在项目的启动类上的添加的注解为@EnableEurekaClient

回到上文中会发现搭建eureka注册中心的时候会发现引入的包是spring-cloud-starter-netflix-eureka-server,在项目的启动类上的添加的注解为@EnableEurekaServer还是很好记忆和理解的。

最后在yml文件中添加上eureka注册中心的地址并可以了:

server:
  port: 7900
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

分别启动一下eureka和provider项目,然后访问一下localhost:8761便会得到下面的页面:
这里写图片描述

可以看到provider的服务已经注册上eureka上了。
发现application的名称为UNKNOWN,可以在yml文件中添加下面内容修改名字:

spring:
  application:
    name: provider-demo

重启一下服务便可以看到名字,这里就不贴图片了。

有可能会遇到下面的红色字体:

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的自我保护的心跳机制导致的,即如果真实的服务已经Down掉,但在注册中心界面服务却一直存在,且显示为UP状态。解决方法可以添加下面内容,上面有eureka的配置信息中解释含义:

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
  instance:
    lease-renewal-interval-in-seconds: 1
    lease-expiration-duration-in-seconds: 2
服务监控

对于服务的众多信息可能都需要监控和监听,SpringCloud主要采用的是下面这个依赖对其实现监听的:

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

添加这个依赖之后重启服务会发现console中多了下图这几个端点:

这里写图片描述

/actuator/health和/actuator/info以及/actuator这三个就是actuator提供的端点,注意以前的版本是没有/actuator前缀的,2.0以后的版本都加了/actuator前缀。可以访问localhost:7900/actuator尝试看监听的都是一些什么内容

如果要开启全部的监听端点,可以在yml文件中加入下面的配置信息:

management:
  endpoints:
    web:
      exposure:
        include: "*"

重启服务便可以看到所有的端点都开启用了。

主要的端点的含义可以参考下面的表格:

端点描述
actuator为其他端点提供“发现页面”。要求Spring HATEOAS在classpath路径上。
auditevents陈列当前应用程序的审计事件信息。
autoconfig展示自动配置信息并且显示所有自动配置候选人以及他们“被不被”应用的原因。
beans显示应用程序中所有Spring bean的完整列表。
configprops显示所有配置信息。
dumpdump所有线程。
env陈列所有的环境变量。
flywayShows any Flyway database migrations that have been applied.
health显示应用程序运行状况信息
info显示应用信息。
loggers显示和修改应用程序中的loggers配置。
liquibase显示已经应用的任何Liquibase数据库迁移。
metrics显示当前应用程序的“指标”信息。
mappings显示所有@RequestMapping的url整理列表。
shutdown关闭应用(默认情况下不启用)。
trace显示跟踪信息(默认最后100个HTTP请求)。

使用同样的方法把consumer-demo一起注册到Eureka上。

ribbon

上文只是将服务注册到eureka上,但是consumer还是硬编码调用,前文也有提到这种硬编码方式肯定是不合理的,一来服务上线之后,IP地址肯定是变动的, 再则,采用硬编码的方式是无法实现负载均衡的。

ribbon便是一个用来做负载均衡的组件。

点进spring-cloud-starter-netflix-eureka-client依赖,会发现,这个依赖中已经添加了spring-cloud-starter-netflix-ribbon,故在项目中可以直接使用ribbon,不用重新添加依赖。

在加载restTemplates的方法上添加@LoadBalanced注解,使其具有负载均衡的能力,然后将硬编码的ip地址换成服务提供者的应用名字(application.name属性的值),修改之后controller便变成了下面的样子,其他地方不做修改。

@RestController
public class ConsumerController {

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

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/consumer/demo")
    public String ConsumerDemo(){
        return this.restTemplate.getForObject("http://provider-demo:7900/provider/demo", String.class);
    }

}

重启一下Eureka,provider-demo,consumer-demo,分别访问localhost:7900/provider/demohttp://localhost:7910/consumer/demo应该都能得到ProviderDemo 这个结果,说明consumer成功调用了provider中的方法。便解决了不采用硬编码的方式,使得consumer-demo调用provider-demo接口。

ribbon默认的负载均衡策略是轮询的。测试一下:

首先启动多个provider-demo项目,我这里分别用的接口是7900和7901两个接口,可以在Eureka中看一下provider的信息:

这里写图片描述

然后在consumer-demo项目的controller中添加下面的内容,代码应该很容易看懂就是模拟loadBalancerClient客户端来访问名字为provider-demo的服务接口,然后输出访问的接口端口号。

 @Autowired
 private LoadBalancerClient loadBalancerClient;

 @RequestMapping("/consumer/getInterfaceInfo")
 public void getInterfaceInfo(){
     ServiceInstance choose = loadBalancerClient.choose("provider-demo");
     System.out.println(choose.getPort());
 }

然后分别访问一下多次访问localhost:7910//consumer/getInterfaceInfo这个URL,可以看到控制台输出的信息在下面,很容易看出是轮询。

7901
7900
7901
7900
7901
7900
7901
7900
7901
7900
7901

ribbon有多种修改负载均衡的策略,可以通过代码,也可以通过配置文件,个人觉得配置文件的方法比较方便,其他的方法可以自行百度:

在consumer-demo的application.yml文件中添加下面的内容,这里使用的是随机的策略,该测试就不贴图了,反正最后结果是随机的:

#服务提供者的名字
provider-demo:  
  ribbon:
    # 所要采用的策略
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

ribbon所有的策略可以参照下表:

策略名策略描述实现说明
BestAvailableRule选择一个最小的并发请求的server逐个考察Server,如果Server被tripped了,则忽略,在选择其中ActiveRequestsCount最小的server
AvailabilityFilteringRule过滤掉那些因为一直连接失败的被标记为circuit tripped的后端server,并过滤掉那些高并发的的后端server(active connections 超过配置的阈值)使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就就是检查status里记录的各个server的运行状态
WeightedResponseTimeRule根据响应时间分配一个weight,响应时间越长,weight越小,被选中的可能性越低。一个后台线程定期的从status里面读取评价响应时间,为每个server计算一个weight。Weight的计算也比较简单responsetime 减去每个server自己平均的responsetime是server的权重。当刚开始运行,没有形成status时,使用roubine策略选择server。
RetryRule对选定的负载均衡策略机上重试机制。在一个配置时间段内当选择server不成功,则一直尝试使用subRule的方式选择一个可用的server
RoundRobinRuleroundRobin方式轮询选择server轮询index,选择index对应位置的server
RandomRule随机选择一个server在index上随机,选择index对应位置的server
ZoneAvoidanceRule复合判断server所在区域的性能和server的可用性选择server使用ZoneAvoidancePredicate和AvailabilityPredicate来判断是否选择某个server,前一个判断判定一个zone的运行性能是否可用,剔除不可用的zone(的所有server),AvailabilityPredicate用于过滤掉连接数过多的Server。
  • 33
    点赞
  • 224
    收藏
    觉得还不错? 一键收藏
  • 16
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值