1.自定义负载均衡规则
接口的实现类:
对剩下的服务进行测试
更改规则
@Configuration
public class ConfigBean {
@Bean
@LoadBalanced // SpringCloud Ribbon 是基于客户端实现的一套负载均衡的工具
public RestTemplate restTemplate(){
return new RestTemplate();
}
//更改lb规则
@Bean
public IRule myRule(){
return new RandomRule();
}
}
RandomRule的源码
照猫画虎
public class RandomRule extends AbstractLoadBalancerRule {
// 默认的随机算法分析 ILoadBalancer负载均衡
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
// 最终选择的服务
Server server = null;
while (server == null) {
if (Thread.interrupted()) {
return null;
}
// 获取所有上线的服务 8001 8002 8003
List<Server> upList = lb.getReachableServers();
// 获取所有所有的服务 8001 8002 8003 ......
List<Server> allList = lb.getAllServers();
// 获取服务数量
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}
// 随机选择一个
int index = chooseRandomInt(serverCount);
// 获得这个随机选中的服务
server = upList.get(index);
if (server == null) {
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
server = null;
Thread.yield();
}
return server;
}
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
// TODO Auto-generated method stub
}
}
@Configuration
public class MySelfRule {
// 修改LB策略
@Bean
public IRule myRule(){
return new CodingRandomRule();
}
}
自定义
public class CodingRandomRule extends AbstractLoadBalancerRule {
// 8001 8002 8003 , 每个服务调用5次,轮询升级版
// 逻辑
// 1、total = 0,当total数量为5,就切换到下一个服务,重置为0
// 2、index = 0. 对外提供的服务选择 ,当index>3, 重置为0
private int total = 0;
private int currentIndex = 0;
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
// 最终选择的服务
Server server = null;
while (server == null) {
if (Thread.interrupted()) {
return null;
}
// 获取所有上线的服务 8001 8002 8003
List<Server> upList = lb.getReachableServers();
// 获取所有所有的服务 8001 8002 8003 ......
List<Server> allList = lb.getAllServers();
// 获取服务数量
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}
// 随机选择一个 (算法的具体位置)
if (total < 5){ // 如果当前total小于5
server = upList.get(currentIndex);
total++;
} else { // 如果当前total大于5
total = 1;
currentIndex++;
// >=
if (currentIndex >= upList.size()){
currentIndex = 0;
}
server = upList.get(currentIndex);
}
// ======= END =======
if (server == null) {
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
server = null;
Thread.yield();
}
return server;
}
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
// TODO Auto-generated method stub
}
}
自定义位置
不能在启动类一级或子集
启动类加上注解
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableEurekaClient //客户端
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT")
public class DeptConsumer80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer80.class,args);
}
}
2.feign
feign 是声明式的 web service 客户端,它让我们微服务调用更加简单了,类似controller 调用 servive
feign,主要是社区,大家都习惯,面向接口编程,封装的resttemplate。
由此,调用微服务方式就有了两种:
1、微服务名字 (Ribbon)
2、接口和注解 (Feign)
Feign能干什么?
Feign可以让我们编写代码更加简单和轻洁!
Feign集成了Ribbon、所有说也支持负载均衡。调用Ribbon
使用feign
1.服务端提供者通过 公共模块 来连接
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
2.公共模块编写接口
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT")
public interface DeptClientService {
@GetMapping("/dept/list")
public List<Dept> list();
@GetMapping("/dept/get/{id}")
public Dept findById(@PathVariable("id")Long id);
}
3.客户端导入依赖编写controller
@RestController
public class DeptConsumerController {
/**
*
*/
@Autowired
private DeptClientService deptClientService;
/*private RestTemplate restTemplate;
private static final String REST_URL_PREFIX = "http://localhost:8001/dept";*/
@RequestMapping("consumer/list")
public List<Dept> list(){
return deptClientService.list();
}
@RequestMapping("consumer/findById/{id}")
public Dept findById(@PathVariable("id") Long id){
return deptClientService.findById(id);
}
}
4.启动类加注释
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableEurekaClient //客户端
@EnableFeignClients(basePackages = "com.sheng")
@ComponentScan("com.sheng")
public class DeptConsumerRibbon81 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumerRibbon81.class,args);
}
}
5.测试
Feign 通过接口的方式调用Rest服务。 (Ribbon + RestTemplate)
我们通常开发使用 Feign 进行调用即可!
Feign 发送请求到 Eurekka服务中,通过 Feign 直接找到对应的服务接口,由于在调用的使用,也融合 Ribbon,所以说也可以是实现负载均衡。
Feign其实不是做负载均衡的,负载均衡是ribbon的功能,feign只是集成了ribbon!
feign的作用就是替代 RestTemplate,性能会相对低一点,但是可以使代码的可读性更好。
3.Hystrix断路器
服务雪崩
多个微服务之间的调用,假设A 调用 B ,B调用C, C 又调用… , 这就是所谓的 扇出。 假设 扇出 的链路上,某一个微服务调用响应时间过长,或者直接不可用,对于微服务A ,这个时候就 会占用越来越多的系统资源,从而可能引起系统崩溃,这就是所谓的 “雪崩效应”。
对于高流量的应用来说,单例的后端依赖可能会导致所有服务器上的资源在几秒中内就饱和。比失败更 加糟糕,导致 服务之间的延迟增加,队列,线程资源都紧张,可能导致更多的问题出现。
割以永治
什么是Hystrix
Hystrix 是一个用于处理分布式系统的延迟和容错的库。组件,在分布式系统中,许多的依赖,不可避免 会调用失败。比如超时,异常,Hystrix 就可以保证在一个依赖出现了问题的时候,不会导致整体的服 务失败,避免级联故障。提高分布式系统的弹性
“ 断路器 ” ,就是开关装置
保险丝,一旦触发危险,就会立马熔断,保证安全! 备份,副本!比如调用失败或者超时的时候,编写 一个失败的响应事件返回给客户端,保证可以用就可以了
作用:
1、服务降级
2、服务熔断
3、服务限流
4、实时监控 。。。
服务熔断
1.导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
2.处理问题,编写解决方案
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
// 面对这种情况,就可以使用Hystrix,
// 使用 @HystrixCommand 然后配置 fallbackMethod
// 一旦调用服务失败,抛出了错误信息后,就自动会调用 fallbackMethod的方法!
@HystrixCommand(fallbackMethod = "hystrix_get_fallbackMethod")
@GetMapping("/dept/get/{id}")
public Dept get(@PathVariable("id") Long id){
Dept dept = deptService.queryById(id);
if (dept==null){
throw new RuntimeException("该id"+id + "没有查询到对应的信息");
}
return dept;
}
// 出现问题的解决方案!备用
public Dept hystrix_get_fallbackMethod(@PathVariable("id") Long id){
return new Dept().setDeptno(id)
.setDname("该id"+id+"没有对应的信息null=>hystrix_get_fallbackMethod")
.setDb_source("no this data in mysql");
}
}
3.启动类加注解
@SpringBootApplication
@EnableEurekaClient // 本服务启动之后,就会自动注册到 Eureka 中
@EnableDiscoveryClient // 显示定义服务发现
@EnableCircuitBreaker // 对Hystrix熔断机制的支持
public class DeptProviderHystrix8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProviderHystrix8001.class,args);
}
}
4.测试
服务降级
1.编写降级调用的方法
// 客户端调用的失败的处理!
// 1、客户端编写 FallbackFactory 类,实现 FallbackFactory接口,参数就是要处理的业务方法
// 2、编写对应的逻辑(处理,接口调用这个 FallbackFactory)
// 3、丢到spring中
@Component
public class DeptClientServiceFallbackFactory implements FallbackFactory<DeptClientService> {
public DeptClientService create(Throwable throwable) {
return new DeptClientService() {
public List<Dept> list() {
return null;
}
public Dept findById(Long id) {
return new Dept().setDb_source("明日退款")
.setDeptno(id)
.setDname("该id"+id+"没有对应的信息 null=>hystrix_get_fallbackMethod");
}
};
}
}
2、将这个类写会到 接口
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT",fallbackFactory = DeptClientServiceFallbackFactory.class)
public interface DeptClientService {
@GetMapping("/dept/list")
public List<Dept> list();
@GetMapping("/dept/get/{id}")
public Dept findById(@PathVariable("id")Long id);
}
3、配置客户端开启支持
server:
port: 80
# Eureka
eureka:
client:
register-with-eureka: false # 客户端,不用注册进去
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
# 开启支持
feign:
hystrix:
enabled: true
4.测试 开一个服务
服务熔断:一般是因为一个服务故障,或者一个异常引起的,类似现实世界的保险丝、、触发了异常, 就直接熔断。不会等待服务超时。服务端的设定
服务降级:所谓降级,从整体的负荷考虑,将某个服务挂掉了,不能再被调用了,这个时候客户端可以 准备一个自己的本地fallback进行回调,返回缺省值,这样的,虽然整体架构上,服务水平会下降,但是 比直接挂掉强
服务监控(可视化面板)
1.引入监控依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
2.配置
server:
port: 9001
3.启动类
@SpringBootApplication
@EnableHystrixDashboard//开启支持
public class DeptDashboard9001 {
public static void main(String[] args) {
SpringApplication.run(DeptDashboard9001.class,args);
}
}
4.测试
看服务8001 8002 8003 是否都有
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
在我们的 hystrix 8001 中,添加一个bean
@SpringBootApplication
@EnableEurekaClient // 本服务启动之后,就会自动注册到 Eureka 中
@EnableDiscoveryClient // 显示定义服务发现
@EnableCircuitBreaker // 对Hystrix熔断机制的支持
public class DeptProviderHystrix8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProviderHystrix8001.class,args);
}
// 本事增加了一个Servlet用来监控
@Bean
public ServletRegistrationBean hystrixMetricsStreamServlet(){
ServletRegistrationBean bean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
bean.addUrlMappings("/actuator/hystrix.stream");
return bean;
}
}
启动测试访问流
http://localhost:8001/actuator/hystrix.stream
使用控制面板
7色
一圈:实例的健康程度
大小:如果越大,代表流量越大,这个圆就越大 颜色:绿色 < 黄色 < 橙色 < 红色
一线:记录2分钟内的流量相对变化,可以观测流量的上升和下降趋势
集群
https://www.cnblogs.com/knowledgesea/p/11283285.html
这个博客写的比较详细
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-turbine</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
配置文件
spring.application.name=shouhou-turbine
server.port=8086
turbine.appConfig=TRADE-ORDER
turbine.aggregator.clusterConfig= default
turbine.clusterNameExpression= new String("default")
turbine.combine-host-port=true
eureka.client.serviceUrl.defaultZone=http://localhost:8081/eureka/
配置文件对比
spring.application.name=hystrix-dashboard-turbine
server.port=8001
turbine.appConfig=node01,node02
turbine.aggregator.clusterConfig= default
turbine.clusterNameExpression= new String("default")
eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/
spring.application.name=node01
server.port=9001
feign.hystrix.enabled=true
eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/
spring.application.name=node02
server.port=9002
feign.hystrix.enabled=true
eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/
#服务名为turbine
spring.application.name=turbine
server.port=8989
management.port=8990
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
#turbine.app-config=RIBBON-CONSUMER,RIBBON-CONSUMER-2
#指定需要收集监控信息的服务名
turbine.app-config=RIBBON-CONSUMER
#指定集群的名称为default,当服务实例非常多的时候,可以启动多个Turbine来
#构建不同的聚合集群,而该参数可以区分这些不同的集群,同时该参数可以在
#Hystrix仪表盘中用来定位不同的聚合集群,只需在Hystrix Stream的URL中通过cluster
#参数来指定。
turbine.cluster-name-expression="default"
#当该参数未true时,可以让同一主机上的服务通过主机名与端口号
#的组合来进行区分,默认情况下会以host来区分不同的服务,这
#会使得在本地调试的时候,本机上不同服务聚合成一个服务来统计
turbine.combine-host-port=true
clusterConfig: default # 指定聚合哪些集群,多个使用","分割,默认为default。可使用http://…/turbine.stream?cluster={clusterConfig之一}访问
appConfig: service_a,service_b # 配置Eureka中的serviceId列表,表明监控哪些服务
clusterNameExpression: new String(“default”)
- clusterNameExpression指定集群名称,默认表达式appName;此时:turbine.aggregator.clusterConfig需要配置想要监控的应用名称
2.当clusterNameExpression: default时,turbine.aggregator.clusterConfig可以不写,因为默认就是default
3.当clusterNameExpression: metadata[‘cluster’]时,假设想要监控的应用配置了eureka.instance.metadata-map.cluster: ABC,则需要配置,同时turbine.aggregator.clusterConfig: ABC
Zuul路由网关
Zuul 包含了对请求和路由的过滤两个主要的功能:
路由功能:负责将外部的请求转发到具体的微服务实例,是一个实现外部访问统一入口的基础。
过滤功能:负责对请求的处理过程进行干预。 实现请求校验
Zuul 可以和 eureka 整合,将Zuul自身作为eureka 下面的应用
三大功能:代理、过滤、路由
使用
1.导入依赖
<!--导入依赖-->
<dependencies>
<!-- 导入依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!--Zuul-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
</dependencies>
<!--build-->
<build>
<finalName>springcloud</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<delimiters>
<!-- 以$开头或者结果的在src/main/resources就可以读取 -->
<delimiter>$</delimiter>
</delimiters>
</configuration>
</plugin>
</plugins>
</build>
2.配置文件
server:
port: 9527
spring:
application:
name: springcloud-zuul-9527
# Eureka
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/
instance:
instance-id: 网关9527
info:
app.name: sheng的微服务网关9527
company.name: 公司名
build.artifactId: ${project.artifactId}
build.version: ${project.version}
3 启动类
@EnableZuulProxy
@SpringBootApplication
public class SpringZuul9527 {
public static void main(String[] args) {
SpringApplication.run(SpringZuul9527.class,args);
}
}
这样暴露服务名
隐藏名称
#zuul配置
zuul:
routes:
mydept.serviceId: springcloud-provider-dept
mydept.path: /mydept/**
#zuul配置
zuul:
routes:
mydept.serviceId: springcloud-provider-dept
mydept.path: /mydept/**
ignored-services: springcloud-provider-dept #不允许访问
prefix: /icoding #公共前缀
SpringCloud Config 分布式配置中心
微服务太多 配置文件十分麻烦
1.创建一个远程仓库
在本地仓库新建一个 application.yml配置文件,编码注意一定要是 utf-8
1.导入依赖
<!--导入依赖-->
<dependencies>
<!-- spring-cloud-config-server 的配置 -->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-config-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
<!-- 导入依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!--Zuul-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
</dependencies>
2.配置文件
server:
port: 3344
spring:
application:
name: springcloud-config
# 配置git的绑定
cloud:
config:
server:
git: # 细节: uri, 不是url
uri: https:自己仓库路径
3.启动类
@SpringBootApplication
@EnableConfigServer // 开启config服务
public class ConfigApp3344 {
public static void main(String[] args) {
SpringApplication.run(ConfigApp3344.class,args);
}
}
4.测试
通过3344能否访问到配置文件
配置 Config 客户端进程连接 Config Server
<!--导入依赖-->
<dependencies>
<!-- 客户端的依赖配置 -->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-config-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
<!-- 导入依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!--Zuul-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
</dependencies>
application.yml
spring:
application:
name: springcloud-config-client
bootstrap.yml
spring:
cloud:
config:
uri: http://config3344.com:3344 # config server的地址
name: application-dept-client # 远程github的资源名称,不需要yml后缀的
profile: test # 环境配置
label: master # 远程的分支
3.编写测试类
@RestController
public class ConfigClientRest {
@Value("${spring.application.name}")
private String applicationName;
@Value("${eureka.client.service-url.defaultZone}")
private String eurekaServers;
@Value("${server.port}")
private String port;
@RequestMapping("/config")
public String getConfig(){
String str = "ConfigClientRest{" +
"applicationName='" + applicationName + '\'' +
", eurekaServers='" + eurekaServers + '\'' +
", port='" + port + '\'' +
'}';
System.out.println(str);
return str;
}
}
4.启动类
@SpringBootApplication
public class ConfigClientApp3355 {
public static void main(String[] args) {
SpringApplication.run(ConfigClientApp3355.class,args);
}
}
5测试