SpringCloud入门搭建及服务调用
开发工具:idea 2020.2.3
java:1.8
maven:3.3.9
SpringBoot:2.1.3.RELEASE
SpringCloud:Greenwich.SR5 (版本和SpringBoot必须对应,对应表自行百度)
idea配置我就不细说了
然后next
finish
然后配置pom.xml:
一般在父项目配置
然后配置依赖,这里只是一个springboot项目,所以配一个springboot就行了:
然后配置pom.xml,它们俩都是独立的springboot项目了,这里也可以和父类工程做依赖继承,但是这里就没必要,更凸显服务的独立性:
controller:
power启动类:
UserController.java
在这里调用另一个服务power服务的接口,可以使用reatTeplate来实现,需要配置
reaTeplate配置文件:AppConfig.java
user服务启动类:
user服务的 application.yml
同样可以以相同方式在power服务中调用user的接口
然后在浏览器:
127.0.0.1:6060/power/getPower.do(6060是power服务的端口)
127.0.0.1:7070/user/getUser.do调用自己的接口(7070是user服务的端口)
127.0.0.1:7070/user/getPower.do调用power的接口
至此实现了不同服务之间的调用
spring cloud服务之间怎么调用
比如有一个服务如下
@EnableEurekaClient
@SpringBootApplication
@RestController
public class EurekaClientApplication {
@Value("${server.port}")
String port;
@RequestMapping("/hi")
public String home(@RequestParam String name) {
return "hi "+name+",i am from port:" +port;
}
public static void main(String[] args) {
SpringApplication.run(EurekaClientApplication.class, args);
}
}
这个服务名为:EurekaClient
----------------------------------------------------------------------------------------------
调用采用以下方式:
定义一个借口,注解@FeignClient(value = "EUREKACLIENT")
@Service
@FeignClient(value = "EUREKACLIENT")//服务名
public interface SchedualServiceHi {
@RequestMapping(value = "/hi",method = RequestMethod.GET)
String sayHiFromClientOne(@RequestParam(value = "name") String name);
}
@RestController
public class HiController {
@Autowired
SchedualServiceHi schedualServiceHi;
@RequestMapping(value = "/hi",method = RequestMethod.GET)
public String sayHi(@RequestParam String name){
return schedualServiceHi.sayHiFromClientOne(name);
}
}
springcloud执行流程
1.Servlet
zuul.servletPath默认配置为/zuul,故请求为/zuul开头的会跳过dispatcherServlet直接进入ZuulServlet,该配置可以自定义配置,例如用于大文件上传
2.ZuulServlet中service方法
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
try {
this.init((HttpServletRequest)servletRequest, (HttpServletResponse)servletResponse);
RequestContext context = RequestContext.getCurrentContext();
context.setZuulEngineRan();
try {
//运行pre过滤器
this.preRoute();
} catch (ZuulException var12) {
//有异常,执行errorFilter
this.error(var12);
//再执行postFilter
this.postRoute();
return;
}
try {
//运行rote过滤器
this.route();
} catch (ZuulException var13) {
//有异常,执行errorFilter
this.error(var13);
//再执行postFilter
this.postRoute();
return;
}
try {
//运行post过滤器
this.postRoute();
} catch (ZuulException var11) {
//有异常,执行errorFilter
this.error(var11);
}
} catch (Throwable var14) {
this.error(new ZuulException(var14, 500, "UNHANDLED_EXCEPTION_" + var14.getClass().getName()));
} finally {
RequestContext.getCurrentContext().unset();
}
}
3.FilterProcessor
其运行交由FilterProcessor中的方法runFilters,根据service中的顺序,取不同的filter类型,执行其中的run方法
public Object runFilters(String sType) throws Throwable {
if (RequestContext.getCurrentContext().debugRouting()) {
Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
}
boolean bResult = false;
ListZuulFilter list = FilterLoader.getInstance().getFiltersByType(sType);
if (list != null) {
for(int i = 0; i list.size(); ++i) {
ZuulFilter zuulFilter = (ZuulFilter)list.get(i);
Object result = this.processZuulFilter(zuulFilter);//见下面zuulFilter的runFilter()
if (result != null result instanceof Boolean) {
bResult |= ((Boolean)result).booleanValue();
}
}
}
return bResult;
}
zuulFilter的runFilter方法,当filter的shouldFilter()返回true时才执行run()方法
public ZuulFilterResult runFilter() {
ZuulFilterResult zr = new ZuulFilterResult();
if (!this.isFilterDisabled()) {
if (this.shouldFilter()) {
Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName());
try {
Object res = this.run();
zr = new ZuulFilterResult(res, ExecutionStatus.SUCCESS);
} catch (Throwable var7) {
t.setName("ZUUL::" + this.getClass().getSimpleName() + " failed");
zr = new ZuulFilterResult(ExecutionStatus.FAILED);
zr.setException(var7);
} finally {
t.stopAndLog();
}
} else {
zr = new ZuulFilterResult(ExecutionStatus.SKIPPED);
}
}
return zr;
}
4.获取过滤器FilterRegistry
其中的属性private final ConcurrentHashMapString, ZuulFilter filters = new ConcurrentHashMap();
保存所有的过滤器
例子中有12个(其中有两个为自定义的):
[org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter@3dc68586,
org.springframework.cloud.netflix.zuul.filters.pre.Servlet30WrapperFilter@4001d8c1,
org.springframework.cl
SpringCloud远程调用-OpenFeign
OpenFeign是SpringCloud提供的一个声明式客户端组件,可以通过注解和接口的组合实现服务的远程接口调用,并且与Eureka和Ribbon组合使用能够实现负载均衡的效果
1.在pom.xml中引入依赖
2.在主启动类上添加 @EnableFeignClients 注解,开启Feign支持
3.创建feignClient接口,并添加注解(指定需要调用的服务和接口)
4.在Controller中注入后,发起远程调用
测试:访问
OpenFeign远程调用的默认等待时长为1秒,超时后报错
1.在消费者服务的application.yml文件中设置超时时间
2.在服务提供者端的接口处设置睡眠时间
3.在服务消费者端打印时间
重启后访问: ,发现等待一段时间后,页面获取到端口值,访问成功,说明时间控制生效
控制台打印信息如下:
OpenFeign提供了日志打印功能,能够对feign借口的调用情况进行监控和输出
Fegin的日志级别:
2.在yml中指定显示哪一个接口的信息
访问接口,可以看到请求你的详细信息
Spring Cloud调用接口过程
Feign -----Hystrix —Ribbon —Http Client(apache http components 或者 Okhttp) 具体交互流程上
Hystrix 是一个供分布式系统使用,提供 延迟 和 容错 功能,保证复杂的分布系统在面临不可避免的失败时,仍能有其弹性。
比如系统中有很多服务,当某些服务不稳定的时候,使用这些服务的用户线程将会阻塞,如果没有隔离机制,系统随时就有可能会挂掉,从而带来很大的风险。SpringCloud使用 Hystrix组件提供断路器、资源隔离与自我修复功能 。下图表示服务B触发了断路器,阻止了级联失败
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统中,许多依赖不可避免的会调用失败,超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,提高分布式系统的弹性
熔断机制是应对雪崩效应的一种微服务链路保户机制,当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的相应信息。当检测当该节点微服务调用响应正常后恢复调用链路,熔断机制的注解是@HystrixCommand
“熔断器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控,,某个异常条件被触发,直接熔断整个服务。,向调用方法返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出吊牌用方法无法处理的异常,就保证了服务调用方的线程不会被长时间占用,避免故障在分布式系统中蔓延,乃至雪崩。
服务降级处理是在客户端实现完成的,与服务端没有关系
整体资源快不够了,忍痛将某些服务单元先关掉,关闭后还要返回一些可处理的备选方法,待渡过难关,再开启回来。
分布式项目中,有数十个依赖关系,每个依赖关系在某些时候不可避免地失败,
服务雪崩 :当A调用微服务B,B调C,和其他微服务,这是扇出,当扇出链路上某个微服务调用响应时间过长或者不可用,对微服务的A的调用就会占用越来越多的系统资源,导致系统崩溃,所谓的雪崩效应
服务熔断 :一般是某个服务异常引起的,相当于“保险丝”,当某个异常条件被触发,直接熔断整个服务,不是等到此服务超时
服务降级 :降级一般是从整体负荷考虑,当某个服务熔断之后,服务器将不再被调用,客户端可自己准备一个本地的fallback回调,返回一个缺省值,虽然服务水平下降,当能用,比直接挂掉要强
springcloud是spring,采用AOP的思想,异常处理信息,我们某个服务的功能是每个方法,我们还可以使用AOP直接在api层通过接口设置服务降级。