该笔记简化并省略了一些官方介绍,使用比价通俗的白话进行讲述,如果有什么需要详细了解的请自行查询资料学习
1. SpringCloud简介
SpringCloud主要功能在于服务间调用,服务可以分为提供者和消费者,提供者来提供数据,消费者来使用数据。
基于restTemplate进行服务间调用。
2. 创建项目
创建maven项目,pom.xml中添加以下配置
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<mysql.version>5.1.47</mysql.version>
<druid.version>1.1.21</druid.version>
<mybatis.version>1.3.2</mybatis.version>
<lombok.version>1.18.16</lombok.version>
<logback.version>1.2.3</logback.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- SpringCloud依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR9</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- SpringBoot依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- mysql数据库依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- 阿里数据源依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!-- logback -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
到此我们已经为SpringCloud项目搭建了一个地基。
此时,我们可以删除掉项目下的src目录,因为我们现在创建的仅仅是一个父级项目而已。
3. Eureka注册中心
为了使服务间调用更加便捷,延伸出了注册中心,Eureka就是一种注册中心,你所启动的服务需要注册到注册中心上才能够被其他服务所发现。也就是说一个服务调用另一个服务首先去注册中心查询要调用的服务信息(打个比方就是查电话本),通过查到的信息再请求对应的地址(打个比方就是查到了电话号码了要拨打对应人的手机号)。
Eureka基于Rest,用于定位服务。
通过Eureka可以监控所有服务,来查看服务是否正常运行。
创建Eureka注册中心
我们在父级项目中创建一个Module,然后添加以下依赖
<!-- SpringCloud的Eureka依赖 -->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
</dependencies>
然后在resource中创建application.yml配置文件,添加以下配置
server:
## 配置端口号
port: 9001
eureka:
instance:
## 注册中心的hostname
hostname: localhost
client:
## 是否向注册中心注册自己
register-with-eureka: false
## false时表示当前服务为注册中心
fetch-registry: false
service-url:
## 注册中心位置
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
创建启动类,添加如下代码
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class FneeEurekaApplication {
public static void main(String[] args) {
SpringApplication.run(FneeEurekaApplication.class);
}
}
现在你可以尝试启动以下该项目,访问localhost:9001就可以看到项目的注册中心页面了,如下
注册测试
现在你可以在项目中再创建一个Module,添加如下依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
配置文件添加如下配置
server:
port: 8081
spring:
application:
## application名称
name: provider
eureka:
client:
service-url:
## eureka注册地址
defaultZone: http://localhost:9001/eureka/
instance:
## 定义注册中心页面实例的status值
instance-id: springCloud-provider-8081
启动项目后会发现,在注册中心页面多了一个Application
集群
eureka集群可以避免当注册中心down掉时影响网站的正常运行。
创建多个eureka注册中心,可以放到不同的服务器进行进行通信,通过注册中心的互相绑定来达到集群的目的。
在eureka注册中心中的配置文件中配置defaultZone来绑定其他注册中心,多个注册中心可以通过英文逗号间隔。
如当前有http://testEureka001.com/eureka
、http://testEureka002.com/eureka
、http://testEureka003.com/eureka
三个注册中心,可以如下配置
注册中心 | defaultZone |
---|---|
http://testEureka001.com/eureka | http://testEureka002.com/eureka,http://testEureka003.com/eureka |
http://testEureka002.com/eureka | http://testEureka001.com/eureka,http://testEureka003.com/eureka |
http://testEureka003.com/eureka | http://testEureka001.com/eureka,http://testEureka002.com/eureka |
然后在被注册的模块儿中的defaultZone值可以是http://testEureka001.com/eureka,http://testEureka002.com/eureka,http://testEureka003.com/eureka
4. Ribbon负载均衡
ribbon负责将用户的请求平摊的分配到服务器上,达到系统的HA(高可用)
模拟负载均衡
条件:当前有多个服务提个者(如:创建三个服务名相同的消费者,比如都是provider,相同接口返回不同数据)
创建一个新的Module,作为消费者
pom依赖如下
<dependencies>
<!-- Ribbon依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
配置文件如下
server:
port: 8082
eureka:
client:
## 向注册中心注册当前服务
register-with-eureka: false
service-url:
defaultZone: http://localhost:9001/eureka/
创建一个config类,添加如下代码
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ConfigBean {
@Bean
@LoadBalanced //负载均衡注释
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
创建一个controller类来创建接口
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@RequestMapping("/test")
public class TestController {
@Autowired
private RestTemplate restTemplate;
// 要调用的服务名称
private static final String REST_SERVER_NAME = "http://PROVIDER";
@GetMapping("/get")
public String testGet() {
return restTemplate.getForObject(REST_SERVER_NAME + "/test/get", String.class);
}
@PostMapping("/post")
public String testPost(String data) {
return restTemplate.postForObject(REST_SERVER_NAME + "/test/post", data, String.class);
}
}
然后访问接口,然后不断刷新,会发现获取到的数据是不同的
修改负载均衡策略
可以在配置类(如上面的ConfigBean)中添加如下代码
@Bean
public IRule myIRule(){
return new RandomRule();
}
以下是Ribbon包含的负载均衡策略
策略类 | 名称 | 描述 |
---|---|---|
RandomRule | 随机策略 | 随机选择server |
RoundRobinRule | 轮询策略 | 轮询选择, 轮询index,选择index对应位置的Server; |
RetryRule | 重试策略 | 对选定的负载均衡策略机上重试机制,在一个配置时间段内当选择Server不成功,则一直尝试使用subRule的方式选择一个可用的server; |
BestAvailableRule | 最低并发策略 | 逐个考察server,如果server断路器打开,则忽略,再选择其中并发链接最低的server |
AvailabilityFilteringRule | 可用过滤策略 | 过滤掉一直失败并被标记为circuit tripped的server,过滤掉那些高并发链接的server(active connections超过配置的阈值)或者使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就就是检查status里记录的各个Server的运行状态; |
ResponseTimeWeightedRule | 响应时间加权重策略 | 根据server的响应时间分配权重,响应时间越长,权重越低,被选择到的概率也就越低。响应时间越短,权重越高,被选中的概率越高,这个策略很贴切,综合了各种因素,比如:网络,磁盘,io等,都直接影响响应时间 |
ZoneAvoidanceRule | 区域权重策略 | 综合判断server所在区域的性能,和server的可用性,轮询选择server并且判断一个AWS Zone的运行性能是否可用,剔除不可用的Zone中的所有server |
自定义策略
自定义策略不能在ComponentScan扫描范围内(不能与启动类同级或同级的包下)
创建自定义策略类,如下(具体算法逻辑请自己完善)
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import org.springframework.context.annotation.Configuration;
import java.util.List;
public class MyRule extends AbstractLoadBalancerRule {
@Override
public Server choose(Object o) {
// 获取负载均衡器
ILoadBalancer loadBalancer = getLoadBalancer();
if (loadBalancer == null) {
return null;
}
Server server = null;
while (server == null){
if (Thread.interrupted()) {
return null;
}
// 获取所有服务器
List<Server> allServers = loadBalancer.getAllServers();
if (allServers.size() == 0) {
return null;
}
// 获取可用服务器
List<Server> reachableServers = loadBalancer.getReachableServers();
//... 算法逻辑
}
return server;
}
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
}
然后定义一个策略配置类,如下
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import org.springframework.context.annotation.Configuration;
import java.util.List;
@Configuration
public class MyRuleConfig extends AbstractLoadBalancerRule {
@Bean
public IRule myRuleBean() {
return new MyRule();
}
}
然后在启动类上添加@RibbonClient(name = "PROVIDER", configuration = MyRuleConfig.class)
注解
name:与当前服务相同名即可
configuration:自定义的策略类
持续更新中...