SpringCloud是个啥我就不说了,直接进入正题。
1.Eureka
Eureka作为SpringCloud的注册中心,有着非常重要的作用,所有的服务都需要在注册之后,才能被注册中心发现,从而供其他的服务调用。除了Eureka之外,SpringCloud还支持Consul、Zookeeper作为注册中心。这里简单记录一下开发步骤:
1.创建maven项目
2.new一个module,导入eureka server,这个时候就自动把需要的依赖包下好
3.在application启动类中添加注解 @EnableEurekaServer,声明其为注册中心
4.写yml配置文件,启动即可
关于Eureka的搭建,我之前找到过一篇比较详细的文章,这里就不做赘述,网址如下:
https://blog.csdn.net/zhou199252/article/details/80745151
下面是我在搭建高可用Eureka时的配置,贴出来供参考:
spring:
application:
name: spring-cloud-eureka
profiles: peer1
server:
port: 8761 #服务端口
eureka:
instance:
hostname: peer1 #与此实例相关联的主机名,是其他实例可以用来进行请求的准确名称
client:
# registerWithEureka: false #服务器自己不注册自己: 实例是否在eureka服务器上注册自己的信息以供其他服务发现,默认为true
# fetchRegistry: false #服务器自己不获取自己信息:此客户端是否获取eureka服务器注册表上的注册信息,默认为true
serviceUrl:
defaultZone: http://peer2:8762/eureka/ #再做高可用的时候注册到另一EurekaServer上
#注册中心配置结束
#搭建高可用的eureka集群,搭建高可用集群的时候配置registerWithEureka、fetchRegistry为true,或不配置(默认为true),供其他eureka注册中心发现
---
server:
port: 8762
spring:
application:
name: spring-cloud-eureka
profiles: peer2
eureka:
instance:
hostname: peer2
client:
serviceUrl:
defaultZone: http://peer1:8761/eureka/
2.Ribbon
在springCloud构建的微服务系统中,Ribbon作为负载均衡器。他有两种使用方式,一是和RestTemplate结合,另一种是和Feign相结合
ribbon目前主要的几个子模块:
ribbon-loadbalancer:可独立使用或与其他模块一起使用的负载均衡器API
ribbon-eureka:Ribbon结合Eureka客户端的API,为负载均衡器提供动态服务注册列表信息
ribbon-core:Ribbon的核心API
这里先讲讲ribbon结合RestTemplate,demo如下:(需要在Eureka所需依赖基础上额外引入Spring-cloud-starter-ribbon的依赖包)
/**
* restTemplate结合Ribbon,开启负载均衡功能
*/
@Configuration
public class RibbonConfig {
@Bean
@LoadBalanced
RestTemplate restTemplate(){
return new RestTemplate();
}
}
如上就实现了restTemplate和Ribbon结合,只要在service层注入restTemplate,直接调用服务即可,如下
@Service
public class HelloService {
@Autowired
RestTemplate restTemplate;
/**
* 调取生产者的服务接口
* @param name
* @return
*/
public String hiService(String name)
{
return restTemplate.getForObject("http://SERVICE-HI/hi?name=" + name, String.class); //调取服务名为SERVICE-HI的服务,将返回的json字符串转换成一个String对象
}
}
这里简单说一下Ribbon的原理:
Ribbon通过LoadBalancerClient来实现负载均衡,而LoadBalancerClient具体交给ILoadBalancer来处理,ILoadBalancer通过配置IRule、IPinng等,向EurekaClient获取注册列表的信息,默认10秒钟向EurekaClient发送一次Ping,来检查是否需要更新注册列表信息。最后,ILoadBalancer根据IRule的策略进行负载均衡。
RestTemplate上加@LoadBalanced之后在远程调度的时候可以实现负载均衡,主要是在远程调度方法前加拦截器,把调度方法交由负载均衡器处理。
3.Feign
Ribbon除了结合RestTemplate在消费服务时做负载均衡外,还可以通过Ribbon和Feign相结合的方式来实现。
Feign目标是使Http客户端调用过程变得简单,采用声明式API接口的风格,将Http客户端绑定到它的内部
Feign开发步骤如下:(需要引入Feign的起步依赖:spring-cloud-starter-feign)
1.启动类中添加注解@EnableFeignClients
/**
* Feign启动类
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients //开启FeignClient功能
public class EurekaFeignClientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaFeignClientApplication.class, args);
}
}
2.编写调用类
@Component
@FeignClient(value = "service-hi",configuration = FeignConfig.class) //value是远程调用的其他服务名 FeignConfig.class为FeignClient的配置类
public interface EurekaClientFeign {
@GetMapping(value = "/hi")
String sayHiFromClientEureka(@RequestParam(value = "name")String name);
}
配置类FeignConfig ,如下:
@Configuration
public class FeignConfig {
@Bean //注入Retryer,注入该bean后,Feign在远程调用失败后会进行重试
public Retryer feignRetryer(){
return new Retryer.Default(100,SECONDS.toMillis(1),5);
}
}
3.编写controller,编写service,sevice调用这里的sayHiFromClientEureka方法
4.Hystrix
Hysyrix是为了解决分布式服务中的雪崩效应:
在分布式服务中,业务错综复杂,各个服务相互依赖,如果其中某个服务故障或者挂掉了,会导致所有请求该服务的线程处于阻塞状态,可能会在短时间内造成大量的资源消耗,导致整个系统的不可用,这就是服务的雪崩效应。Hystrix熔断器在单个服务出现问题的时候,采用快速失败机制来防止大量线程阻塞,或者通过预先设计好的回退方案转接到其他方法的方式来防止雪崩。
Hystrix熔断器工作原理:
当服务的某个API接口失败次数在一定时间内小于设定的阈值时,熔断器处于关闭状态,API接口正常提供服务;当失败次数大于阈值时,Hystrix判定接口出现故障,熔断器打开,此时请求该API接口会执行快速失败的逻辑(即fallback回退逻辑)。一段时间后,处于打开状态的熔断器会处于半打开状态,并将一部分的请求执行正常逻辑,如果执行成功了,就打开熔断器,如果失败,就关闭熔断器,这样就实现了熔断器的自我修复功能。
这里分别就在Restemplate和Feign上使用熔断器做一个记录:
4.1.RestTemplate和Ribbon上使用熔断器:
1.导入熔断器起步依赖spring-cloud-starter-hystrix,在启动类添加注解@EnbaleHystrix
2.在要加熔断器的方法上加@HystrixCommand注解,指定回退方法,如下,此时熔断器配置完毕
@Service
public class HelloService {
@Autowired
RestTemplate restTemplate;
/**
* 调取生产者的服务接口
* @param name
* @return
*/
@HystrixCommand(fallbackMethod = "hiError") //熔断器器配置,当无法调用如下方法时,会调用自定义的hiError方法,fallbackMethod即处理回退逻辑的方法
public String hiService(String name)
{
return restTemplate.getForObject("http://SERVICE-HI/hi?name=" + name, String.class); //
}
//回退逻辑不用写的很复杂,方便快速失败,释放线程资源,不建议在回退逻辑里调用其他服务,如果实在要调用,建议再加个熔断器
public String hiError(String name)
{
return "hey " +name + ", there is some problem with hi page";
}
}
4.2在Feign上使用熔断器:
注意:Feign的依赖spring-cloud-starter-feign的pom文件中默认引入了熔断器Hystrix和负载均衡Ribbon的依赖,实现了服务熔断和负载均衡,所以这里不需要再重复导包
1.在application.yml中添加配置,如下:
feign:
hystrix:
enabled: true
2.在feign调用服务的方法前的注解中指定回退处理类,如下:
@Component
@FeignClient(value = "service-hi",configuration = FeignConfig.class,fallback = HiHystrix.class)
//value是远程调用的其他服务名FeignConfig.class为FeignClient的配置类,fallback指定熔断器快速失败的处理类,这个类需要继承这里的EurekaClientFeign接口
public interface EurekaClientFeign {
@GetMapping(value = "/hi")
String sayHiFromClientEureka(@RequestParam(value = "name")String name);
}
3.回退处理类中重写方法,如下:
/**
* 熔断器快速失败处理类,需要继承Feign调用服务的接口类,并把回退逻辑实现在重写的方法里面
*/
@Component
public class HiHystrix implements EurekaClientFeign{
@Override
public String sayHiFromClientEureka(String name) {
return "hi,"+name+",sorry,error!";
}
}
4.3在SpringCluod中可以用Hystrix Dashboard监视熔断器状态
先说一下在restTemplate中使用Hystrix DashBoard步骤:
1.添加起步依赖hystrix、Hystrix DashBoard、Actuator
2.启动类添加@EnableHystrixDashboard注解,即可
<!--熔断器-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.4.RELEASE</version>
</dependency>
<!--熔断器状态检测组件所需依赖Hystrix Dashboard、actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
<version>1.4.5.RELEASE</version>
</dependency>
/**
* Eureka消费者
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix
@EnableHystrixDashboard //开启Hystrix Dashboardg功能,用来对熔断器做状态监控
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
在Feign中使用Hystrix DashBoard步骤:
1.添加起步依赖hystrix、Hystrix DashBoard、Actuator
2.启动类添加@EnableHystrixDashboard注解,即可
步骤和在Restemplate类似,这里不做赘述,注意Feign中添加HystrixDashBoard时需要引入hystrix和Actuator依赖,因为它自带的并不是原生的。
Turbine聚合监视熔断器状态:
Hystrix DashBoard每个服务都有一个监控主页,主页不是很方便,服务多了不利于监控,netflix开源了另一个组件Turbine,用于聚合监控多个Hystrix DashBoard,将多个Hystrix DashBoard组件的数据放在同一个页面上监控。有兴趣的同学可以去了解下,碍于篇幅这里就不记录了。
5.Zuul
Zuul作为微服务的网关组件,用于构建边界服务(Edge Service),致力于动态路由、过滤、监控、弹性伸缩和安全
Zuul网关的几个主要作用:
1.对外统一暴露API接口,调用者不需要知道内部服务之间是怎么调用的,避免敏感信息被外界获取
2.与Ribbon、Eureka结合可实现负载均衡和智能路由功能
3.可做流量监控,必要时进行服务降级,提供请求监控功能,实时日志输出,对请求进行记录
4.可用来做身份认证和权限认证,防止非法请求操作API接口,对服务器起到保护作用
Zuul的核心是一系列的过滤器,可在Http请求的发起和响应返回期间执行过滤器,主要包含以下四种:
PRE过滤器:请求路由到具体服务前执行,可用来做安全验证
ROUTING过滤器:用于将请求路由到具体的微服务实例
POST过滤器:用于在请求已被路由到微服务之后执行,可用来做信息收集统计等
ERROR过滤器:在其他过滤器发生错误时执行
5.1简单记录一下Zuul的开发步骤:
1.导依赖zuul、eureka、springboot-web、springboot-test
2.启动类添加注解@EnbaleZuulProxy
3.写配置文件,如下
spring:
application:
name: service-zuul
server:
port: 5000
eureka:
client:
service-url:
defaultZone: http://peer1:8761/eureka/
zuul:
routes:
hiapi: #制定好path和serviceId,所有以path开头的请求都会被路由到对应的服务。如下面/hiapi/**被路由到service-hi对应的服务,/ribbonapi/**被路由到service-ribbon对应的服务
path: /hiapi/**
serviceId: service-hi
ribbonapi:
path: /ribbonapi/**
serviceId: service-ribbon
prefix: /v1 #配置版本号,至此url请求地址变成:http://localhost:5000/v1/ribbonapi/**
至此,一个简单的Zuul demo就配好了,可以试着访问http://localhost:5000/v1/hiapi/****,会调用ID为service-hi的服务对应接口。
5.2.Zuul上配置熔断器:
zuul上配置熔断器需要继承ZuulFallbackProvider接口,这个接口里有两个方法,一个是getRoute(),用于指定熔断器应用于哪些服务,还有一个fallbackResponse()方法,用于做进入熔断功能时执行的逻辑
@Component
class MyFallbackProvider implements ZuulFallbackProvider {
@Override
public String getRoute() {
return "service-ribbon";
}
如上表示熔断器应用于id为service-ribbon的服务,如果需要应用于所有服务,可以改成return "*";
@Override
public ClientHttpResponse fallbackResponse() {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return 200;
}
@Override
public String getStatusText() throws IOException {
return "OK";
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("oooops!errpr,i'm the fallback.".getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
return httpHeaders;
}
};
}
}
5.3.Zuul配置过滤器:
需要继承ZuulFilter接口, 具体代码如下:
/**
* zuul配置过滤器
*/
@Component
public class Myfitler extends ZuulFilter {
@Override
public String filterType() { //设置过滤类型:pre、post、routing、error
return PRE_TYPE;
}
@Override
public int filterOrder() { //设置过滤顺序,值越小越早执行
return 0;
}
@Override
public boolean shouldFilter() { //是否过滤逻辑,如果为true则执行run()方法,如果false,则不执行run()方法
return true;
}
@Override
public Object run() throws ZuulException { //具体的过滤逻辑,这里检查请求参数中是否包含token参数,如果没有,就直接返回响应,不路由到具体服务,状态码401
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
Object accessToken = request.getParameter("token");
if(accessToken == null){
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
try {
ctx.getResponse().getWriter().write("token is empty");
}catch (Exception e){
return null;
}
}
return null;
}
}
6.Config
Config是微服务的配置中心,可以从本地仓库读取配置文件,也可以从远处git仓库读取。
6.1从本地读取配置步骤:
1创建maven工程,指定spring-boot版本为1.5.3,spring-cloud版本为Dalston.RELEASE,新建module,添加起步依赖spring-cloud-config-server
2在启动类上添加@EnableConfigServer注解,开启configServer功能
3配置application.yml
spring:
cloud:
config:
server:
native:
search-locations: classpath:/shared #指定本地读取的配置的路径:Resources目录下的shared文件夹
profiles:
active: native
application:
name: config-server
server:
port: 8769
4Resources目录下创建shared文件夹,在文件夹中创建一个名为config-client-dev.yml的文件,用来填写配置信息,如下:
server:
port: 8769
fool: fool version 1
5构建configClient,编辑配置文件bootstrao.yml( bootstrao.yml比 application.yml有优先的执行顺序),读取之前配置文件中的内容
spring:
cloud:
config:
uri: http://localhost:8769
fail-fast: true
profiles:
active: dev
application:
name: config-client
#spring-application-name和spring-profiles-active两者以“-”相连,构成了向ConfigServer读取的配置名,比如这里读取config-client-dev.yml
6.2.从远程git仓库读取配置
相比本地的好处是可以统一管理配置,并通过消息总线在不人工启动的情况下对配置进行刷新
其他步骤与本地一样,application.yml配置与配置在本地略有出入,如下:
spring:
cloud:
config:
server:
git:
url: https://github.com/forezp/SpringcloudConfig
searchPaths: respo #搜索远程仓库的文件夹地址
username: aaa #git用户名
password: bbb #git密码
label: master #指定从哪个分支读取
application:
name: config-server
server:
port: 8769
6.3.构建高可用configServer
当服务实例很多时,所有服务都要从配置中心读取配置,这个时候就要考虑将configServer做成一个微服务,并将其集群化,从而达到高可用。主要做法是将configServer和ConfigClient注册到注册中心,并多实例部署configServer,实现负载均衡。步骤如下:
1.创建eureka注册中心
2.注册configServer,在原有配置的基础上往application中添加以下配置
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
3.注册configClient,修改application.yml文件
spring:
cloud:
config:
fail-fast: true
discovery:
endabled: true
serviceId: config-server #向服务名为config-server的配置服务读取配置文件
profiles:
active: dev
application:
name: config-client
4.开启多个configServer,实现高可用负载均衡
6.4.使用消息总线Spring Cloud Bus刷新配置
当配置中间开了很多个服务之后,更新配置需要开启多个服务,非常麻烦。通过消息总线就可以解决这个麻烦,配置了消息总线之后,只需要向其中一个服务发送“bus/refresh"Post请求,通过消息组件就可以通知其他的服务也去git仓库里读取更新配置。
可选取RabbitMQ、AMQP和Kafka作为消息总线。以RabbitMQ为例,步骤如下(安装rabbitmq服务器步骤这里不做阐述):
1.pom引入rabbitMQ依赖
2.application.yml中配置rabbitmq
spring:
rabbitmq:
host:localhost
port:5672
username:guest
password:guest
management:
security:
enabled:false #暂时先屏蔽安全验证
3.启动类添加@RefreshScope注解。
至此就完成了消息总线的配置,通过发送http://localhost:8762/bus/refresh,就可以通知所有服务去重新读取配置了
7.SpringCloudSleuth
在微服务中,内部各个服务间调用关系经常是非常复杂的,一旦出现了错误和异常,将很难去定位是哪里发生的,这个时候就需要用到分布式链路追踪,去跟进一个请求到底有哪些服务参与,参与的顺序是怎样的,从而去快速定位问题。
目前常见的追踪组件有Goolge的Dapper、Twitter的Zipkin,阿里的Eagleeye。这里介绍Zipkin
使用步骤如下:
1.编写ZipkinServer
1.1、添加zipkin-server依赖,添加zipkin的UI界面依赖
1.2、启动类上添加@EnbaleZipkinServer注解,开启ZipkinServer功能
1.3、编写配置文件,将此服务注册到注册中心
spring:
application:
name: zipkin-server
server:
port: 9411
eureka:
client:
service-url:
defaultZone: http://peer1:8761/eureka/
2.编写UserService提供服务
步骤和编写普通的eurekaClient基本一致,只是多引入zipkin的包,同时在配置文件中加入zipkin的配置,如下:
spring:
application:
name: user-service
zipkin:
base-url: http://localhost:9411/ #指定zipkinServer地址
sleuth:
sampler:
percentage: 1.0 #以100%的概率将链路数据上传给ZipkinServer,默认为0.1
server:
port: 8762
eureka:
client:
service-url:
defaultZone: http://peer1:8761/eureka/
配置完之后再编写一个对外的api接口供调用。
3.编写Gateway Service
按常规步骤编写一个服务,引入eureka、zuul和zipkin,编写配置文件,如下
spring:
application:
name: gateway-service
zipkin:
base-url: http://localhost:9411/ #指定zipkinServer地址
sleuth:
sampler:
percentage: 1.0 #以100%的概率将链路数据上传给ZipkinServer,默认为0.1
server:
port: 5000
eureka:
client:
service-url:
defaultZone: http://peer1:8761/eureka/
zuul:
routes:
api-a:
path: /user-api/**
serviceId: user-service
之后在启动类上添加eureka和zuul的启动注解。
浏览器通过网关访问user-service提供的接口,再访问http://localhost:9411/,即可打开zipkin的展示页面,查看此次的链路数据。应该可以看到gateway-service服务调用user-service服务的链路。
顺道提一下:
链路数据默认通过Http上传个zipkin-server,可通过消息组件rabbitMQ访问。
链路数据默认存储在内存中,但极易丢数据,可存在mysql、ElasticSearch、Cassandra上。有兴趣可以去了解下。
先写到这里吧…关于监控组件及安全组件,每个的篇幅都比较长,后面有时间专门重新写篇文章记录下。