摘自百度百科;
SpringCloud是一系列框架的有序集合。它利用SpringBoot的开发便利性巧妙地简化了分布式系统基础设施的开发;如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等;都可以用SpringBoot的开发风格做到一键启动和部署。SpringCloud并没有重复制造轮子;它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来;通过SpringBoot风格进行再封装屏蔽掉了复杂的配置和实现原理;最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
反正SpringCloud是一个用来做微服务的框架;主要是基于SpringBoot来完成的;相关的微服务框架还有比较出名的Dubbo和DubboX。关于微服务是用来做什么的;有什么好处自行百度。
SpringCloud是Spring全家桶中的一员;所以SpringCloud对于Spring的其他组件都有着很好的支持。
SpringCloud主要采用的Eureka;Dubbo主要采用的是zookeeper。
直接新建一个SpringBoot项目;由于是为了示例;本次示例中不使用数据库;只引入了spring-boot-starter-web
项目及其简单;只暴露了一个接口;用于测试;yml文件只配置了端口号为7900
同样新建一个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/demo和http://localhost:7910/consumer/demo应该都能得到ProviderDemo这个结果;说明consumer成功调用了provider中的方法。
问题;
这样的编码方式是将接口;http://localhost:7900/provider/demo;硬编码在代码中;但是项目发布之后;ip地址必然是变动的。而且;硬编码的方式肯定是无法实现负载均衡的;就是说如果同时启动多个provider服务;这种硬编码方式是无法根据负载均衡策略去调用服务的。
解决方法;
在Dubbo中使用的ZooKeeper作为服务注册与发现的容器;在Springcloud中使用的是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前缀
eureka.instance.instanceId为实例的id;可以自定义;默认值为
${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}}
省略了eureka.instance为前缀
回到之前写的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: ;*;
重启服务便可以看到所有的端点都开启用了。
主要的端点的含义可以参考下面的表格;
使用同样的方法把consumer-demo一起注册到Eureka上。
上文只是将服务注册到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/demo和http://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所有的策略可以参照下表;