Nicksxs's Blog

What hurts more, the pain of hard work or the pain of regret?

0%

虽然说之前讲解过一些redis源码相关的,但是说实话,redis的各种使用其实有时候有点生疏,或者在一些特定的使用场景中,一些使用方法还是需要学习和记录的

获取所有数据

获取list类型的所有元素,可以使用 lrange , 直接用lrange key 0 -1
比如

这里有一些方便的就是可以不用知道长度,直接全返回,或者如果想拿到特定区间的就可以直接指定起止范围,

这样就不用一个个pop出来

裁剪list

前面用了lrange取得了一个范围的数据,如果想将数据直接移除,那可以用 ltrim ,

这两个命令就可以从list里取出批量数据,并且能从list里删除这部分数据

可能是太久没写单测了,写个单测发现不符合预期,后来验证下才反应过来
我们来看下demo

class RenameTest extends TestCase
{
    public function setUp(): void
    {
        var_dump("setUp");
    }

    public function test1()
    {
        var_dump("test1");
        assertEquals(1, 1);
    }

    public function test2()
    {
        var_dump("test2");
        assertEquals(1, 1);
    }

    protected function tearDown(): void
    {
        var_dump("tearDown");
    }
}

因为我是想写个重命名的小工具,希望通过setUptearDown做一些文件初始化和清理工作,但是我把两个case的初始化跟清理工作写到了单个setUptearDown中,这样就出现了异常的错误
通过上面的示例代码,可以看到执行结果

❯ vendor/bin/phpunit
PHPUnit 9.5.25 by Sebastian Bergmann and contributors.

.string(5) "setUp"
string(5) "test1"
string(8) "tearDown"
.                                                                  2 / 2 (100%)string(5) "setUp"
string(5) "test2"
string(8) "tearDown"


Time: 00:00.005, Memory: 6.00 MB

OK (2 tests, 2 assertions)

其实就是很简单的会在每个test方法前后都执行setUptearDown

这周开始打算写个比较简单的php工具包,然后顺带学习使用下php的单元测试,通过phpunit还是比较方便的,首先就composer require phpunit/phpunit
安装下 phpunit, 前面包就是通过 composer init 创建,装完依赖后就可以把自动加载代码生成下 composer dump-autoload
目录结构差不多这样

.
├── composer.json
├── composer.lock
├── oldfile.txt
├── phpunit.xml
├── src
│   └── Rename.php
└── tests
    └── RenameTest.php

2 directories, 6 files

src/是源码,tests/是放的单测,比较重要的是phpunit.xml

<?xml version="1.0" encoding="UTF-8"?>
<phpunit colors="true" bootstrap="vendor/autoload.php">
    <testsuites>
        <testsuite name="php-rename">
            <directory>./tests/</directory>
        </testsuite>
    </testsuites>
</phpunit>

其中bootstrap就是需要把依赖包的自动加载入口配上,因为这个作为一个package,也会指出命名空间
然后就是testsuite的路径,源码中

<?php
namespace Nicksxs\PhpRename;

class Rename
{
    public static function renameSingleFile($file, $newFileName): bool
    {
        if(!is_file($file)) {
            echo "it's not a file";
            return false;
        }
        $fileInfo = pathinfo($file);
        return rename($file, $fileInfo["dirname"] . DIRECTORY_SEPARATOR . $newFileName . "." . $fileInfo["extension"]);
    }
}

就是一个简单的重命名
然后test代码是这样,

<?php

// require_once 'vendor/autoload.php';

use PHPUnit\Framework\TestCase;
use Nicksxs\PhpRename\Rename;
use function PHPUnit\Framework\assertEquals;

class RenameTest extends TestCase 
{
    public function setUp() :void
    {
        $myfile = fopen(__DIR__ . DIRECTORY_SEPARATOR . "oldfile.txt", "w") or die("Unable to open file!");
        $txt = "file test1\n";
        fwrite($myfile, $txt);
        fclose($myfile);
    }
    public function testRename()
    {
        Rename::renameSingleFile(__DIR__ . DIRECTORY_SEPARATOR . "oldfile.txt", "newfile");
        assertEquals(is_file(__DIR__ . DIRECTORY_SEPARATOR . "newfile.txt"), true);
    }

    protected function tearDown(): void
    {
        unlink(__DIR__ . DIRECTORY_SEPARATOR . "newfile.txt");
    }
}

setUptearDown 就是初始化跟结束清理的,但是注意如果不指明 __DIR__ ,待会的目录就会在执行 vendor/bin/phpunit 下面,
或者也可以指定在一个 tmp/ 目录下
最后就可以通过vendor/bin/phpunit 来执行测试
执行结果

❯ vendor/bin/phpunit
PHPUnit 9.5.25 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 00:00.005, Memory: 6.00 MB

OK (1 test, 1 assertion)

在目前环境下使用容器部署Java应用还是挺普遍的,但是有一些问题也是随之而来需要解决的,比如容器中应用的dubbo注册,在比较早的版本的dubbo中,就是简单地获取网卡的ip地址。
具体代码在这个方法里 com.alibaba.dubbo.config.ServiceConfig#doExportUrlsFor1Protocol

private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
        String name = protocolConfig.getName();
        if (name == null || name.length() == 0) {
            name = "dubbo";
        }

        String host = protocolConfig.getHost();
        if (provider != null && (host == null || host.length() == 0)) {
            host = provider.getHost();
        }
        boolean anyhost = false;
        if (NetUtils.isInvalidLocalHost(host)) {
            anyhost = true;
            try {
                host = InetAddress.getLocalHost().getHostAddress();
            } catch (UnknownHostException e) {
                logger.warn(e.getMessage(), e);
            }
            if (NetUtils.isInvalidLocalHost(host)) {
                if (registryURLs != null && registryURLs.size() > 0) {
                    for (URL registryURL : registryURLs) {
                        try {
                            Socket socket = new Socket();
                            try {
                                SocketAddress addr = new InetSocketAddress(registryURL.getHost(), registryURL.getPort());
                                socket.connect(addr, 1000);
                                host = socket.getLocalAddress().getHostAddress();
                                break;
                            } finally {
                                try {
                                    socket.close();
                                } catch (Throwable e) {}
                            }
                        } catch (Exception e) {
                            logger.warn(e.getMessage(), e);
                        }
                    }
                }
                if (NetUtils.isInvalidLocalHost(host)) {
                    host = NetUtils.getLocalHost();
                }
            }
        }

通过jdk自带的方法 java.net.InetAddress#getLocalHost来获取本机地址,这样子对于容器来讲,获取到容器内部ip注册上去其实是没办法被调用到的,
而在之后的版本中例如dubbo 2.6.5,则可以通过在docker中设置环境变量的形式来注入docker所在的宿主机地址,
代码同样在com.alibaba.dubbo.config.ServiceConfig#doExportUrlsFor1Protocol这个方法中,但是获取host的方法变成了 com.alibaba.dubbo.config.ServiceConfig#findConfigedHosts

private String findConfigedHosts(ProtocolConfig protocolConfig, List<URL> registryURLs, Map<String, String> map) {
        boolean anyhost = false;

        String hostToBind = getValueFromConfig(protocolConfig, Constants.DUBBO_IP_TO_BIND);
        if (hostToBind != null && hostToBind.length() > 0 && isInvalidLocalHost(hostToBind)) {
            throw new IllegalArgumentException("Specified invalid bind ip from property:" + Constants.DUBBO_IP_TO_BIND + ", value:" + hostToBind);
        }

        // if bind ip is not found in environment, keep looking up
        if (hostToBind == null || hostToBind.length() == 0) {
            hostToBind = protocolConfig.getHost();
            if (provider != null && (hostToBind == null || hostToBind.length() == 0)) {
                hostToBind = provider.getHost();
            }
            if (isInvalidLocalHost(hostToBind)) {
                anyhost = true;
                try {
                    hostToBind = InetAddress.getLocalHost().getHostAddress();
                } catch (UnknownHostException e) {
                    logger.warn(e.getMessage(), e);
                }
                if (isInvalidLocalHost(hostToBind)) {
                    if (registryURLs != null && !registryURLs.isEmpty()) {
                        for (URL registryURL : registryURLs) {
                            if (Constants.MULTICAST.equalsIgnoreCase(registryURL.getParameter("registry"))) {
                                // skip multicast registry since we cannot connect to it via Socket
                                continue;
                            }
                            try {
                                Socket socket = new Socket();
                                try {
                                    SocketAddress addr = new InetSocketAddress(registryURL.getHost(), registryURL.getPort());
                                    socket.connect(addr, 1000);
                                    hostToBind = socket.getLocalAddress().getHostAddress();
                                    break;
                                } finally {
                                    try {
                                        socket.close();
                                    } catch (Throwable e) {
                                    }
                                }
                            } catch (Exception e) {
                                logger.warn(e.getMessage(), e);
                            }
                        }
                    }
                    if (isInvalidLocalHost(hostToBind)) {
                        hostToBind = getLocalHost();
                    }
                }
            }
        }

        map.put(Constants.BIND_IP_KEY, hostToBind);

        // registry ip is not used for bind ip by default
        String hostToRegistry = getValueFromConfig(protocolConfig, Constants.DUBBO_IP_TO_REGISTRY);
        if (hostToRegistry != null && hostToRegistry.length() > 0 && isInvalidLocalHost(hostToRegistry)) {
            throw new IllegalArgumentException("Specified invalid registry ip from property:" + Constants.DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry);
        } else if (hostToRegistry == null || hostToRegistry.length() == 0) {
            // bind ip is used as registry ip by default
            hostToRegistry = hostToBind;
        }

        map.put(Constants.ANYHOST_KEY, String.valueOf(anyhost));

        return hostToRegistry;
    }

String hostToRegistry = getValueFromConfig(protocolConfig, Constants.DUBBO_IP_TO_REGISTRY);
就是这一行,

private String getValueFromConfig(ProtocolConfig protocolConfig, String key) {
    String protocolPrefix = protocolConfig.getName().toUpperCase() + "_";
    String port = ConfigUtils.getSystemProperty(protocolPrefix + key);
    if (port == null || port.length() == 0) {
        port = ConfigUtils.getSystemProperty(key);
    }
    return port;
}

也就是配置了DUBBO_IP_TO_REGISTRY这个环境变量

题目介绍

You are given an integer array nums where the largest integer is unique.

Determine whether the largest element in the array is at least twice as much as every other number in the array. If it is, return the index of the largest element, or return -1 otherwise.
确认在数组中的最大数是否是其余任意数的两倍大及以上,如果是返回索引,如果不是返回-1

示例

Example 1:

Input: nums = [3,6,1,0]
Output: 1
Explanation: 6 is the largest integer.
For every other number in the array x, 6 is at least twice as big as x.
The index of value 6 is 1, so we return 1.

Example 2:

Input: nums = [1,2,3,4]
Output: -1
Explanation: 4 is less than twice the value of 3, so we return -1.

提示:

  • 2 <= nums.length <= 50
  • 0 <= nums[i] <= 100
  • The largest element in nums is unique.

简要解析

这个题easy是题意也比较简单,找最大值,并且最大值是其他任意值的两倍及以上,其实就是找最大值跟次大值,比较下就好了

代码

public int dominantIndex(int[] nums) {
    int largest = Integer.MIN_VALUE;
    int second = Integer.MIN_VALUE;
    int largestIndex = -1;
    for (int i = 0; i < nums.length; i++) {
        // 如果有最大的就更新,同时更新最大值和第二大的
        if (nums[i] > largest) {
            second = largest;
            largest = nums[i];
            largestIndex = i;
        } else if (nums[i] > second) {
            // 没有超过最大的,但是比第二大的更大就更新第二大的
            second = nums[i];
        }
    }

    // 判断下是否符合题目要求,要是所有值的两倍及以上
    if (largest >= 2 * second) {
        return largestIndex;
    } else {
        return -1;
    }
}

通过图

第一次错了是把第二大的情况只考虑第一种,也有可能最大值完全没经过替换就变成最大值了