SpringCloud核心组件实战笔记

课程地址:https://coding.imooc.com/class/187.html

聚合:对多个api调用逻辑进行聚合来减少客户端请求数

裁剪:根据不同的需求返回不同的数据,例:pc端返回html,app端返回json,显然pc端更详细

spring cloud eureka是基于netflix eureka做了二次封装

eureka:
	client:
	 service-url:
		defaultZone: http://localhost:8080/eureka/
	 register-with-eureka: false
	server:
	 enable-self-preservation: false//关闭上线率检测

多台机器的eureka需要两两注册,两台交叉注册

client注册到所有的eureka上面

康威定律

通信方式

restTemplate

//第一种方式(直接使用restTemplate,url写死)
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.getForObject("http://localhost:8080/msg",String.class);//url,返回类型

//第二种方式(利用loadBalancerClient通过应用名称获取url,让后使用restTemplate)
@Autowired
private LoadBalancerClient loadBalancerClient;

RestTemplate restTemplate = new RestTemplate();
ServiceInstance serviceInstance = loadBalancerClient.choose("PRODUCT");
String url = String.format("http://%s:%s",serviceInstance.getHost(),serviceInstance.getPort())+"/msg";
String response = restTemplate.getForObject(url,String.class);

//第三种方式(@LoadBalanced,可在restTemplate里面使用应用名字)
@Component
public class RestTemplateConfig {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

@Autowired
private RestTemplate restTemplate;

String reponse = restTemplate.getForObject("http://Product/msg",String.class);

ribbon

自定义负载均衡策略

PRODUCT:
	ribbon:
		NFLoadBalancerRuleClassName: com.netfix.loadbalancer.RandomRule//默认轮询,这里改成随机策略

feign(采用了基于接口的注解)

@FeignClient(name = "product")
public interface ProductClient {
    @GetMapping("/msg")
    String productMsg();
}

反序列化失败,可能是缺少无参构造方法

统一配置中心

需要配置远程git

关系远端git-----config server -----本地git

cloud:
  config:
    server:
      git:
        uri:
        username:
        password:
        basedir://本地配置文件存放位置

/{name}-{profiles}.yml

/{label}/{name}-{profiles}.yml

name 服务名

profiles环境

label分支

先启动再去拉取配置需要改成bootstrap.yml

找不到id

客户端先去注册中心找config server ,没有发现远端git将注册中心改成8762,默认会使用8761的地址,这个时候就要拿出来配置

eureka:
	client:
	 service-url:
		defaultZone: http://localhost:8762/eureka/

远端git有多个配置文件注册到config server他会根据文件名类型将多个文件整合

springCloud Bus自动刷新配置,rabbitmq通知配置变更

使用Finchley.BUILD-SNAPSHOT这个版本和spring-cloud-starter-bus-amqp组件

注意升级版本后,spring-cloud-starter-feign改名成spring-cloud-starter-openfeign了

要对属性哪些配置做刷新记得在类上加@RefreshScope

记得暴露刷新api接口

management:
  endpoints:
	web:
      expose: "*"

配置自动访问/bus-refresh 接口

在远程仓库配置自动刷新

选择webhooks

点击add webhook

填写payloadURL地址http:ip/monitor

填写Content type 选applicationn/json

然后更改远程git配置文件就能实现动态刷新

rabbitmq

1、@RabbitListener(queues = "myQueue")
2、自动创建队列@RabbitListener(queuesToDeclare = @Queue("myQueue"))
3、自动创建,Exchange和Queue绑定
    @RabbitListener(bindings = @QueueBinding(
    value = @Queue("myQueue"),
    exchange = @Exchange("myExchange")
    ))
    public void process(String message) {
    log.info("MqReceiver:{}",message);
}
只接受带key的消息
    @RabbitListener(bindings = @QueueBinding(
    exchange = @Exchange("myOrder"),
    key = "fruit",
    value = @Queue("fruitQueue"),
    ))
    public void process(String message) {
    log.info("MqReceiver:{}",message);
}

spring cloud stream对中间件封装

定义

public interface StreamClient {
    
    String INPUT = "myMessage";
	@Input(StreamClient.INPUT)
	SubscribableChannel input();
	
	@Output(StreamClient.INPUT)
	MessageChannel output();
}

接收端

@Component
@EnableBinding(StreamClient.class)
@Slf4j
public class StreamReceiver {
    
    @StreamListener(StreamClient.INPUT)
    public void process(Object message){
        log.info("StreamReceiver:{}",message);
    }
}

发送端

@RestController
public class StreamSend {
    
    @Autowired
    private StreamClient streamClient;
	
    @GetMapping("/sendMessage")
    public void process(){
        String message = "now" + new Date();
        streamClient.output().send(MessageBuilder.withPayload(message).build());
    }
}

配置分组

spring:
  application:
    stream:
      bindings:
        myMessage:
          group: order
          content-type: application/json    //不用在编码里面序列化和反序列化就可查看堆积消息内容

实现接收消息的回应,添加@SendTo()

@StreamListener(value = StreamClient.INPUT)
@SendTo(StreamClient.INPUT2)
public String process(OrderDTO message) {
    log.info("StreamReceiver:{}",message);
    return "received";
}

@StreamListener(value = StreamClient.INPUT2)
public void process(String message) {
    log.info("StreamReceiver:{}",message);
}

订单失败回滚时,消息已经发送,那么就需要将事务单独提取出一个方法

zuul

zuul:
  sensitive-headers:#全局可传递cookie
  routes:
    myProduct:
      path: /myProduct/**
      serviceId: product
      sensitiveHeaders:   #这里设置为空,方便获取cookie
  #简介写法
    product: /myProduct/**
  #排除某些路由
  ignored-patterns:
    - /**/product/listForOrder
#查看所有路由配置,要关闭权限校验,访问localhost:端口/application/routes
management:
  security:
    enabled: false
    

动态配置路由

可以新建一个类添加@Componet注解
    
或者加到启动类
    
@ConfigurationProperties("zuul")
@RefreshScop
public ZuulProperties zuulProperties() {
    return new zuulProperties();
}

限流早于鉴权

继承ZuulFilter实现方法

//令牌数量 
private static final RateLimiter RATE_LIMITER = RateLimiter.create(100);
//调高优先级
@Override
public int filterOrder()
{
    return SERVLET_DETECTION_FILTER-ORDER - 1;
}    
//处理令牌
@Override
public Object run() {
    if(!RATE_LIMITER.tryAcquire()) {
        throw new RateLimitException();
    }
    return null;
}

设置cookie+redis

public static void set(HttpServletRespose response,
                      String name,
                      String value,
                      int maxAge) {
    Cookie cookire  = new Cookie(name, value);
    cookie.setPath("/");
    cookie.setMaxAge(maxAge);
    response.addCookie(cookie);
}
//使用
String token = UUID.randomUUID().toString();
Integer expire = CookieConstant.expire;
stringRedisTemplate.opsForValue().set(String.format(RedisConstant.TOKEN_TEMPLATE,token),
                                     openid,
                                     expire,
                                     TimeUnit.SECONDS);
CookieUtil.set(response, CookieConstant.OPENID, openid,CookieConstant.expire);

//防止刷新导致redis里面存多个key
public static Cookie get(HttpServletRequest request,
                        String name) {
    Cookie[] cookies = request.getCookies();
    if(cookies != null) {
        for(Cookie cookie: cookies) {
            if(name.equals(cookie.getName())) {
                return cookie;
            }
        }
    }
    return null;
}
//使用
Cookie cookie = CookieUtil.get(request,CookieConstant.TOKEN);
if(cookie != null && 
!StringUtils.isEmpty(stringRedisTemplate.opsForValue().get(RedisConstant.TOKEN_TEMPLATE,cookie.getValue()))){
    return ResultVOUtil.success();
}

zuul鉴权

@Override
public boolean shouldFilter() {
    RequestContext requestContext = RequestContext.getCurrentContext();
    HttpServletRequest request = requestContext.getRequest();
    if("/order/order/create".equals(request.getRequestURI())) {
        return true;
    }
    retrun false;
}

zuul跨域配置

@Configuration
public class CorsConfig {
   
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration config = new CorsConfiguration();
        
        config.setAllowCredentials(true);
        config.setAllowedOrigins(Arrays.asList("*"));
        config.setAllowedHeaders(Arrays.asList("*"));
        config.setAllowedMethods(Arrays.asList("*"));
        config.setMaxAge(300l);
        
        source.registerCorsConfiguration("/**",config);
        return new CorsFilter(source);
            
    }
}

spring cloud hystrix

服务降级

优先核心服务,非核心服务不可用或弱可用

通过HystrixCommand注解指定

fallbackMethod(回退函数)中具体实现降级逻辑

@SpringCloudApplication=@SpringBootApplication+@EnableDiscoveryClint+@EnableCircuitBreaker

//当然服务降级不止应用于服务,出现错误可以用降级处理

@RestController
//配置默认降级方法
@DefaultProperties(defaultFallback = "defaultFallback")
public class HystrixController {
    @HystrixCommand(commandProperties = {
        @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
    })//配置服务超时时间/进入降级时间
    //服务熔断
    @HystrixCommand(commandProperties = {
        @HystrixProperty(name ="circuitBreaker.enabled",value = "true"),//设置熔断
        @HystrixProperty(name ="circuitBreaker.requestVolumeThreshold",value = "10"),
        @HystrixProperty(name ="circuitBreaker.sleepWindowInMilliseconds",value = "10000"),
        @HystrixProperty(name ="circuitBreaker.errorThresholdPercentage",value = "60"),
    @HystrixCommand(fallbackMethod = "fallback")
    @GetMapping("/getProductInfoList")
    public String getPoductInfoList() {
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.postForObject("http://127.0.0.1:8005/product/list",
                                         Arrays.asList("1578832904"),
                                         String.class);
    }
    private String fallback() {
        return "太拥挤了,请稍后再试"
    }
    
}

//熔断器有三个状态,打开,关闭,半开状态,出问题会先进入半开状态,失败次数超过窗口设置阈值就会打开

yaml配置,记得在降级方法上面加@HystrixCommand注解,否则不生效
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
     	    timeoutInMilliseconds: 1000
   	getProductInfoList://针对方法降级
      execution:
        isolation:
 	 	  thread:
			timeoutInMilliseconds: 3000

hystrix-dashboard

导入依赖,加上@EnableHystrixDashboard

management:
	context-path: /
	访问:ip:port/hystrix.stream

第一次启动项目有时候会超时?因为懒加载,刚开始可以将超时时间设置长一些

链路监控

Spring Cloud Sleuth

通过zipkin外部面板观察链路

zipkin:
  base-url: http://localhost:9411/
sleuth:
  sampler:
    percentage: 1

访问:localhost:9411/zipkin/

posted @ 2024-02-05 14:30  开源遗迹  阅读(8)  评论(0编辑  收藏  举报