springcloud

springcloud

微服务概述

微服务:微服务是一种架构风格,一个大型复杂软件应用由一个或者多个微服务组成,系统中各个微服务可被独立部署,是松耦合的;每个微服务仅专注于完成一件任务,每个任务就代表着一个小的业务能力,它代表了更细粒度的组件;

微服务架构的一些特性:

  • 通过服务实现应用的组件化,每个组件可被独立替换和升级;
  • 围绕业务能力组织服务;
  • 产品而非项目模式,即一个团队负责一个微服务的完整生命周期,倡导谁开发谁运营的方式
  • 智能端点与管道扁平化,即主张将通讯相关业务放在组件端点;
  • 去中心化,即每个微服务可以考虑使用最佳的工具来完成任务;
  • 故障处理设计,即需要考虑每个服务的失败容错机制;

微服务架构存在的缺点:

  • 运维及开发成本增加;
  • 组件间接口匹配会带来一些额外的问题;
  • 系统复杂,需要考虑分布式架构带来的额外的问题;

springcloud是一个轻量级的微服务集群框架,具有服务治理、负载均衡、熔断器、接口组件、分布式配置、网关组件等功能。

注册中心

eureka

github地址:https://github.com/Netflix/eureka/releases

github wiki:https://github.com/Netflix/eureka/wiki

是spring netflix的二次封装。在eureka中,Eureka Server(服务端)也就是注册中心,管理所有在注册中心注册了服务名称的Eureka Client(客户端);客户端可以是提供者、消费者、外部调用者等;它们都需要在服务端注册,提交节点信息,也都可以调用服务;同时Eureka Server也是一个客户端,也可以在服务端中注册,这点是Eureka集群的基础。

单机

搭建:

  • 依赖(版本选择尽量与springboot一致)
<!--springboot1.x使用-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka-server</artifactId>
    <version>1.4.7.RELEASE</version>
</dependency>
<!--springboot2.x使用-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>
  • 配置
server.port=8080
# 服务名
spring.application.name=eureka-server
eureka.instance.hostname=localhost
# 配置注册地址的
eureka.client.serviceUrl.defaultZone=http://localhost:8080/eureka

# 单机环境可以设置下面两个参数
# 是否注册自己,默认true
eureka.client.registerWithEureka=false
# 是否检索服务,默认true
eureka.client.fetchRegistry=false
  • 编写启动类,在启动类上添加@EnableEurekaServer注解。
  • 启动,访问http://localhost:8080/ 即可看到注册中心页面。
高可用

原理:服务端向注册中心注册自己,并每30s一次心跳检测,若注册中心90s还未收到心跳检测,则认为该服务宕机,接着在60s以后对这些客户端集中注销;另外Eureka还有自我保护机制,即15分钟内超过85%的节点没有正常心跳,则注册中心会认为自身出现网络故障,不再接收心跳,也不删除服务;

  • 搭建,在上面搭建的环境基础上,修改配置如下(启动多个时,只需要复制多份,然后修改端口号即可):
server.port=8080
spring.application.name=eureka-server
eureka.instance.hostname=localhost
# 配置注册地址,将所有的注册地址都配置上
eureka.client.serviceUrl.defaultZone=http://localhost:8079/eureka,http://localhost:8080/eureka
  • 访问http://localhost:8080/可以看到,两个注册中心都成功注册了,同时,关闭其中一个,仍然可以正常工作;
客户端
  • 依赖(使用时,要选择与上面相同的版本)
<!--springboot1.x使用-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!--springboot2.x使用-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
  • 配置
server.port=8001
# 服务名
spring.application.name=provider-hello
# 是否注册到eureka,默认true;false代表只进行服务的调用
# eureka.client.registerWithEureka=false
# 是否检索服务,默认true
# eureka.client.fetchRegistry=false
# eureka.instance.hostname=localhost
eureka.client.serviceUrl.defaultZone=http://localhost:8080/eureka/
  • 启动类添加注解:@EnableEurekaClient

zookeeper

使用zookeeper注册中心得先安装和启动zookeeper,具体参见zookeeper笔记;以zookeeper为注册中心的客户端搭建方法如下:

  • 依赖(版本选择尽量贴近springboot的版本,否则可能会出现版本问题)
<!--使用zookeeper注册中心-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!--这里使用与启动的zookeeper相同的版本-->
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <!--解决与springboot项目的日志冲突问题-->
    <exclusions>
        <exclusion>
            <artifactId>slf4j-log4j12</artifactId>
            <groupId>org.slf4j</groupId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-commons</artifactId>
</dependency>
  • 配置
# 客户端端口号
server.port=8000
# 服务别名--注册到zookeeper的名称
spring.application.name=provider-hello
# 是否注册到zookeeper,只有调用操作的客户端可以不用注册
# spring.cloud.zookeeper.discovery.register=false
spring.cloud.zookeeper.connect-string=127.0.0.1:2181
  • 在启动类上添加@EnableDiscoveryClient注解;

提供者

  • 搭建注册中心客户端环境,配置合适的服务别名
  • 编写controller,例如:
@RestController
public class HelloService {

    @Autowired
    private HelloDao helloDao;

    @RequestMapping("/hello")
    public String hello(HttpServletRequest req){
        return "端口为:" + req.getServerPort() + "的service模块";
    }

    @RequestMapping("/json")
    public UserInfo json(Long id){
        UserInfo userInfo = helloDao.findOne(id);
        return userInfo;
    }

    @RequestMapping("/list")
    public List<UserInfo> list(@RequestBody UserInfo userInfo){
        return helloDao.findAll(Example.of(userInfo));
    }

}
  • 启动客户端,即可在注册中心看到有服务注册了。

eureka报警说明:当提供服务的client断开连接,程序关闭,宕机等问题,导致服务治理中心无法发现或连接通信服务提供者时,可连接的服务提供者占用所有服务提供者的85%(默认值)以下时,可以在eureka的web页面看到红色报警信息,并且拒绝剔除服务。

消费者

Ribbon

消费者可以实现通过springcloud内部服务调用,从eureka注册中心获取服务列表,完成负载均衡的访问(默认是轮询方式);RestTemplate是由SpringBootWeb组件提供的,可以实现对服务提供者的调用,其实使用方式是:

  • 通过@Bean来创建RestTemplate并交给SpringBoot容器管理;
  • 通过RestTemplate对象的getForXxx方法来调用方法,其url参数可以直接填参数,也可以填服务名(推荐服务名,且服务名方式需要在@Bean的同时加@LoadBalance注解,表示开启负载均衡的能力,即ribbon);

搭建方式如下:

  • 搭建注册中心客户端环境,配置合适的服务别名;
  • 加入ribbon依赖(版本选择要与注册中心相对应)
<!--springboot1.x使用-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<!--springboot2.x使用-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
  • 配置Bean:
@Bean
// Ribbon负载均衡:轮询策略(默认)
@LoadBalanced
public RestTemplate getRestTemplate(){
    return new RestTemplate();
}
  • 接着就可以在业务代码中注入RestTemplate类了,使用示例如下:
@Autowired
private RestTemplate template;
@RequestMapping("/")
public String test01(){
    // 域名就是提供者在注册中心注册的服务名
    return template.getForObject("http://provider-hello/hello", String.class);
}

Hystrix(熔断器)

熔断器,容错管理工具,当服务集群中出现访问服务的异常,或者错误,或者http请求的断开,可以利用熔断的机制完成各种方法的封装,调用方法,实现错误的管理逻辑。其主要有一下几个特点:

  • 包裹请求:使用HystrixCommand包裹对依赖的调用,每个命令都在独立的线程中执行;
  • 跳闸机制:当某个服务的错误率超过一定阈值,则跳闸,一段时间内停止对该服务的请求;
  • 资源隔离:为每个依赖维护一个线程池,若已满则拒绝发往该依赖的请求;
  • 监控:监控运行指标和配置的变化;
  • 回退机制:当请求失败、超时时,会执行回退逻辑,可以自定义回退逻辑;
  • 自我修复:自动打开、关闭、半开;

使用Hystrix,只需要在Ribbon项目的基础上做如下操作:

  • 添加依赖(版本尽量与注册中心相对应):
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
  • 启动类添加**@EnableHystrix**注解;
  • 修改service代码:
@Autowired
private RestTemplate template;
// hystrix默认超时时间是1秒(指浏览器延时),超时则会执行服务降级方法并返回,原服务方法则只执行而不返回,可以手动禁用
@HystrixCommand(fallbackMethod = "failHello")
public String test01(){
    return template.getForObject("http://provider-hello/hello", String.class);
}
//请求超时、失败的回退方法
public String failHello(){
    return "<h1>sorry!</h1>";
}

Feign(服务调用)

负载均衡调用服务的客户端组件,声明式(注解)客户端。使得调用更简单(无需关心底层调用的api,如:getForEntity、getForObject、post…等);底层基于ribbon+restTemplate(内部整合了容错的断容器Hystrix)。

使用方法如下:

  • 搭建注册中心客户端环境;
  • 添加依赖(版本要与注册中心相对应):
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  • 在启动类上加**@EnableFeignClients**注解;
  • 编写Service接口(无需写实现类):
// 这个注解里边是可以定义服务降级处理方法的
@FeignClient(value = "provider-hello")
public interface HelloService {

    @GetMapping("/hello")
    String hello();

    @GetMapping("/json")
    UserInfo json(@RequestParam("id") Long id);

    @GetMapping("/list")
    List<UserInfo> list(@RequestBody UserInfo userInfo);

}
  • 接着就可以在业务代码中注入这个service进行使用了;

网关

zuul

概念及特点

zuul内部整合了ribbon,用于监听微服务组件,访问的时候根据url路径,过滤网关拦截的信息,访问指向的服务,网关可以对请求进行权限控制、负载均衡、日志管理、接口调用监控等。其在功能上类似于nginx,但并不完全相同。

zuul与nginx:

  • zuul采用ribbon+eureka实现,底层使用了servlet,而nginx采用服务器实现;
  • nginx更强大,更适合服务器端负载均衡,缓解请求压力,zuul更适用于在微服务中使用,归类业务;
  • nginx在微服务中的使用特别麻烦。
使用
  • 依赖,如果路由规则要使用服务名,则需要先搭建注册中心客户端环境(版本要与注册中心相对应)
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
  • 配置application.properties文件
# 下面的api、api-a、api-b只是个名字,可以任意写
# 客户端请求路径
zuul.routes.api.path=/**
# zuul.routes.api-b.url=http://localhost:8081/
# 服务别名, 需要配合注册中心使用
zuul.routes.api.serviceId=consumer-hello

# 如果有多个web,则可以配置多个这个,例如:
# zuul.routes.api-a.path=/api-a/**
# zuul.routes.api-a.serviceId=consumer1-hello

# zuul.routes.api-b.path=/api-b/**
# zuul.routes.api-b.serviceId=consumer2-hello
  • 在启动类上加上@EnableZuulProxy注解
过滤器

过滤器方法说明:

  • filterType:过滤器类型,决定在请求的那个生命周期中执行;
    • pre:路由之前执行,可用于身份验证,请求选择,记录调试等;
    • routing:路由时执行,用于构建发送给服务的请求;
    • post:路由之后或error之后执行,可用于加工响应信息、收集信息、统计等;
    • error:请求发生错误时执行;
    • static:直接返回静态值,不转发;
  • filterOrder:过滤器执行顺序,越小越先;
  • shouldFilter:过滤器是否需要执行;
  • run:过滤器执行逻辑。

过滤器示例:

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;
import java.io.IOException;

@Component
public class MyFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        String a = request.getParameter("a");
        if(StringUtils.isBlank(a)){
            System.out.println("=============zuul拦截了=============");
            context.setSendZuulResponse(false);
            context.setResponseStatusCode(401);
            try {
                context.getResponse().setContentType("text/html;charset=UTF-8");
                context.getResponse().getWriter().write("缺少参数:a");
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
        System.out.println("===============zuul放行了================");
        return null;
    }
}

gateway

Spring Cloud Gateway作为Spring Cloud生态系中的网关,目标是替代ZUUL,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/埋点,熔断,重试,和限流等。

与zuul相比,gateway更高效,内置了许多功能。

几个重要概念:

  • 路由(Route):由id、uri、断言和过滤器组成,若断言为真则路由匹配;
  • 断言(Predicate):输入类型时ServerWebExchange,用来匹配http请求;
  • 过滤器(Filter):分为网关路由和全局路由。
使用

使用步骤(如果在路由中要使用服务名,则需要在搭建好注册中心的基础上操作):

  1. 依赖(gatway需要配合springboot2.x使用):
<dependency>
    <groupId>org.springframework.cloud </groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
  1. 配置application.properties:
# 是否结合注册中心使用
spring.cloud.gateway.discovery.locator.enabled=true

# 配置路由
spring.cloud.gateway.routes[0].id=route0
# 这里是lb://服务名/或者http://域名/
spring.cloud.gateway.routes[0].uri=lb://consumer-hello/
spring.cloud.gateway.routes[0].predicates[0]=Path=/**
# 配置局部过滤器
spring.cloud.gateway.routes[0].filters[0]=MyFilterFactory

# 可以配置多个路由
#spring.cloud.gateway.routes[1].id=route1
#spring.cloud.gateway.routes[1].uri=lb://consumer1-hello/
#spring.cloud.gateway.routes[1].predicates[0]=Path=/api-a/**
#spring.cloud.gateway.routes[2].id=route2
#spring.cloud.gateway.routes[2].uri=lb://consumer2-hello/
#spring.cloud.gateway.routes[2].predicates[0]=Path=/api-b/**
  1. 启动即可访问。
过滤器
  • 全局过滤器:编写类实现GlobalFilterOrdered接口即可,全局有效,不需要额外配置;
  • 局部过滤器:只是针对配置了的路由有效,也可配置为全局的。
    • 编写自定义的过滤器类实现GatewayFilterOrdered
    • 编写过滤器工厂类继承AbstractGatewayFilterFactory<Object>,并在实现方法中返回自定义的过滤器
    • 在application.properties文件中配置自定义的工厂类。

过滤器示例代码

@Component
public class MyLocalFilter implements GatewayFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 这里相当于pre过滤器
        System.out.println("=========过滤器start==========");
        MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();
        for (Map.Entry<String, List<String>> entry : params.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
            if(entry.getValue() != null && !entry.getValue().isEmpty() && !entry.getValue().get(0).equals("")){
                System.out.println("=========这个请求被拦截了==========");
                ServerHttpResponse response = exchange.getResponse();
                response.getHeaders().add("Content-Type", "text/html; charset=UTF-8");
                return response.writeWith(Mono.just(response.bufferFactory().wrap("被拦截了".getBytes())));
            }
        }
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            // 这里相当于post过滤器
            ServerHttpResponse res = exchange.getResponse();
            // 要获取返回值还挺麻烦的,先不看了
            System.out.println("===========过滤器end=============");
        }));
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

认证授权思路

思路一:

  • 将权限应用(如shiro)拆分为服务端和客户端,服务端单独部署,实现认证和授权,客户端以jar包的形式在具体应用中引入,且客户端只使用授权功能;

思路二:

  • 单独部署一台权限应用,在网关编写过滤器,在过滤器中调用权限应用提供的接口判断是否拥有权限;

思路三:

  • 对于zuul网关的情况下,由于zuul使用servlet,而shiro使用filter,可以直接在zuul中配置shiro,然后在zuul的zuulFilter中编写登录页和登录接口。

分布式配置

概念

在微服务中,使用同一个服务器管理所有服务配置文件信息,实现后台可管理和实时加载配置文件信息的功能。

分布式配置中心框架:

  • 阿波罗:由携程开发,有可管理配置文件信息的图形界面;
  • SpringCloudConfig:配置文件信息存放在版本控制器的,没有后台管理。
  • 也可以使用zookeeper实现分布式配置。

SpringCloudConfig

搭建config服务端:

  • 引入依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-server</artifactId>
</dependency>
  • 添加bootstrap.properties配置文件
server.port=8888
spring.application.name=config-server
# 使用本地文件配置需要配置这个
spring.profiles.active=native
# 本地文件地址(也可以是:file:E:/),
# 访问配置文件的地址为:http://ip:port/文件名/环境/分支
# 或者:http://ip:port/分支/文件全名,分支即目录名

spring.cloud.config.server.native.search-locations=classpath:/config/

# spring.cloud.config.server.native.git.uri=http://localhost/
  • bootstrap.properties配置的指定目录下存放项目需要的配置文件
  • 编写启动类并添加@EnableConfigServer注解

搭建config客户端(需要在原来项目的基础上做以下操作):

  • 依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
</dependency>
  • 添加配置文件bootstrap.properties
# 使用配置springConfig需要的配置文件
# 配置中心的具体地址,即 config-server
spring.cloud.config.uri=http://localhost:8888
# 对应配置文件中的文件名的部分,如:文件名为:aaa-dev.yml,则此处为:aaa
spring.cloud.config.name=config
# 对应配置文件的文件名的环境名部分,如:文件名为:aaa-dev.yml,则此处为:dev
spring.cloud.config.profile=dev
# 对应 {label} 部分,即 Git 的分支(默认是master)。如果配置中心使用的是本地存储,则是目录名
spring.cloud.config.label=cfg2
spring.cloud.config.fail-fast=true
  • 现在就可以在客户端中使用config服务端的配置了;

Apollo

简介(阿波罗的官方文档还是很齐全的,可以直接参考)

阿波罗是携程框架部门研发的分布式配置中心,能够集中化管理配置,配置修改及时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景;

github地址:https://github.com/ctripcorp/apollo

github文档:https://github.com/ctripcorp/apollo/wiki

服务雪崩

概念:默认情况下,tomcat只有一个线程池去处理客户端发送的所有服务请求,因此,在高并发的情况下,如果客户请求堆积到同一个服务接口上,就会产生所有线程去处理该服务接口,从而导致其他服务接口无法访问,或者延迟等待的情况。

基于Hystrix解决服务雪崩效应:

  • 服务降级:即暂时不去处理请求,而是调用fallBack方法,返回一个友好的提示,提升用户体验。
  • 服务熔断:当请求达到一定阈值,则自动使用服务降级开启保护功能,熔断和降级是一起使用的。
  • 服务隔离:
    • 线程池隔离:每个服务接口拥有自己独立的线程池,每个线程池互不影响,缺点:CPU占用率高。这种方法只适用于核心关键接口。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值