Springcloud(demo)
目录
Eureka服务注册与发现
eureka是做什么的
为什么要使用eureka
服务治理
服务注册
服务发现
搭建Eureka注册中心服务
1.搭建父项目
2.搭建eureka单机
3.搭建Eureka集群
4.服务提供者
5.自我保护模式
Feign服务调用
服务消费者
Ribbon负载均衡
IRule的几个重要实现类
Hystrix熔断器/断路器
目标
服务雪崩
服务降级
服务熔断
使用Hystrix
Config配置中心
zuul网关(路由)
案例
路由规则
添加路由规则
Zuul网关(过滤)
Eureka服务注册与发现
eureka是做什么的
eureka主要负责完成微服务架构中的服务治理功能
为什么要使用eureka
当服务的模块越来越多,系统的功能越来越复杂,以前用到的静态配置就会变得越来越难以维护,会消耗巨大的人力,所以通过使用服务注册与发现来完成对微服务应用的自动化管理。
服务治理
用来实现各个微服务实例化的自动化注册与发现。
服务注册
首先会构建一个服务中心,之后的每个服务单元向这个服务中心提供主机,端口号等信息登记自己提供的服务。而注册中心通过服务名分类组织服务清单,并对其进行维护。服务注册中心需要以心跳的方式去监测清单中的服务是否可用,若不可用需要从服务清单中剔除。
服务发现
服务之间不需要指定具体的实例地址,而是通过服务中心发起请求调用实现。所以,调用方并不需要知道服务提供方具体的位置,只需向注册中心发起请求,从而获取所有服务的实例清单,才能实现对具体服务实例的访问。
实际应用中,不会每次都向注册中心获取服务,使用了缓存和服务剔除等不同的策略。
搭建Eureka注册中心服务
1.搭建父项目
首先创建一个新的maven项目,默认next即可

项目名为cloud-parent,将生成的src目录删除,这个项目作为一父项目,用作集成公共的依赖即可。 springcloud是依托于springboot,所以必须用到springboot,两者的关联版本为
Release Train Boot version
Hoxton 2.2.x
Greenwich 2.1.x
Finchley 2.0.x
Edgware 1.5.x
Dalston 1.5.x
cloud版本使用Honxton.SR7,如下,对应2.3.2的springboot;
4.0.0 org.example cloud-parent pom 1.0-SNAPSHOT cloud-server-eureka cloud-client-product cloud-client-order 8 8 UTF-8 Hoxton.SR7 2.3.2.RELEASE org.springframework.boot spring-boot-dependencies ${spring.boot-version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud-version} pom import org.springframework.boot spring-boot-maven-plugin
maven仓库:https://mvnrepository.com/
这是一个简单的父项目,springboot与cloud以及build打包方式都已经配置好了。
2.搭建eureka单机
1.创建maven项目
配置eureka注册中心,右键cloud-parent,新建module名为cloud-server-eureka的Maven项目
最终项目结构如下

2.pom
cloud-parent org.example 1.0-SNAPSHOT 4.0.0 cloud-server-eureka org.springframework.cloud spring-cloud-starter-netflix-eureka-server
3.application.yml
server:port: 8866eureka:instance:prefer-ip-address: true #以IP地址注册到服务中心client:register-with-eureka: false #是否将自己注册到Eureka Serverfetch-registry: false #是否从Eureka Server获取注册信息 #Eureka Server地址service-url: #Eureka Server地址defaultZone: http://127.0.0.1:8866/eureka/spring:application:name: server-eureka
主要是通过如下配置将该微服务定位为Eureka注册中心
register-with-eureka: true #是否将自己注册到Eureka Server fetch-registry: false #是否从Eureka Server获取注册信息 #Eureka Server地址
4.启动类:
package com.cse;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;//标记为服务端
@EnableEurekaServer
//标记自己为启动类
@SpringBootApplication
public class EurekaApplication {public static void main(String[] args) {SpringApplication.run(EurekaApplication.class);}
}
写好启动类,然后不用手动再配置如下图,直接启动后会自动配置好下图,如果需要另外配置jvm参数的另行配置即可;

访问Eureka地址: http://127.0.0.1:8866/,如图,启动成功

3.搭建Eureka集群
1.首先修改hosts文件,C:\Windows\System32\drivers\etc
添加如下内容,用于在本地模拟不同域名
127.0.0.1 www.eureka-node1.com
127.0.0.1 www.eureka-node2.com
127.0.0.1 www.eureka-node3.com
2.配置文件
注册中心是集群,需要相互注册,无论访问哪一个注册中心,都能看到其余的注册中心

其余代码不用改,只需要继续创建另外两个Module即可,修改端口以及对应的注册中心网址即可;
注册中心1号配置:
server:port: 8861eureka:client:service-url: #Eureka Server地址defaultZone: http://www.eureka-node2.com:8862/eureka/,http://www.eureka-node3.com:8863/eureka/instance:hostname: www.eureka-node1.comspring:application:name: server-eureka
注册中心2号配置:
server:port: 8862eureka:client:service-url: #Eureka Server地址defaultZone: http://www.eureka-node1.com:8862/eureka/,http://www.eureka-node3.com:8863/eureka/,instance:hostname: www.eureka-node2.comspring:application:name: server-eureka
注册中心3号配置:
server:port: 8863eureka:client:service-url: #Eureka Server地址defaultZone: http://www.eureka-node1.com:8861/eureka/,http://www.eureka-node3.com:8862/eureka/,instance:hostname: www.eureka-node3.comspring:application:name: server-eureka
4.服务提供者
最终项目结构如下:

注意:此处注册中心使用的是上个内容的单点模块
1.创建maven项目
在父项目cloud-parent的基础上,创建一个maven子项目cloud-client-product,引入如下依赖
org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.boot spring-boot-starter-web
如上引入了eureka客户端,意思是该微服务是eureka的客户端,启动后需要将服务地址注册到注册中心上,这是相对于eureka来说的;
web用来支持spring注解;
2.配置yml文件
server:port: 8081spring:application:name: client-producteureka:client:serviceUrl: defaultZone: http://127.0.0.1:8866/eureka
#这里只写了一个注册中心网址,如果是集群,用逗号隔开可继续加
配置了eureka为了将服务注册到eureka上;
3.主程序
package com.ccp;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;//@EnableEurekaClient新版本不需要加了,可省略
@SpringBootApplication
public class ProductApplication {public static void main(String[] args) {SpringApplication.run(ProductApplication.class);}
}
4.controller数据接口
package com.ccp.controller;import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class ProductController {@GetMapping("getProductById")public String getProductById(Integer productId){System.out.println("服务提供者》根据ID查询商品:"+productId);return "这是你要的商品"+productId;}
}
启动cloud-server-eureka注册中心,再启动cloud-client-product服务提供者
访问注册中心,可在页面中发现服务已经注册上来
当启动多个项目时,idea会提示如下

点击show,方便查看各个微服务项目;
如图,服务已经注册上去了

访问http://localhost:8081/getProductById?productId=2正常访问
5.自我保护模式
这时候如果将product服务停一会后,发现注册中心进入了自我保护模式

翻译下
紧急情况!尤里卡可能错误地声称,当实例未启动时,它们已启动。续订小于阈值,因此实例不会为了安全而过期。
原因:eureka的客户端,也就是这里的cloud-server-product会每个30秒发送一次心跳告诉eureka服务端(注册中心)还活着,eureka服务端接收eureka客户端心跳,持续90秒没有收到心跳,就会从注册列表中剔除掉该微服务,但是上图client-product服务已经停了却依旧在列表中,这是因为触发了eureka另外一个机制,也就是自我保护模式;
本地单机测试是最容易进入该模块的;
自我保护模式:在15分钟内,心跳失败率低于85%就会触发,eureka会保留该服务,保证服务的有效性;因为服务也许因为网络延迟等未知原因并没有挂掉,只是心跳发送的不那么正常,这时候不能直接就剔除掉,否则其余调用该服务的就调用不到了;
Feign服务调用
声明一个代理接口,服务调用者通过调用这个代理接口的方式来调用远程服务。
另外Feign整合了Ribbon和Hystrix,可以让我们不再需要显式地使用这两个组件。
服务消费者
最终项目结构如下:

1.创建maven项目
在父项目cloud-parent的基础上,创建一个maven子项目cloud-client-order,引入如下依赖
org.springframework.cloud spring-cloud-starter-openfeign org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.boot spring-boot-starter-web
2.yml
server:port: 8082spring:application:name: client-ordereureka:client:serviceUrl: defaultZone: http://127.0.0.1:8866/eureka/
3.启动类
@EnableFeignClients启用Feign组件
package com.cco;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;@EnableFeignClients
@SpringBootApplication
public class OrderApplication {public static void main(String[] args) {SpringApplication.run(OrderApplication.class);}
}
4.调用远程服务的Feign客户端接口
使用@FeignClient注册来指定这个接口所要调用的服务名,接口中定义的各个函数使用SpringMVC的注解就可以来绑定服务提供方的接口,并进行参数的传递,参数传递时@RequestParam必不可少;
package com.cco.remote;import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;@FeignClient(name = "client-product")
public interface ProductRemote {@GetMapping("/getProductById")String getProductById(@RequestParam("productId") Integer productId);
}
这里需要调用client-product服务,使用feign,相当于搭了个桥,client-product这个名字来自如下yml配置,方法名也要与client-produc的一致;

5.controller接口调用Feign
package com.cco.controller;import com.cco.remote.ProductRemote;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@RestController
public class OrderController {@Resourceprivate ProductRemote productRemote;@GetMapping("/getOrderDetail")public Object getOrderDetail(){String product = productRemote.getProductById(666);System.out.println("服务消费者》获取到远程服务提供的商品信息");return product;}
}
启动order项目,访问
http://127.0.0.1:8082/getOrderDetail

成功调到了product服务的接口;
6.注册中心宕机
如果把product服务关闭,进入了保护模式,那么order是否还能调用到呢?
当然不能,
order调的不是eureka的注册中心的服务,而是通过先到注册中心找到product地址再通过feign调product服务,eureka保存的仅仅只是一个url地址列表而已。
如果eureka注册中心突然挂了,其余服务还会正常使用吗?
当然能
因为order已经拿到调用地址了,就不会走注册中心了,那么之后注册中心即使挂了也不会影响调用product服务;如果还没拿到就挂了,肯定无法调用到product服务。
Ribbon负载均衡
Ribbon已经被Feign整合,引用了Feign组件的项目,可以直接使用Ribbon
案例:重复启动同一个服务提供者(修改端口即可),然后进行远程调用,即可看到服务提供者在被依次的调用(默认的轮询策略--RoundRobinRule),一般BestAvailableRule用的比较多一些;
IRule的几个重要实现类
- com.netflix.loadbalancer.RoundRobinRule : 轮询
- com.netflix.loadbalancer.RandomRule: 随机
- com.netflix.loadbalancer .RetryRule: 先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务
- WeightedResponseTimeRule:对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择
- BestAvailableRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
- AvailabilityFilteringRule:先过滤掉故障实例,再选择并发较小的实例
- ZoneAvoidanceRule:默认规则,复合判断server所在区域的性能和server的可用性选择服务器
上方order导入的openfeign已经整合了ribbon,可以直接启几个不同端口的product服务访问看后台是负载均衡的;
修改默认的负载均衡规则,主要有两种方式,需要对服务消费者进行改动:
方式1.yml配置中添加如下内容
client-product:ribbon:NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
写法规则为:
服务名:
ribbon:
NFLoadBalancerRuleClassName: 规则类名路径
方式2.添加一个配置文件,往Spring容器中注入指定的IRule实现类,这里用RandomRule规则;
package com.cco.config;import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RibbonConfig {@Beanpublic IRule randomRule(){return new RandomRule();}
}
Hystrix熔断器/断路器
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时,异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,一条高分布式系统的弹性。
“断路器”本身是一种开关装置,当某个服务单元发生故障后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个服务预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会长时间、不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩;
目标
微服务架构是高度分布式的,必须防止单个服务(或服务实例)中的问题级联暴露给服务消费者
服务雪崩
一个服务失败导致整条链路的服务都失败的情形称为服务雪崩
在分布式系统中,如果服务调用层数很多,那么其中一个环节如果除了错,整个服务都会抛出异常,这就是雪崩效应;A为服务提供者, B为A的服务调用者, C和D是B的服务调用者. 当A的不可用,引起B的不可用,并将不可用逐渐放大C和D时, 服务雪崩就形成了;

服务降级
- 当下游服务因某种原因响应过慢,下游服务主动停掉一些不太重要的业务,释放出服务器资源,增加响应速度!
- 当下游服务因某种原因不可用,上游主动调用本地的一些降级逻辑,避免卡顿,迅速返回给用户!
服务熔断
- 当下游服务因某种原因突然变得不可用或响应过慢,上游服务为保证自己整体服务的可用性,不再继续调用目标服务,直接返回,快速释放资源,如果目标服务情况好转则恢复调用
使用Hystrix
Hystrix已经被Feign整合,引用了Feign组件的项目,可以直接使用Hystrix,因为熔断只是作用在服务调用这一端,所以使用Hystrix只需要在服务消费端进行处理。
1.在yml文件启用Hystrix
feign:hystrix:enabled: true
2.创建回调类,实现Feign调用接口
package com.cco.rollback;import com.cco.remote.ProductRemote;
import org.springframework.stereotype.Component;@Component
public class ProductRemoteFallBack implements ProductRemote {@Overridepublic String getProductById(Integer productId) {System.out.println("进入熔断处理......ProductRemoteFallBack");System.out.println("熔断器Hystrix接收的参数productId:"+productId);return "商品未获取到";}
}
3.Feign客户端调用处添加fallback属性,指向回调类
package com.cco.remote;import com.cco.rollback.ProductRemoteFallBack;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;@FeignClient(name = "client-product",fallback = ProductRemoteFallBack.class)
public interface ProductRemote {@GetMapping("/getProductById")String getProductById(@RequestParam("productId") Integer productId);
}
在product服务的方法里加个异常代码,比如:int i = 1/0;
启动服务,访问:http://localhost:8082/getOrderDetail

异常进行了熔断处理。
Config配置中心
待定
zuul网关(路由)
在微服务框架中,每个对外服务都是独立部署的,对外的api或者服务地址都不是不尽相同的。对于内部而言,很简单,通过注册中心自动感知即可。但我们大部分情况下,服务都是提供给外部系统进行调用的,不可能同享一个注册中心。同时一般上内部的微服务都是在内网的,和外界是不连通的。而且,就算我们每个微服务对外开放,对于调用者而言,调用不同的服务的地址或者参数也是不尽相同的,这样就会造成消费者客户端的复杂性,所以根据请求的url不同,路由到不同的服务上去,同时入口统一了,还能进行统一的身份鉴权、日志记录、分流等操作。
案例
最终项目结构如下

1.创建项目
在父项目cloud-parent的基础上,创建一个maven子项目cloud-gateway-zuul,引入如下依赖
org.springframework.cloud spring-cloud-starter-netflix-zuul org.springframework.cloud spring-cloud-starter-netflix-eureka-client
2.yml配置
spring:application:name: gateway-zuulserver:port: 6677eureka:client:service-url: defaultZone: http://127.0.0.1:8866/eureka/
3.启动类
添加@EnableZuulProxy注解启用Zuul
package com.cgz;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;@EnableZuulProxy
@SpringBootApplication
public class ZuulApplication {public static void main(String[] args) {SpringApplication.run(ZuulApplication.class);}
}
测试
启动全部微服务,按如下路径访问网关,可以看到成功的将请求路由到了指定的服务接口
http://127.0.0.1:6677/client-product/getProductById?productId=666

路由规则
springcloud zuul在整合了Eureka之后,具备默认的服务器功能,当zuul项目启动并注册到Eureka之后,服务网关zuul发现Eureka上面注册的服务,这时候Zuul就会创建处对应的路由规则。
如:转发到 client-product 服务的请求规则为: /client-product/**
转发到 client-order 服务的请求规则为: /client-order/**
添加路由规则
在cloud-gateway-zuul网关项目的yml配置中添加以下内容
zuul:routes:client-product: /product/**client-order: /order/**
添加后的效果即为:访问网关的请求路径中如果含有product,则路由到client-product的服务下
http://127.0.0.1:6677/product/getProductById?productId=666

Zuul网关(过滤)
zuul大部分功能都是通过过滤器来实现的,zuul定义了4种标准的过滤器类型,
- pre:可以在请求被路由之前调用
- routing: 路由请求时被调用
- post:在routing和error过滤器之后被调用
- error:处理请求时发生错误时被调用
除了默认的过滤器类型,zuul还允许创建自定义的过滤器类型。
zuul请求的生命周期如下,描述了各种类型过滤器的执行顺序

product服务加个方法,在地址中加个view
@GetMapping("view/getProductById2")public String getProductById2(Integer productId){System.out.println("服务提供者》根据ID查询商品:"+productId);return "这是你要的商品"+productId;}
zuul服务里创建过滤器,用来拦截含view的服务地址
package com.cgz.filter;import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;@Component
public class TokenFilter extends ZuulFilter {/**** 过滤的类型* 返回pre为前置过滤,表示会在请求被路由之前执行*/@Overridepublic String filterType() {return FilterConstants.PRE_TYPE;}/*** 过滤器执行顺序* 数字越小,优先级越高* @return*/@Overridepublic int filterOrder() {return 0;}/*** 该过滤器是否需要被执行* true-执行* false-不执行* @return*/@Overridepublic boolean shouldFilter() {RequestContext ctx = RequestContext.getCurrentContext();HttpServletRequest request = ctx.getRequest();//获取请求路径String url = request.getRequestURL().toString();System.out.println("用户请求地址:"+url);//路径中包含"/view/",则执行该过滤器return url.contains("/view/");}/*** 过滤器具体处理* @return*/@Overridepublic Object run() throws ZuulException {System.out.println("ZuulFilter--服务网关过滤器开始处理------");RequestContext ctx = RequestContext.getCurrentContext();HttpServletRequest request = ctx.getRequest();String accessToken = request.getHeader("token");if (StringUtils.isEmpty(accessToken)) {ctx.setSendZuulResponse(false); //过滤该请求,不对其进行路由ctx.setResponseStatusCode(401); //响应状态码ctx.getResponse().setContentType("text/html;charset=utf-8"); //响应内容编码ctx.setResponseBody("401!访问拒绝,请先登录"); //响应内容}return null;//官网这里最后返回null}
}
启动微服务,
访问http://127.0.0.1:6677/product/getProductById?productId=666
不含view,可以正常访问
访问http://127.0.0.1:6677/product/view/getProductById2?productId=666

如图,请求被网关中的过滤器拦截到了
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

