分类目录微服务

Sentinel  限流

增加依赖
<!--阿里Sentinel限流-->
        <!-- https://mvnrepository.com/artifact/com.alibaba.csp/sentinel-core -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-core</artifactId>
            <version>1.7.1</version>
        </dependency>
声明资源
try(Entry entry = SphU.entry("getRole")) {
            //Sentinel要保护的资源定义
            response = restTemplate.postForObject("http://RBAC/role/test",  Arrays.asList("157875196366160022"),String.class);
            log.info("response={}", response);
        }catch (BlockException e) {
            log.info("限流");
            e.printStackTrace();
        }
定义规则

针对资源定义流控,限流,降级,熔断的规则

Component
public class SentinelConfig implements ApplicationListener<ContextRefreshedEvent> {

    /**
     * 系统启动完成后就执行的代码,在这里声明流控规则
     * @param contextRefreshedEvent
     */
    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        FlowRule rule = new FlowRule();
        rule.setRefResource("getRole");
        //QPS第秒请求的数量
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        //每秒10个
        rule.setCount(10);
        //添加到FlowRuleManager
        List<FlowRule> rules = new ArrayList<FlowRule> ();
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }
}
日志

当前用户下~/logs/csp/

DASHBOARD

https://github.com/alibaba/Sentinel
默认用户名密码都是sentinel
how to download
docker pull bladex/sentinel-dashboard:tagname
how to start
docker run –name sentinel -d -p 8858:8858 -d bladex/sentinel-dashboard:tagname
how to login web-dashboard
visit: http://localhost:8858/
account and password: [sentinel sentinel]
just enjoy 🙂
github
https://github.com/chillzhuang/SpringBlade
website
https://bladex.vip

docker install dashboard
docker run --name sentinel  -d -p 8858:8858  bladex/sentinel-dashboard:1.7.1
springboot
<dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-transport-simple-http</artifactId>
            <version>1.7.1</version>
        </dependency>
项目启动参数
-Dcsp.sentinel.dashboard.server=DASHBOARD的服务地址
使用Spring Cloud Starter Alibaba Sentinel
1. 依赖
<!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-sentinel -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    <version>2.2.0.RELEASE</version>
</dependency>
2. 配制
spring:
  application:
    #应用名
    name: client
  cloud:
    #sentinel流控
    sentinel:
      transport:
        port: 8719
        dashboard: http://192.168.88.108:9001
3. 使用注解定义资源
@GetMapping("/role3")
    //Sentinel要保护的资源定义
    @SentinelResource(value = "getRole",blockHandler = "doOnBlock")
    public Result role3(@AuthenticationPrincipal String userName){
        String response = response = restTemplate.postForObject("http://RBAC/role/test",  Arrays.asList("157875196366160022"),String.class);
        return Result.success(response);
    }
    public Result doOnBlock(@AuthenticationPrincipal String userName,BlockException exception) throws InterruptedException {
        log.info("userName [{}] doOnBlock By [{}]",userName,exception.getClass().getSimpleName());
        return Result.success(userName);
    }
在面版中配制规则更方便
数据持久化

可用zookeeper,apollo,nacos按自己需求配制
1. 下载dashboard源码,选择分支,导入项目,
– 修改pom中相应的 publisher sample 去掉scope
– 实现rule下的数据接口,样列在test包下的rule中,COPY
– 修改controller/v2/FlowControllerV2.java
ruleProvider的注入改flowRuleZookeeperProvider
rulePublisher的注入改flowRuleZookeeperPublisher
– 修改配制文件
application.properties更改默认的用户名及密码,服务器运行端口等
– 修改表态页面
webapp/resources/app/scripts/directives/sidebar/sidebar.html 流控规则的dashboard.flowv1->flow
– 修改数据地址
NacosConfig ConfigService

  1. 修改应用项目
    增加数据源依赖
<!-- https://mvnrepository.com/artifact/com.alibaba.csp/sentinel-datasource-zookeeper -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-zookeeper</artifactId>
    <version>1.7.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba.csp/sentinel-datasource-nacos -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <version>1.8.1</version>
    <scope>test</scope>
</dependency>

增加配制文件
#sentinel规则zoopeeker数据源地址

sentinel:
  zookeeper:
    address: 192.168.88.108:2781
    path: /sentinel_rule_config

去掉手动的规则SentinelConfig,改读取配制文件

@Component
public class SentinelConfig {

    @Value("${sentinel.zookeeper.address}")
    private String zkServer;

    @Value("${sentinel.zookeeper.path}")
    private String zkPath;

    @Value("${spring.application.name}")
    private String appName;
    ......
}

拦截器 Interceptor

实现一个自己拦截器只需要两个步骤即可:

  1. 新建一个类实现HandlerInterceptor接口;
    重写我们需要的方法,如上的三个方法并不是必须的,视自己的业务需求而定。
public class MyInterceptor implements HandlerInterceptor {
    private Logger logger = LoggerFactory.getLogger(MyInterceptor.class);
    /**
     * 预处理回调方法,实现处理器的预处理(如检查登陆),第三个参数为响应的处理器,自定义Controller
     * 返回值:true表示继续流程(如调用下一个拦截器或处理器);
   *       false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应;
   */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        logger.info("preHandle");
        // 返回false会阻止调用链的继续进行
        return true;
    }
    /**
     * 后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null。
   */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        logger.info("postHandle");
    }
    /**
    * 整个请求处理完毕回调方法,即在视图渲染完毕时回调,如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finall
   * 但仅调用处理器执行链中preHandle返回true的拦截器的afterCompletion。
   */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        logger.info("afterCompletion");
    }
}
  1. 在实现了自己的拦截器后还不能马上使用,还需要在Spring中注册与配置,我们新建一个配置类,将上面我们实现的拦截器进行注册,此时拦截器就能生效了。
@Configuration
@EnableWebMvc
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor());
    }
}

多拦截器的处理过程

当我们需要多个拦截器的时候,每个拦截器的方法处理顺序如下:
Interceptor1:preHandle > Interceptor2:preHandle > Interceptor2:postHandle > Interceptor1:postHandle > Interceptor2:afterCompletion > Interceptor1:afterCompletion
为什么会有这样的执行结果呢?其实这和HandlerInterceptor中三个方法的执行时机有关系:

preHandle:在Controller处理之前做拦截进行处理,因此越是声明在前的拦截器,其preHandle方法就越先得到执行。

postHandle:在Controller处理之后,视图渲染返回之前进行处理,有点像括号嵌套的机制,左括号越是在前,其对应的右括号越是在后。

(PS:这里的“返回之前”只是针对视图渲染逻辑的处理,如果是Rest的接口,那么调用会在Controller处理之后直接返回,不会等到postHandle处理完之后才返回)

afterCompletion:在所有处理完成后用于对资源进行回收进行处理,因此,所有的afterCompletion都会在所有的preHandle和postHandle之后才会执行,且和postHandle一样,按照右括号规则执行。

对特定请求地址的拦截

我们可以在拦截器中定义对那些URL需要拦截,而哪些URL是不需要做任何处理的。

@Configuration
@EnableWebMvc
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 对所有的请求都要拦截,但是“/getInt”不在拦截的范围之内
        registry.addInterceptor(new MyInterceptor())
                .addPathPatterns("/**").excludePathPatterns("/getInt");
    }
}

拦截器适配器HandlerInterceptorAdapter

有时候我们可能只需要实现三个回调方法中的某一个,如果实现HandlerInterceptor接口的话,三个方法必须实现,不管你需不需要,此时spring提供了一个HandlerInterceptorAdapter适配器(一种适配器设计模式的实现),允许我们只实现需要的回调方法。

k8s卸载清理

k8s 节点删除

kubectl drain <node name> --delete-local-data --force --ignore-daemonsets
kubectl delete node <node name>
kubeadm reset

若需要重新加入 ,则再次执行 kubeadm init or kubeadm join

清理 Pods

kubectl delete node --all
rm -r /var/etcd/backups/* 删除备份。

卸载清理K8S

kubeadm reset -f
modprobe -r ipip
lsmod
rm -rf ~/.kube/
rm -rf /etc/kubernetes/
rm -rf /etc/systemd/system/kubelet.service.d
rm -rf /etc/systemd/system/kubelet.service
rm -rf /usr/bin/kube*
rm -rf /etc/cni
rm -rf /opt/cni
rm -rf /var/lib/etcd
rm -rf /var/etcd

链路监控 Spring Cloud Sleuth & Zipkin

链路监控 Spring Cloud Sleuth

        <!--<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-sleuth</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-sleuth-zipkin</artifactId>
        </dependency>-->
        <!--已经包括sleuth和zipkin-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>

Zipkin

docker run -itd --name zipkin -p 9411:9411 openzipkin/zipkin

spring:
  zipkin:
    base-url: http://192.168.88.108:9411
 sleuth:
    sampler:
      probability: 1

CentOS6.5升级内核 安装Docker

查看系统版本

lsb_release -a
uname
rpm -q redhat-release
cat /proc/version

升级内核

vi /etc/sysconfig/network-scripts/ifcfg-eth0 配制DNS
yum update nss
rpm –import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org 导入KEY
rpm -Uvh http://www.elrepo.org/elrepo-release-6-8.el6.elrepo.noarch.rpm 安装ELRepo
yum –enablerepo=elrepo-kernel install kernel-lt –y 安装内核
vi /etc/grub.conf # 改为default=0 修改引导文件,将默认引导改为0
reboot 启完成后查看内核版本

安装docker

yum install docker-io
提示错误:No package docker-io available,则运行
yum -y install http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
yum install epel-release
yum -y install https://get.docker.com/rpm/1.7.1/centos-6/RPMS/x86_64/docker-engine-1.7.1-1.el6.x86_64.rpm
service docker start
查看已安装的包:
yum list installed | grep docker
删除软件包:
yum -y remove docker-io.x86_64
删除镜像/容器:
rm -rf /var/lib/docker

Error

docker 安装之后,通过命令查看docker相关信息时,可能会出现错误:Get http:///var/run/docker.sock/v1.19/version: dial unix /var/run/docker.sock: no such file or directory. Are you trying to connect to a TLS-enabled daemon without TLS?
服务无法正常停止或重启,可能就是系统环境出现了问题,
You may have to enable the public_ol6_latest repo in order to get this package.
sudo yum-config-manager –enable public_ol6_latest
And then install the package:
sudo yum install device-mapper-event-libs

Spring Cloud Stream 分组

服务 介绍
stream-group-sender 消息发送者服务
stream-group-receiverA 消息接收者服务
stream-group-receiverB 消息接收者服务

创建stream-group-sender 服务

spring.application.name=stream-sender
//对应 MQ 是 exchange outputProduct自定义的信息
spring.cloud.stream.bindings.outputProduct.destination=exchangeProduct
1. 定义发送接口

public interface ISendeService {
    String OUTPUT="outputProduct";
    /**
     * 指定输出的交换器名称
     * @return
     */
    @Output(OUTPUT)
    MessageChannel send();
}
  1. 在启动类增加注解
    // 绑定我们刚刚创建的发送消息的接口类型
    @EnableBinding(value={ISendeService.class})
  2. 测试发送
@RunWith(SpringRunner.class)
@SpringBootTest(classes=StreamSenderStart.class)
public class StreamTest {
    @Autowired
    private ISendeService sendService;

    @Test
    public void testStream(){
        Product p = new Product(666, "stream test ...");
        // 将需要发送的消息封装为Message对象
        Message message = MessageBuilder
                                .withPayload(p)
                                .build();
        sendService.send().send(message );
    }
}

创建stream-group-receiverA服务

spring.application.name=stream-group-receiverA
// 对应 MQ 是 exchange 和消息发送者的 交换器是同一个
spring.cloud.stream.bindings.inputProduct.destination=exchangeProduct
// 具体分组 对应 MQ 是 队列名称 并且持久化队列 inputProduct 自定义
spring.cloud.stream.bindings.inputProduct.group=groupProduct
1. 定义接收口

public interface IReceiverService {
    String INPUT = "inputProduct";
    /**
     * 指定接收的交换器名称
     * @return
     */
    @Input(INPUT)
    SubscribableChannel receiver();
}
  1. 消息的具体处理
@Service
@EnableBinding(IReceiverService.class)
public class ReceiverService {
    @StreamListener(IReceiverService.INPUT)
    public void onReceiver(Product p){
        System.out.println("消费者A:"+p);
    }
}
  1. 在启动类添加注解
    @EnableBinding(value={IReceiverService.class})

创建stream-group-receiverB服务

 此服务和stream-group-receiverA一样,复制一份只需修改配制中的服务名称,端口,group设置不一样
在stream-group-receiverA和stream-group-receiverB服务的group不一致的情况下都收到了消息
改为同组的情况下只有其中一个受到消息。避免了消息重复消费

RabbitMq使用

用于异步处理,日志处理,流量削峰,应用解耦

引用依赖

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
        </dependency>

参数配制

默认配制
spring:
 rabbitmq:
   host: localhost
   port: 5672
   username: guest
   password: guest

发送消息

public class MqSend {
    @Autowired
    private AmqpTemplate amqpTemplate;

     /**
     * 发送Mq测试消息
     */
    public void send(){
        amqpTemplate.convertAndSend("myQueue","now "+new Date());
    }

    /**
     * 发送数据供应商分组Mq测试消息
     */
    public void sendOrder(){
        amqpTemplate.convertAndSend("myQueue","computer","now "+new Date());
    }

}

接收消息

@Slf4j
@Component
public class MqReceiver {

    //1. 接收手动创建的消息,需先手动创建myQueue队列名
    /*@RabbitListener(queues = "myQueue")
    public void processtest(String message){
        log.info("MQReceiver: {}",message);
    }*/

    //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);
    }
     /**
     * 数码供应商服务 接收分组消息
     * @param message
     */
    @RabbitListener(bindings = @QueueBinding(
            exchange = @Exchange("myOrder"),
            key = "computer",
            value = @Queue("computerOrder")
    ))
    public void processComputer(String message) {
        log.info("computer MqReceiver: {}", message);
    }

    /**
     * 水果供应商服务 接收分组消息
     * @param message
     */
    @RabbitListener(bindings = @QueueBinding(
            exchange = @Exchange("myOrder"),
            key = "fruit",
            value = @Queue("fruitOrder")
    ))
    public void processFruit(String message) {
        log.info("fruit MqReceiver: {}", message);
    }
}