关于springcloud学习笔记,用于谨记提示自己!
本篇代码(包括片段)基于上个笔记基础上
注:springCloud是一种模式,其组件并非没有替换技术,甚至某些组件对另外组件功能也有不同实现,可以灵活在其中使用
Eurekahttps://github.com/Netflix/eureka
云端服务发现,一个基于 REST 的服务,用于定位服务,以实现云端中间层服务发现和故障转移。
搭建环境:按上一篇中,搭建环境。
Eureka中集群是在每个EurekaServer中将自身服务相互注册即可集群
server: #server相关 port: 8000 #对应相同的3份端口,8000,8001,8002 spring: application: name: eureka-server eureka: client: register-with-eureka: true serviceUrl: defaultZone: http://192.168.8.128:8000/eureka,http://192.168.8.128:8001/eureka,http://192.168.8.128:8002/eureka
其他参数相同即可,搭建3份,效果如下:
以上是将服务注册到一份Eureka中,得到的集群效果,集群自动同步到所有存活集群EurekaServer服务中,可以通过简单配置实现高可用。
EurekaClient启动时,会将自身服务信息发送到EurekaServer。然后进行向EurekaServer拉取注册表中服务信息,保存到EurekaClient中。当服务间相互调用其它服务时,在EurekaClient中获取服务信息(如服务地址,端口等)后,根据信息直接调用服务。(注:服务的调用通过http(s)调用),当某个服务只调用其他服务,自身不提供服务调用时。在EurekaClient启动后会拉取EurekaServer拉取注册表中服务信息,需要调用时,在EurekaClient的本地缓存中获取信息,调用服务。
EurekaClient会向EurekaServer发送心跳(默认每30秒)来续约服务的, 注册信息和续订被复制到集群中的EurekaServe所有节点。 以此来确保当前服务还“存活”,可以被调用。如果客户端持续不能续约,那么它将在一段时间(默认90秒)从服务器注册表中剔除,但是EurekaServer并不是立即剔除服务,而是将其加入剔除清单,默认每60s剔除一次。默认90s是最短剔除时间,可能因为剔除周期,或者自我保护影响。自我保护是,当服务心跳续约异常时,未按约定续约,EurekaServer会统计15分钟内心跳比例是否低于85%,用于判断是否剔除,保证容错。
来自任何区域的EurekaClient都可以查找注册表信息(每30秒发生一次),以此来确保调用“存活”服务。并且当某个服务被更新或者新加进来,也可以调用到新的服务。
EurekaClient拉取EurekaServer细节参考(Eureka的注册表拉取及多级缓存机制简析_LO_YUN的博客-CSDN博客)
服务注册:
#手动配置服务端口 server.port=8888 #配置服务名 spring.application.name=eureka-service #服务注册中心地址 eureka.client.serviceUrl.defaultZone=http://192.168.8.129:8003/eureka
服务续约:
eureka.instance.lease-renewal-interval-in-seconds=30 #默认30s进行一次续约
服务清除:
eureka.instance.lease-expiration-duration-in-seconds=90 #默认续约失败90s进行清除
服务拉取:
eureka.client.registry-fetch-interval-seconds=30 #默认定时拉取周期30s
服务器剔除周期:
eureka.server.eviction-interval-timer-in-ms=1000 #剔除周期,默认60*1000ms
服务自我保护:
eureka.server.enable-self-preservation=true #是否打开自我保护,默认打开
目前服务相互调用可以使用://以下为代码片段,不可直接运行,是从上一篇(SpringCloud学习笔记_hmiaoerdai的博客-CSDN博客)抽取
/*
需要配置注入RestTemplate (spring整合的一个http客户端)
*/
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
/*
注入相关类
*/
@Autowired
RestTemplate restTemplate;
@Autowired
DiscoveryClient discoveryClient;
/*
调用逻辑
*/
//从缓存注册列表信息获取服务信息
List instances = discoveryClient.getInstances("test-service");
//获取该服务信息中的一个节点(服务集群可能会有多个节点)
ServiceInstance serviceInstance = instances.get(0);
//获取该节点的url
URI uri = serviceInstance.getUri();
//通过restTemplate调用特定方法,将获取信息以String输出,本质是通过http(s)协议交互
String user = restTemplate.getForObject(uri+"/test",String.class);
//控制台输出该user
System.out.println(user);
该方法可以进行调用,但是有一定缺陷,并且略微繁琐,比如获取到了的List,可以以一定算法,进行复杂均衡,并且同时进行封装等,使代码高效且简洁可靠,springCloud已经做了这一部分事情,Ribbon(负载均衡)。
提供云端负载均衡,有多种负载均衡策略可供选择,可配合服务发现和断路器使用。
添加依赖
org.springframework.cloud
spring-cloud-starter-netflix-ribbon
简单使用,手动负载均衡:
//注入
@Autowired
RibbonLoadBalancerClient ribbonClient;
//调用缓存注册表信息,区别DiscoveryClient,获取的List,负载均衡后仅获取一个
ServiceInstance choose = ribbonClient.choose("test-service");
System.out.println(restTemplate.getForObject(choose.getUri()+"/test",String.class));
正常使用,注解自动负载均衡:
/*
在配置注入RestTemplate时,添加@LoadBalanced注解
*/
@Bean
@LoadBalanced //该注解后,会通过负载均衡拦截器拦截RestTemplate的HTTP请求
public RestTemplate restTemplate(){
return new RestTemplate();
}
/*
调用服务逻辑,自动负载均衡底层和手动原理相同
*/
//注解自动拦截restTemplate的HTTP请求,并负载均衡
String url = "http://test-service/test";
String userStr = restTemplate.getForObject(url, String.class);
System.out.println(userStr);
结果:
注意:如果开启注解自动均衡的话,默认会通过RibbonLoadBalancerClient去查找注册中心的instances,意味着所有都将会这样做,所以不能使用该restTemplate进行其他http访问,否则IllegalStateException: No instances available for xxx,如果确实需要可以结合@Qualifier(“XxxBean”)进行再装配一个不进行拦截负载均均衡的restTemplate
负载均衡内部提供多种负载均衡算法,并且支持配置负载均衡算法和自定义算法。默认是进行轮询算法。
配置负载均衡算法:
#test-service服务名,RandomRule随机,将test-service负载均衡算法改为随机
test-service.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
配置连接超时时间和请求处理超时时间
ribbon: ConnectTimeout: 5000 #请求连接的超时时间,默认时间为1秒 ReadTimeout: 5000 #请求处理的超时时间
熔断器,容错管理工具,旨在通过熔断机制控制服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。
添加依赖坐标:
org.springframework.cloud
spring-cloud-starter-netflix-ribbon
配置注解:在springboot入口中添加
//添加熔断降级注解 @EnableCircuitBreaker//熔断降级 @EnableDiscoveryClient//客户端注册,兼容多个注册中心 @SpringBootApplication//springBoot //或者添加 @SpringCloudApplication//该注解包含上面3个注解,官方提供的标准springCloud注解
启用熔断降级服务:
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import top.mypapa.demo.mapper.TestTKMapper;
@RestController
@DefaultProperties(defaultFallback = "fallback")//配置默认降级方法
public class TestController {
@Autowired
TestTKMapper testMapper;
@GetMapping("test")
@HystrixCommand//开启熔断器服务
//@HystrixCommand(commandProperties = {
// @HystrixProperty(name =
// "execution.isolation.thread.timeoutInMilliseconds",value = "4000")
//})//配置超时处理属性,从预设1000ms配置为3000ms
//@HystrixCommand(fallbackMethod = "fallback")//开启熔断器服务,并配置单独降级方法,单独的降级方法
public String test() throws Exception {
try {Thread.sleep(3000);}catch (Exception e){e.printStackTrace();}//进行人为超时模拟
return testMapper.selectByPrimaryKey(7).toString();
}
/*
通用fallback,没有参数,返回值统一
单独fallback,必须要参数和返回值与原方法完全一致
因为一般返回交互基本都是json,所以可以进行返回值String,也可以使用专用返回对象,将其json化返回
*/
public String fallback(){
return "抱歉,服务器阵亡了!";
}
}
测试用例:
//测试直接查询,返回正常
User user = testMapper.selectByPrimaryKey(7);
System.out.println(user);
//注解自动负载均衡,由于人为模拟超时,触发降级,返回执行降级方法
String userStr = restTemplate.getForObject("http://test-service/test", String.class);
System.out.println(userStr);
测试结果: 成功触发降级
配置细节在com.netflix.hystrix包下HystrixCommandProperties配置类中,可以参阅。
降级机制:服务超时进行,进行降级处理,进行降级,调用降级方法,结束等待,以保证服务活性
默认超时时长:1000ms
//default_executionTimeoutInMilliseconds可以通配置execution.isolation.thread.timeoutInMilliseconds属性改变
private static final Integer default_executionTimeoutInMilliseconds = 1000; // default => executionTimeoutInMilliseconds: 1000 = 1 second
所以上面人为进行线程3000ms睡眠,会造成超时,触发降级,返回->"抱歉,服务器阵亡了!"
添加注解,并使用 @HystrixProperty进行配置execution.isolation.thread.timeoutInMilliseconds为3000
/*
该注解作用在方法上,@DefaultProperties也可以使用相同方式配置作用域为类的配置
*/
@HystrixCommand(commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
})
效果如下:使用时间3007ms,依旧超时
配置为4000ms,响应成功,没有触发降级,效果如下:
Hystrix全局超时时长配置:
#在application.yml配置文件中添加该配置属性,可更改全局超时时长属性,单位ms hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=3000 #default位置可以配置相同类型作用范围,例如:test-service(服务),test(方法)等同样生效 hystrix.command.[作用范围名].execution.isolation.thread.timeoutInMilliseconds=3000
熔制机制:熔断器有三个状态,关闭,打开,半开,在服务正常进行时,超时仅仅会触发降级,熔断器关闭,如果失败超过阈值,熔断器将会进入打开状态,该状态所有方法直接熔断,返回fallback方法值,5s后进入半开状态,会通过部分请求,测试通过后关闭熔断,否则继续打开,等待下一个观察时间窗循环操作
熔断阈值:默认是失败比例的阈值50%,请求次数不低于20次
休眠时间:默认5s
配置熔断和配置降级相同,只是属性不同:
//可以按照上面降级同样方式进行其他阈,或者application.yml配置
@HystrixCommand(commandProperties = {
//启动熔断最低次数
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "20"),
//配置观察时间窗,单位ms
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "5000"),
//配置失败阈值
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "50")
})
Feignhttps://github.com/OpenFeign/feign
Feign是一种声明式、模板化的HTTP客户端。
feign也是负载均衡处理器,是基于ribbon的,是在 Ribbon的基础上进行了一次改进,是一个使用起来更加方便的 HTTP 客户端。采用接口的方式, 只需要创建一个接口,然后在上面添加注解即可 ,将需要调用的其他服务的方法定义成抽象方法即可, 不需要自己构建http请求。然后就像是调用自身工程的方法调用,而感觉不到是调用远程方法,使得编写 客户端变得非常容易。
添加依赖坐标:
org.springframework.cloud
spring-cloud-starter-openfeign
springBoot入口中配置启用feign注解:
@EnableFeignClients //配置使用feign
创建伪装申明类:(以springMVC注解形式伪装远程调用成申明式)
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
/*
通过springMVC形式伪装成申明式,完成远程调用
*/
@FeignClient("test-service")//服务名称
public interface UserClient {
@GetMapping("test")
String test();
}
注意:由于Feign底层使用Ribbon调用请求,Ribbon的默认超时时间为1s,所以超过1s就报错,需要向上面Ribbon超时配置一样配置较长一点时间
使用Feign可以不用引入Ribbon和Hystrix了,因为Feign是基于Ribbon,所以不用引入Ribbon,并且集成有Hystrix,但是和springCloud组件流程不大相同,可以都实现,但是没必要,Feign流程是先Ribbon,在Hystrix,所以涉及一个Ribbon的超时重试机制,和Hystrix熔断降级机制的相互配合问题,一般是Ribbon 开启Feign内置的Hystrix:默认关闭 配置降级方法:先配置fallback参数 实现声明伪装接口UserClient:UserClientFallback 中实现对应降级逻辑 Hystrix的application.yml方式配置依旧有效,但是java注解配置和Hystrix不同 Zuul 是在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。 新建maven工程,参考上一篇配置环境。 导入依赖:由于需要拉取eurekaServer中的 springBoot入口添加注解: 在application.yml配置中添加:大概作用是从EurekaServer中拉取信息做映射,注释中有配置详情 效果演示:访问网关,将路径映射到test-service服务,并调用成功 zuul服务中的模式流程: 在zuul中默认开启hystrix和ribbon,添加application.yml配置如下: 配置过滤器: 过滤器效果:启用成功 ,并且正常输出拦截信息 做了基本的关于springCloud微服务的学习和分析,分析一下基本访问流程,用户从一个http请求,一般是从某个域名,域名服务器进行解析成ip,从而找到指定代理服务器(以nginx为例),nginx可以负载均衡,反向代理,和存放静态资源,非常有用的3个功能,代理服务器先返回静态资源,用户端解析静态资源,通过静态资源中的逻辑,进行异步交互(例如AJAX),请求再次到达nginx服务器,服务器会进行负载均衡,和反向代理,访问一个良好的zuul,zuul网关是后端逻辑的入口,正式进入后端逻辑,一般网关会进行鉴权,判断是否有该权限,没有拦截登录,有放行等等,然后zuul进行拉取EurekaServer注册表信息,也会负载均衡选择良好的EurekaServer,通过注册表信息进行负载均衡选择良好的服务节点,进行调用业务逻辑,其中可能会触发熔断降级等,因为网关存在该功能,可以用于保证系统生态,在调用业务逻辑中也可能会调用其他服务,也存在负载均衡和熔断器,可以通过Feign进行申明式伪装,Feign也存在负载均衡和熔断器,可以自我选择其一通过拉取注册表信息进行调用其他逻辑,从而完成整体业务逻辑,后返回。该流程实现前后端分离,微服务,部署可以对微服务部署docker,高可用,高并发,低耦合,拓展性强,利于部署维护等等,但是不太利于小业务开发,因为架构庞大,部署开发流程复杂,随然各种工具已经做了很多,并且系统复杂度提高了,整体效率等等也会带来相应负担等等其他的。 仅仅是因为学习过程中的思考,如有问题,敬请指正。 ——2021.12.27 xiaochen
feign.hystrix.enabled=true #开启feign熔断,默认关闭
@FeignClient(value = "test-service",fallback = UserClientFallback.class)//服务名称
import org.springframework.stereotype.Component;
@Component
public class UserClientFallback implements UserClient {
@Override
public String test() {
return "查询失败,熔断触发!";
}
}
Zuulhttps://github.com/Netflix/zuul
网关Zuul:
简单使用:
@EnableZuulProxy
server: #server相关
port: 7000
spring:
application:
name: gateway
#配置客户端,可以从EurekaServer中拉取注册表,进行映射配置
eureka:
client:
serviceUrl:
defaultZone: http://192.168.8.129:8003/eureka
registry-fetch-interval-seconds: 2 #拉取周期
#配置具体映射规则
zuul:
routes:
#配置映射
test:
path: /test/**
serviceId: test-service
#去除匹配前缀,zuul下配置,作用域为全局
strip-prefix: false
#简化配置
test-service: /test-service1/**
#排除映射,自动会将所有EurekaServer中服务名进行路径映射
ignored-services:
- eureka-server
zuul网关细节:
#hystrix超时时长
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=6000
#配置ribbon
ribbon.ConnectTimeout=5000 #请求连接的超时时间,默认时间为1秒
ribbon.ReadTimeout=5000 #请求处理的超时时间
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class TestZuulFilter extends ZuulFilter {
@Override
public String filterType() {//过滤器类型,类型分为前置,后置,路由,错误
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {//过滤级别
return FilterConstants.PRE_DECORATION_FILTER_ORDER-1;
}
@Override
public boolean shouldFilter() {//是否过滤
return true;
}
@Override
public Object run() throws ZuulException {//过滤逻辑
RequestContext ctx = RequestContext.getCurrentContext();//获取上下文
HttpServletRequest request = ctx.getRequest();//获取到请求
System.out.println("拦截成功,打印request:========================================================================================================================"+request);
if(false){
ctx.setResponseStatusCode(403);//通过上下文返回状态码
ctx.setSendZuulResponse(false);//通过上下文返回拦截
}
return null;
}
}