存档1月 2020

链路监控 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

Ubuntu自定桌面主题

Ubuntu18.04 默认的桌面环境是 gnome3,相比较 unity 和 KDE,笔者更喜欢 gnome,unity 看起来有点楞,KDE 又有些复杂,不够简洁。

  1. 安装 gnome-tweaks
    在 Ubuntu 的应用商店中搜索 gnome-tweaks,安装即可。
    安装完成后打开 gnome-tweaks,Appearance 标签下的 shell 选项是 disable 的,参见 这里
    自行解决。

  2. dash-to-panel 插件安装
    我觉得,插件有这一个就够了。
    genome 桌面顶部会有个状态栏,然后还会有个任务栏,对于不大的屏幕来说,简直是浪费空间,而且丑逼。相较而言,Windows 的处理方式,我就觉得很好,而 dash-to-panel 插件,就做了这样一件事,合并两栏。

    1. 下载
      在 这里 下载相应版本的插件。
      下载链接:https://www.ubuntu.com/download/desktop

    2. 安装
      然后解压,解压后将文件夹的名字改为 dash-to-panel@jderose9.github.com 这个形式, 然后放到 ~/.local/share/gnome-shell/extensions/ 路径下,这个时候,打开 gnome-tweaks 后,就可以在 Extensions 的标签下看到看到该插件了,enable 就可以了。

  3. 安装 vimix 主题
    提供了各种安装方式,按照文档安装就好了,这个没啥问题。
    安装完成后,打开 gnome-tweaks 的 Appearance 标签下,分别在 Applications 和 Shell 选项里选择 vimix 相关的主题就好了,笔者选的是 Vimix-Laptop。

  4. 安装 vimix 图标主题
    提供了各种安装方式,按照文档操作就好了

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

WordPress 文章禁用自动保存、修订版本的两种方法

方法一、通过修改 WordPress 配置文件实现功能禁用

不得不说,WordPress 有非常多的隐藏功能都是可以通过 WordPress 安装根目录下的 wp-config.php 这个配置文件来实现功能的启用禁用等。

//禁用自动保存
define(‘AUTOSAVE_INTERVAL’, false);
//设置自动保存间隔/秒
define(‘AUTOSAVE_INTERVAL’, 120);
//禁用文章修订
define(‘WP_POST_REVISIONS’, false);
//设置修订版本最多允许几个
define(‘WP_POST_REVISIONS’, 3);

方法二、当前使用主题 functions.php 文件添加代码

  1. 禁用 WordPress 自动保存
//禁用自动保存(方法一)
add_action( 'admin_print_scripts', create_function( '$a', "wp_deregister_script('autosave');" ) );
//禁用自动保存(方法二)
add_action('wp_print_scripts', 'fanly_no_autosave');
function fanly_no_autosave() { wp_deregister_script('autosave'); }
  1. 禁用 WordPress 修订版本
//禁用所有文章类型的修订版本
add_filter( 'wp_revisions_to_keep', 'fanly_wp_revisions_to_keep', 10, 2 );
function fanly_wp_revisions_to_keep( $num, $post ) { return 0;}
//禁用某种文章类型的修订版本
add_filter( 'wp_revisions_to_keep', 'fanly_wp_revisions_to_keep', 10, 2 );
function fanly_wp_revisions_to_keep( $num, $post ) {
    if ( 'post_type' == $post->post_type ) { //引号中 post_type 改为你想禁用修订版本的文章类型
        return 0;
    }
    return $num;
}

常见 WordPress 常见默认有的文章类型有:

文章(Post Type: ‘post’)
页面(Post Type: ‘page’)
附件媒体(Post Type: ‘attachment’)
修订版本(Post Type: ‘revision’)
导航菜单(Post Type: ‘nav_menu_item’)

当然如果网站使用了自定义文章类型,那么就是自定义的名称了。

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);
    }
}

Windows Server 2008 R2 SN

Windows Server 2008 R2 激活码,无需破解,直接使用

  1. 下面的激活码,有180天试用期
    Windows Server 2008 R2 Web: KBV3Q-DJ8W7-VPB64-V88KG-82C49
    Windows Server 2008 R2 Standard: 4GGC4-9947F-FWFP3-78P6F-J9HDR
    Windows Server 2008 R2 Enterprise: 7PJBC-63K3J-62TTK-XF46D-W3WMD
    Windows Server 2008 R2 Datacenter: QX7TD-2CMJR-D7WWY-KVCYC-6D2YT

  2. 在180天试用期即将结束时,在命令行模式下,输入“slmgr.vbs /rearm”后,重启电脑,重新输入上面的序列号,剩余时间又恢复到180天。微软官方文档中声明该命令只能重复使用5次。

  3. 上面的方法5次后,此后将无法再次使用。就要进行下一步,修改注册表中的一处键值(SkipRearm),以后就可以再次使用“Rearm”的命令,这个键值总共可以修改8次。

win2008r2激活码有两种,一种是server2008永久密钥,另一种是kms安装密钥

Winserver2008序列号说明:server2008永久密钥有激活次数限制,可能失效。kms激活密钥是微软官方提供,没有激活次数限制,永久有效。

一、windows server 2008/R2 批量授权版VL 永久序列号
Server 2008 R2 Std and Ent Volume MAK:标准版和企业版
[Key]:HR6PW-GDXKC-BXVGJ-PRWDX-B3C68 [剩余次数:400+]
[Key]:74BV6-J6RV8-X669X-TWXJ3-6WYQG [剩余次数:40+]
[Key]:C38C9-RPMJY-JC2F8-YRWMG-8G864 [剩余次数:40+]
Server 2008 R2 DC and IA64 Volume MAK:
[Key]:BTPC8-738CF-KC4D4-K7GX6-RFJY8 [剩余次数:40+]
[Key]:J9FC3-JV743-MXQ4D-KDCQT-MMQV9 [剩余次数:40+]
Server 2008 R2 Web and CC Volume MAK:
[Key]:6KWRD-PP7K8-7BKR6-GW9FC-96P24 [剩余次数:40+]
[Key]:7WMCT-PPJX7-B3BVH-7W8JT-D4G48 [剩余次数:40+]
[Key]:QYXXF-T2DR9-MXFP9-KF2XP-89Q2T [剩余次数:40+]
Server 2008 Standard & Enterprise MAK:
[Key]:BC4DK-3FV3H-96KDQ-2YQ74-GXV2R [剩余次数:400+]
[Key]:KF36P-T8CH4-7B844-B8BW7-VWDBB [剩余次数:40+]
Windows Web & Compute Cluster Server 2008 MAK:
[Key]:37CW9-K88BH-3W9XB-QXB9K-GW9T4 [剩余次数:40+]
[Key]:D9QWP-YHC9P-8RK27-B2C27-DTMTV [剩余次数:40+]

二、windows server 2008/R2 批量授权版KMS安装密钥
1、Windows Server 2008 R2
Windows Server 2008 R2 Web 版:6TPJF-RBVHG-WBW2R-86QPH-6RTM4
Windows Server 2008 R2 HPC 版:TT8MH-CG224-D3D7Q-498W2-9QCTX
WindowsServer 2008 R2 Standard:YC6KT-GKW9T-YTKYR-T4X34-R7VHC
WindowsServer 2008 R2 企业版:489J6-VHDMP-X63PK-3K798-CPX3Y
WindowsServer 2008 R2 Datacenter:74YFP-3QFB3-KQT8W-PMXWJ-7M648
面向基于 Itanium 系统的 Windows Server 2008 R2:GT63C-RJFQ3-4GMB6-BRFB9-CB83V
2、Windows Server 2008
Windows Web Server 2008:WYR28-R7TFJ-3X2YQ-YCY4H-M249D
Windows Server 2008 Standard:TM24T-X9RMF-VWXK6-X8JC9-BFGM2
不带 Hyper-V 的 Windows Server 2008 Standard:W7VD6-7JFBR-RX26B-YKQ3Y-6FFFJ
Windows Server 2008 企业版:YQGMW-MPWTJ-34KDK-48M3W-X4Q6V
不带 Hyper-V 的 Windows Server 2008 企业版:39BXF-X8Q23-P2WWT-38T2F-G3FPG
Windows Server 2008 HPC:RCTX3-KWVHP-BR6TB-RB6DM-6X7HP
Windows Server 2008 Datacenter:7M67G-PC374-GR742-YH8V4-TCBY3
不带 Hyper-V 的 Windows Server 2008 Datacenter:22XQ2-VRXRG-P8D42-K34TD-G3QQC
面向基于 Itanium 系统的 Windows Server 2008:4DWFP-JF3DJ-B7DTH-78FJB-PDRHK
三、windows server 2008/R2 KMS密钥激活步骤
1、点击开始图标打开开始菜单,在搜索框输入cmd,右键cmd,选择“以管理员身份运行”;
2、打开命令提示符,依次执行下面的代码。分别表示:安装server2008密钥,设置kms服务器,激活server2008,查询激活期限,kms一般是180天,到期后再次激活。
slmgr /ipk 489J6-VHDMP-X63PK-3K798-CPX3Y
slmgr /skms zh.us.to
slmgr /ato
slmgr /xpr

Feign/RestTemplate

  1. 增加依赖
         <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
  1. 在启动主类增加注解
@EnableFeignClients
  1. 定义feign调用商品微服务的接口
访问PRODUCT服务下的接口
@FeignClient(name = "PRODUCT")
public interface ProductClient {

    /**
     * 从商品微服务中获取一个测试信息
     * @return
     */
    @GetMapping("/msg")
    String productMsg();

    /**
     * 根据一组商品ID获取商品列表
     * @param productIdList
     * @return
     */
    @PostMapping("/listForOrder")
    List<ProductInfoOutput> listForOrder(@RequestBody List<String> productIdList);

    /**
     * 扣库存
     * @param decreaseStockInputList
     */
    @PostMapping("/decreaseStock")
    void decreaseStock(@RequestBody List<DecreaseStockInput> decreaseStockInputList);
}

  1. 调用feign接口
@Api(tags="RestTemplate/feign调用微服务API")
@RestController
@Slf4j
public class ClientController {
    /*第二种方式*/
   /* @Autowired
    private LoadBalancerClient loadBalancerClient;*/

    /* 第三种方式
     @Autowired
    private RestTemplate restTemplate;*/

    /*第四种方式feign*/
    @Autowired
    private ProductClient productClient;

    @ApiOperation("获取一个测试信息")
    @GetMapping("/msg")
    public String getProductMsg(){
        //第一种方式 直接使用RestTemplate,URL固定
        //RestTemplate restTemplate = new RestTemplate();
        //String response = restTemplate.getForObject("http://localhost:8080/productserver/msg",String.class);

        //第二种方式 直接使用RestTemplate,URL利用LoadBalancerClient获取
        /*RestTemplate restTemplate = new RestTemplate();
        ServiceInstance serviceInstance = loadBalancerClient.choose("PRODUCT");
        String url = String.format("http://%s:%s/msg",serviceInstance.getHost(),serviceInstance.getPort());
        String response = restTemplate.getForObject(url,String.class);*/

        //第三种,将 RestTemplate 作为一个Bean 配制到项目,使用注解@LoadBalanced,在restTemplate直接使用应用名字
        //String response = restTemplate.getForObject("http://PRODUCT/msg",String.class);
        //4.feign
        String response = productClient.productMsg();
        log.info("response={}",response);
        return response;
    }
    }
    第三种方式使用注解@LoadBalanced的配制文件
    @Component
    public class RestTemplateConfig {
     @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
    or
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        //return new RestTemplate();
        return new RestTemplateBuilder().basicAuthentication("oauth", "123").build();
    }
    }

HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> map= new LinkedMultiValueMap<>();
map.add(“grant_type”, “password”);
map.add(“client_id”, “oauth”);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, header);
MyToken myToken = restTemplate.postForObject(“http://192.168.88.108:30002/oauth/token”,request,MyToken.class);

    // build http headers
    HttpHeaders headers = new HttpHeaders();
    headers.add("Authorization","bearer "+myToken.getAccess_token());
    ResponseEntity<ClientUser[]> responseEntity = restTemplate.exchange(RBAC_SERVER+POST_ALL_USER, HttpMethod.GET,new HttpEntity<String>(headers),ClientUser[].class);

Ribbon 客户端负载均衡

更改负载均衡规则

PRODUCT:
  ribbon:
    NFLoadBalancerRuleClassName: com.loadbalancer.RandomRule

PRODUCT为应用名,此处改为随机方式,注意写完整的Class路径,不改默认为轮询,一般也够用了,其它参考官方说明文档

Hystrix熔断

具有服务降级(HystrixCommand注解指定/fallbackMethod回退函数实现降级逻辑),服务熔断,依赖隔离,监控的作用,防雪崩的利器

        <!--添加Hystrix依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
           <!-- <version>2.0.2.RELEASE</version>-->
        </dependency>
        <!--添加Hystrix dashboard依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
            <!-- <version>2.0.2.RELEASE</version>-->
        </dependency>
        <!--如果有了不用再引入-->
        <!--<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>-->
在主函数上添加注解
@EnableCircuitBreaker //启动Hystrix
@EnableHystrixDashboard

@HystrixCommand
如果我们使用的是@HystrixCommand注解,那么可以在注解中直接指定超时时间,如下:

@HystrixCommand(fallbackMethod="fallback",
    commandProperties = {
         @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000" )
    }
)

当然也可以指定commandKey,然后在配置文件中配置超时时间,如下:

@HystrixCommand(fallbackMethod="fallback",commandKey="userGetKey")
配置文件给commandKey配置超时时间:
hystrix.command.userGetKey.execution.isolation.thread.timeoutInMilliseconds = 13000

全局配置
如果只是想全局的配置,可以配置默认的超时时间:

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=3000

接口级别配置
假如我们的Feign Client定义如下:

@FeignClient(value = "user-service", fallbackFactory = UserRemoteClientFallbackFactory.class)
public interface UserRemoteClient {

    @GetMapping("/user/get")
    public ResponseData<UserDto> getUser(@RequestParam("id") Long id);

}

那么配置如下:

hystrix.command.UserRemoteClient#getUser(Long).execution.isolation.thread.timeoutInMilliseconds = 300

为什么要配置成上面的方式呢?

其实就是对commandKey进行配置,只要我们知道commandKey的生成规则就可以对接口级别进行配置,接口级别的规则是 Client名称#方法名(参数类型)

源码在feign.hystrix.SetterFactory.Default中:

String commandKey = Feign.configKey(target.type(), method);
服务级别配置
1.在Zuul中针对服务级别的话,直接配置service-id,如下:

hystrix.command.service-id.execution.isolation.thread.timeoutInMilliseconds=3000
Zuul中之所以要配置service-id原因是commandKey就是用的service-id, 通过源码分析可以得到结论。

首先进入的是RibbonRoutingFilter中的run方法,然后我们看核心的forward方法:

ClientHttpResponse response = forward(commandContext);
在forward中有下面的代码:

RibbonCommand command = this.ribbonCommandFactory.create(context);
通过create可以定位到具体的实现,这边就看你用的什么Http客户端,默认有三种实现,默认定位到org.springframework.cloud.netflix.zuul.filters.route.apache.HttpClientRibbonCommandFactory.create(RibbonCommandContext)方法。
所以service-id就是commandKey。

2.在Feign中针对服务级别的话,需要对commandKey进行定制,可以用service-id, 也可以用Feign Client Name,如下:

@Bean
@Scope("prototype")
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "feign.hystrix.enabled")
public Feign.Builder feignHystrixBuilder() {
    return HystrixFeign.builder().setterFactory(new SetterFactory() {

        @Override
        public Setter create(Target<?> target, Method method) {
            String groupKey = target.name();
            String commandKey = Feign.configKey(target.type(), method);
            return HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
                        //.andCommandKey(HystrixCommandKey.Factory.asKey(commandKey))
                        //.andCommandKey(HystrixCommandKey.Factory.asKey(groupKey))
                        .andCommandKey(HystrixCommandKey.Factory.asKey(target.type().getSimpleName()));
            }
    });
}
.andCommandKey(HystrixCommandKey.Factory.asKey(commandKey))
默认的接口方式

.andCommandKey(HystrixCommandKey.Factory.asKey(groupKey))
service-id方式

.andCommandKey(HystrixCommandKey.Factory.asKey(target.type().getSimpleName()));
Feign Client Name方式

配置的话根据不同的配置填写不通的commandKey就可以了:

hystrix.command.Feign Client Name.execution.isolation.thread.timeoutInMilliseconds=3000