简介
摘自百度百科:
SpringCloud是一系列框架的有序集合。它利用SpringBoot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用SpringBoot的开发风格做到一键启动和部署。SpringCloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过SpringBoot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
三年前我还在大三的时候,自学了dubbo,如今已忘得一干二净,现在正流行的springCloud,是时候整理一下了。springCloud是一种微服务的框架,最主要的由几部分组成:provider服务提供者、consumer服务消费者、eureka注册中心、ribbon负载均衡、actuator服务监控。基本上就是几个springBoot引入几个依赖,因为是springBoot,所以开发基本上和以前一样,同时也支持spring的其他一些组件。
Provider服务提供者
快速构建一个SpringBoot项目,具体构建方法可以参考 https://www.cnblogs.com/m1996/p/13030161.html ,未见简单方便测试,项目只引入 spring-boot-starter-web , 配置文件只暴露接口8080。由于是服务提供者,对外提供一个Controller,直接返回string "hello cloud"。
依赖如下:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
Consumer服务消费者
同样是一个SpringBoot项目,同样只引入 spring-boot-starter-web , 配置文件只暴露接口7900。
这里解释一下RestTemplates这个类,关于这个类,这个老哥 https://blog.csdn.net/u012702547/article/details/77917939 的解释很清楚了,RestTemplates在SpringCloud中,消费者通过这个类访问生产者,@bean注解是为了实例化这个类,实例化之后通过@AutoWired注解引入,将其交给Spring进行管理。分别启动两个项目,分别访问localhost:8080/provider/demo和http://localhost:7900/consumer/demo应该都能得到“hello cloud”这个结果,说明consumer成功调用了provider中的方法。
问题:
这样的编码方式是将接口(http://localhost:8080/provider/demo)硬编码在代码中,但是项目发布之后,ip地址必然是变动的。而且,硬编码的方式肯定是无法实现负载均衡的,就是说如果同时启动多个provider服务,这种硬编码方式是无法根据负载均衡策略去调用服务的。
解决方法:
在Dubbo中使用的ZooKeeper作为服务注册与发现的容器,在Springcloud中使用的是Eureka作为服务注册与发现的容器。
Eureka注册中心
同样是一个SpringBoot项目,引入 spring-boot-starter-web , 配置文件只暴露接口7900。引入 spring-cloud-starter-netflix-eureka-server 依赖,maven依赖如下:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>
没有版本号是因为有可能会和springBoot的版本冲突,导致项目跑步起来,所以把版本交给dependencyManagement 去处理,他会自动回匹配合适的版本号。具体做法如下:
在maven中引入
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
下面是整个Eureka的pom文件,可以参考。
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>provider</artifactId> <version>0.0.1-SNAPSHOT</version> <name>provider</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.7.1</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project>
然后在Springboot的启动类上添加@EnableEurekaServer注解,并在yml文件中加入Eureka的配置信息。参考如下:
server: port: 8761 eureka: client: register-with-eureka: false fetch-registry: false service-url: defaultZone: http://localhost:8761/eureka/ serviceUrl: defaultZone: http://localhost:8761/eureka/ instance: lease-renewal-interval-in-seconds: 1 lease-expiration-duration-in-seconds: 2
启动项目,打开网页 http://localhost:8761/ 会显示Eureka的实例信息:
这个时候还没有服务提供者和服务消费者的信息,因为我们的服务还没有注册到Eureka。
注册服务到Eureka
在之前创建的 provider和consumer项目中,引入 spring-cloud-starter-netflix-eureka-client 依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>RELEASE</version>
</dependency>
然后再启动类上加上注解 @EnableEurekaClient
@SpringBootApplication @EnableEurekaClient public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class, args); } }
最后在yml文件或properties中加入Eureka的配置就可以了。第二个注解就是该实例的名字,会显示在Eureka的页面。
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
spring.application.name=provider-demo
最后分别启动provider和consumer,在此进入Eureka的页面,会发现我们的启动的服务提供者实例和服务消费者实例已经显示在这个页面:
负载均衡Ribbon
上文已经将服务注册到了Eureka但是,服务调用还是采用硬编码方式,实际到了生产商不可能一直只调用一个服务,也不可能写IP地址,所以使用ribbon负载均衡组件,使用起来非常简单,只要在实例化调用服务的RestTemplate地方加上这个注解就行了:@LoadBalanced,使其具有负载均衡的能力,并把下面配置的IP地址改成服务的实例名。然后Controller就变成了这个样子:
@RestController public class ConsumerController { @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } @Autowired RestTemplate restTemplate; @Autowired private LoadBalancerClient loadBalancerClient; @RequestMapping("/consumer/demo") public String home(User user){ System.out.println(user.toString()); return restTemplate.getForObject("http://provider-demo:8080/provider/demo",String.class); } }
重启一下Eureka,provider-demo,consumer-demo,分别访问localhost:7900/provider/demo和http://localhost:8080/consumer/demo应该都能得到 hello cloud 这个结果,说明consumer成功调用了provider中的方法。便解决了不采用硬编码的方式,使得consumer-demo调用provider-demo接口。
为了验证负载均衡,我们再启动一个provider,端口8081,然后在消费者的controller加入如下代码:
@RequestMapping("/consumer/getInterfaceInfo") public int getInterfaceInfo(){ ServiceInstance choose = loadBalancerClient.choose("provider-demo"); return choose.getPort(); }
可以返回提供者的端口信息,然后再网页调用该端口,http://localhost:7900/consumer/getInterfaceInfo 多次调用页面轮流打印 8080 和8081,说明负载均衡实现了,而且默认是轮询的。ribbon有多种修改负载均衡的策略,可以通过代码,也可以通过配置文件修改。
服务监控
对于服务的众多信息可能都需要监控和监听,SpringCloud主要采用的是下面这个依赖对其实现监听的:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
在provider配置后启动项目,打开网页http://localhost:8081/actuator,会显示监听的内容: