Spring Cloud深入浅出

小知识点

  1. application.yml中,"—"分割多个配置断,有spring.profiles=xxx的,表示要spring.profiles.active=xxx才能激活,没有spring.profiles的代码段是默认生效的配置
  2. @SpringCloudApplication是@SpringBootApplication、@EnableDiscoveryClient、@EnableCircuitBreaker组成的
  3. yml配置:和value之间必须要有个空格,name后面必须要有“:”

注册中心Eureka

原理:

  • 服务注册表,提供服务名称、IP、端口的映射关系,即查询API
  • 服务管理,即服务管理API,注册服务和注销服务
  • 服务检查,即服务提供者主动发送心跳,注册中心定时检查注册服务的组件,超时都主动剔除
    Spring Cloud深入浅出_第1张图片

实现:

  1. Eureka
  2. Consul
  3. Zookeeper

Eureka:

  1. Eureka节点间相互复制来实现注册表数据的同步
  2. Eureka客户端会缓存注册表数据,即可以减少和注册中心的请求流量,又可以在注册中心宕机之后也能运行

Eureka服务搭建:

1、依赖

    org.springframework.cloud
    spring-cloud-starter-eureka-server

2、添加注解
@EnableEurekaServer
3、application.properties配置
server.port=1111
eureka.instance.hostname=localhost
eureka.client.register-with-eureka=false  //是否将自己注册到eureka,由于自己已经是eureka负责,所以false
eureka.client.fetch-registry=false //是否从eureka获取注册信息,由于是单点服务,所以为false
//eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/ //注册到其他Eureka的地址

Eureka客户端搭建

1、依赖

    org.springframework.cloud
    spring-cloud-starter-eureka

2、添加注解
@EnableDiscoveryClient //服务注册的通用注解,可以注册Eureka、Consul、Zookeeper,spring-cloud-commons提供
@EnableEurekaClient //当确定使用Eureka时,可以替代@EnableDiscoveryClient,spring-cloud-netflix提供
3、application.properties配置
spring.application.name=hello-service  //注册到eureka的服务名
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
eureka.instance.prefer-ip-addresss=true //是否将自己的ip注册到eureka,默认未false表示注册hostname

Eureka高可用

1、注册中心配置application.properties
eureka.client.serviceUrl.defaultZone=http://ip:port/eureka/  //把自己注册到其他Eureka
2、注册客户端配置
eureka.client.serviceUrl.defaultZone=http://ip1:port/eureka/,http://ip2:port/eureka/

Eureka添加用户认证

1、服务端依赖

    org.springframework.cloud
    spring-cloud-starter-security

2、服务端配置
security.basic.enabled=true
security.user.name=tuyou
security.user.password=123456
3、客户端配置
eureka.client.serviceUrl.defaultZone=http://tuyou:123456@localhost:1111/eureka/

Eureka元数据

配置

eureka.instance.metadata-map.mykey=myvalue //自定义元数据

客户端获取配置
@Autowired
private DiscoveryClient client;

client.getInstances()

Eureka的Rest接口

eureka提供了一些接口,方便其他非jvm的服务接入进来,并且eureka的client也是调用的这些接口
Spring Cloud深入浅出_第2张图片

Eureka其他配置

//禁用自我保护模式,当eureka认为出现网络分区的时候,会启动自我保护,也就是不会删除已注册的微服务
eureka.server.enable-self-preservation=false 

//客户端启用健康检查,如果想实现更细腻度的健康检查,可以实现检查接口
eureka.client.healthcheck=true

客户端负载均衡Ribbon

Spring Cloud深入浅出_第3张图片

集成Ribbon

1、maven依赖

    org.springframework.cloud
    spring-cloud-starter-ribbon

2、代码修改
@EnableDiscoveryClient   //必须加上
@SpringBootApplication
public class RibbonApplication {

    @Bean
    @LoadBalanced //ribbon负载均衡
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    public static void main(String[] args) {

        SpringApplication.run(RibbonApplication.class, args);
    }
}
3、获取ribbon路由信息
@Autowired
LoadBalancerClient loadBalancerClient;

@RequestMapping(value = "/ribbon-consumer", method = RequestMethod.GET)
public String helloConsumer() {
    ServiceInstance instance = loadBalancerClient.choose("HELLO-SERVICE");
    logger.info("serviceId:{},host:{},port:{}", instance.getServiceId(), instance.getHost(), instance.getPort());
    return restTemplate.getForEntity("http://HELLO-SERVICE/hello", String.class).getBody();
}

自定义Ribbon配置

ribbon的默认配置类是RibbonClientConfiguration
Spring Cloud深入浅出_第4张图片

Ribbon脱离Eureka使用

前提:SpringCloud Netflix版本要高于1.2.0

  1. 去除Eureka依赖,去除@EnableDiscoveryClient注解,继续保留@LoadBalanced注解
  2. 加上配置
HELLO-SERVICE.ribbon.listOfServers=localhost:8081,localhost:8082

Feign声明式调用

引入Feign

1、添加依赖

    org.springframework.cloud
    spring-cloud-starter-feign

2、创建一个接口,并添加@FeignClient注解
//name对应要调用服务的spring.application.name
//如果FeignClient不想使用Eureka,可以直接指定ribbon.listOfServers
//或者指定url,@FeignClient(name="服务名称", url="http://localhost:8080/")
@FeignClient(name = "hello-service")
public interface HelloService {
    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    String index();
}

3、启动FeignClient

@EnableFeignClients
//如果要使用Eureka,则要启用注册发现客户端
@EnableDiscoveryClient

自定义配置

  • feign的默认配置是FeignClientsConfiguration
  • 可以用@FeignClient自定义feign配置
演示使用feign默认契约
@Configuration
public class FeignConfiguration {

    @Bean
    public Contract modifyDefaultFeignContract(){
        return new feign.Contract.Default();
    }
}


@FeignClient(name = "hello-service", configuration = FeignConfiguration.class)
public interface DefaultFeignService {

    @RequestLine("GET /hello")
    String index();
}

手动创建Feign

@Import(FeignClientsConfiguration.class)  //必须加上
@RestController
@RequestMapping("/custom")
public class CustomFeignController {

    HelloService helloService;

    @Autowired
    public CustomFeignController(Client client, Decoder decoder, Encoder encoder, Contract contract) {
        helloService = Feign.builder().client(client)
                .decoder(decoder)
                .encoder(encoder)
                .contract(contract)
                .target(HelloService.class, "http://hello-service/");
    }

    @RequestMapping(value = "/feign-consumer", method = RequestMethod.GET)
    public String helloConsumer() throws InterruptedException {
        return helloService.index();
    }
}

Feign支持继承

这样就可以服务端把controller接口封装到interface里面,然后controller和FeignClient都继承这个interface,达到服务端和客户端共享接口的目的

共享的接口
public interface CommonInterface {
    @RequestMapping("/hello")
    String hello(@RequestParam String name, @RequestParam Integer age);
    //@RequestParam在controller可以不写,但是在feign中要写
}
controller实现
@RestController
public class HelloController implements CommonInterface {
    @Autowired
    private DiscoveryClient client;

    public String hello(String name, Integer age) {
        ServiceInstance instance = client.getLocalServiceInstance();
        return "/hello, service_id:" + instance.getServiceId() + ", host:" + instance.getHost() + ", port:" + instance.getPort();
    }
}
feign客户端实现
@FeignClient(name = "hello-service") //name对应要调用服务的spring.application.name
public interface CommonService extends CommonInterface {
    
}

Feign支持压缩

feign.compression.request.enable=true
feign.compression.response.enable=true
feign.compression.request.mime-types=text/html,application/xml,application/json
feign.compression.request.min-request-size=1

Feign修改日志打印等级

@Bean
public Logger.Level loggerLevel() {
    return Logger.Level.FULL;
}

Spring Cloud深入浅出_第5张图片
由于feign打印的日志是debug等级,修改日志打印等级为debug


  
     %-4relative [%thread] %-5level %logger{35} - %msg %n
  



  

Hystrix熔断、限流

  • 当一段时间内,请求失败率达到一定阈值,熔断器将打开
  • 打开后的熔断是半开状态,也就是依旧允许一个请求访问服务,达到检测服务是否恢复的目的,如果已恢复就自动关闭熔断

Hystirx配置属性

Spring Cloud深入浅出_第6张图片

隔离策略

  1. 线程隔离(默认):HystrixCommand将在单独的线程上执行,并受到线程池终的线程数量限制,适用于并发不是非常大,网络IO的情况
  2. 信号量隔离:HystrixCommand将在调用线程上执行,开销较小,并发量受信号量个数限制

feign和hystrix

feign默认已经集成了hystrix,只要发现hystrix在classpath中,就会使用断路器包裹feign客户端方法,只需在@FeignClient上指定fallback属性即可指定回退方法

@FeignClient(value = "HELLO-SERVICE", fallback = HelloFeignFallbackService.class)
public interface HelloFeignService {

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    String hello();

    @RequestMapping(value = "/param", method = RequestMethod.GET)
    String param(@RequestParam("name") String name, @RequestParam("age") Integer age) throws InterruptedException;
}


@Component
public class HelloFeignFallbackService implements HelloFeignService {
    @Override
    public String hello() {
        return "hello默认回退返回";
    }

    @Override
    public String param(String name, Integer age) throws InterruptedException {
        return "param默认回退返回";
    }
}
feign.hystrix.enabled=false //禁用hystrix

Zuul服务网关

接入zuul

1、依赖

    org.springframework.cloud
    spring-cloud-starter-zuul

2、添加注解
@EnableZuulProxy
@SpringCloudApplication
public class ZuulApplication {

    public static void main(String[] args) {

        new SpringApplicationBuilder(ZuulApplication.class).web(true).run(args);
    }

//    @Bean
//    public TestFilter createFilter() {
//        return new TestFilter();
//    }
}
3、配置
spring.application.name=api-gateway
server.port=5555

zuul.routes.hello.path=/hello/**
zuul.routes.hello.url=http://localhost:8080/hello/

zuul.routes.feign.path=/feign-consumer
zuul.routes.feign.service-id=feign-consumer

zuul.routes.ribbon.path=/ribbon-consumer
zuul.routes.ribbon.service-id=ribbon-consumer


eureka.client.serviceUrl.defaultZone=http://localhost:1110/eureka/

详细配置

/routes 端点返回配置的路由信息
zuul.ignored-services=service1,service2
zuul.ignored-services='*' //禁用所有服务
zuul.ignored-patterns=**/admin/** //全局忽略代理指定的路径
zuul.prefix=/api
zuul.strip-prefix=true //全局指定是否切除前缀

zuul.routes.hello.path=/hello/**
zuul.routes.hello.url=http://localhost:8080/hello/
zuul.routes.hello.strip-prefix=false //局部指定是否切除前缀


logging.level.com.netflix=debug //修改zuul的日志等级,方便调试

//header配置
//敏感header,即只会在服务内部传输,不会外泄
zuul.sensitive-headers=Cookie,Set-Cookie,Authorization //全局设置敏感header,这三个也是默认值
zuul.service-id.sensitive-headers=header1,header2 //局部设置
//忽略header
zuul.ignored-headers=header1,header2 //默认为空,如果有security则为Cache-Control,Expires
zuul.ignored-secutiry-headers=false //关闭security禁用的header

zuul大文件上传

使用/zuul前缀上传大文件,并增加超时时间

上传接收服务配置
spring:
  application:
    name: hello-service
  http:
    multipart:
      max-file-size: 2000mb //默认1mb
      max-request-size: 2000mb //默认10mb
zuul配置
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 6000
ribbon:
  ConnectionTime: 3000
  ReadTimeout: 60000

Zuul过滤器

类型 说明
pre 这种过滤器在请求被路由之前调用
routing 这种过滤器将请求路由到微服务
post 这种过滤器在请求路由到微服务之后执行
error 在其他阶段发生错误执行过滤器
继承ZuulFilter
public class TestFilter extends ZuulFilter {

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

    @Override
    public Object run() {
        RequestContext requestContext = RequestContext.getCurrentContext();
//        Map> requestQueryParams = requestContext.getRequestQueryParams();
//        System.out.println(requestQueryParams);

        HttpServletRequest request = requestContext.getRequest();

//        System.out.println(request.getQueryString());
        Map parameterMap = request.getParameterMap();
        System.out.println(parameterMap);
//        try {
//            ServletInputStream inputStream = request.getInputStream();
//            BufferedReader bufferedInputStream = new BufferedReader(new InputStreamReader(inputStream));
//            String line = null;
//            System.out.println("参数:");
//            while ((line = bufferedInputStream.readLine()) != null) {
//                System.out.println(line);
//            }
//        } catch (IOException e) {
//            e.printStackTrace();
//        }


        return null;
    }

    @Override
    public String filterType() {
        return "pre";
    }

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

禁用过滤器

默认的过滤器:spring-cloud-netflix-core.jar包的org.springframework.cloud.netflix.zuul.filters包下面

zuul...disable=true

Zuul回退

@Component
public class HelloFallback implements ZuulFallbackProvider {
    @Override
    public String getRoute() {
        return "hello-service";
    }

    @Override
    public ClientHttpResponse fallbackResponse() {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return this.getStatusCode().value();
            }

            @Override
            public String getStatusText() throws IOException {
                return this.getStatusCode().getReasonPhrase();
            }

            @Override
            public void close() {

            }

            @Override
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream("微服务不可能用".getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                MediaType mediaType = new MediaType("application", "json", Charset.forName("utf-8"));
                headers.setContentType(mediaType);
                return headers;
            }
        };
    }
}

Zuul高可用

  1. 注册到Eureka(仅供内部使用时可行)
  2. 使用负载均衡代理,比如nginx、haproxy、f5,这是这种场景更多,由于供外部调用无法使用eureka
  3. 使用sidecar整合非jvm服务

Sleuth链路跟踪

Sleuth术语

  • 一条请求链路中包含一个TraceID,多个SpanID,由16位16进制组成
  • span export:表示是否将此条记录发送到zipkin等用来收集和展示
  • annotation(标注):用来标注请求的开始和结束
    1. CS:客户端发送请求,span的开始
    2. SR:服务端收到请求
    3. SS:服务端发送请求
    4. CR:客户端收到请求,span的结束

Sleuth接入

1、maven依赖

    org.springframework.cloud
    spring-cloud-starter-sleuth

2、修改日志打印级别
logging:
  level:
    root: INFO
    org.springframework.web.servlet.DispatchServlet: DEBUG
    org.springframework.cloud.sleuth: DEBUG

Sleuth整合ELK

1、maven依赖

    net.logstash.logback
    logstash-logback-encoder
    4.6

2、logback日志打印配置


    

    
    
    

    
    
    
        
            
            DEBUG
        
        
            ${CONSOLE_LOG_PATTERN}
            utf8
        
    
    
    
        ${LOG_FILE}
        
            ${LOG_FILE}.%d{yyyy-MM-dd}.gz
            7
        
        
            ${CONSOLE_LOG_PATTERN}
            utf8
        
    

    
    
        ${LOG_FILE}.json
        
            ${LOG_FILE}.json.%d{yyyy-MM-dd}.gz
            7
        
        
            
                
                    UTC
                
                
                    
                        {
                        "severity": "%level",
                        "service": "${springAppName:-}",
                        "trace": "%X{X-B3-TraceId:-}",
                        "span": "%X{X-B3-SpanId:-}",
                        "parent": "%X{X-B3-ParentSpanId:-}",
                        "exportable": "%X{X-Span-Export:-}",
                        "pid": "${PID:-}",
                        "thread": "%thread",
                        "class": "%logger{40}",
                        "rest": "%message"
                        }
                    
                
            
        
    

    
        
        
        
    

3、启动logstash进程
logstash -f logstash.conf

logstash.conf配置

input {
  file {
    codec => json
    path => "/Users/mac/Downloads/*.json"
  }
}
filter {
  grok {
    match => { "message" => "%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span},%{DATA:exportable}\]\s+%{DATA:pid}---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" }
  }
}
output {
  elasticsearch {
    hosts => "localhost:9200"
  }
}

Sleuth和Zipkin整合

搭建zipkinServer
1、maven依赖

    io.zipkin.java
    zipkin-autoconfigure-ui


    io.zipkin.java
    zipkin-server

2、添加注解
@SpringBootApplication
@EnableZipkinServer
public class ZipkinApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZipkinApplication.class, args);
    }
}

3、配置

server:
  port: 7777
微服务接入zipkin
1、maven依赖

    org.springframework.cloud
    spring-cloud-sleuth-zipkin

2、配置
spring:
  zipkin:
    base-url: http://localhost:7777/
sleuth:
  sampler:
    percentage: 1.0
访问zipkin页面

http://localhost:7777/

zipkin使用mq搜索数据

zipkin-server数据持久化

你可能感兴趣的:(java)