[转]Confluence环境搭建

原文地址:https://blog.csdn.net/u013952133/article/details/81634978

1. 环境准备

JDK1.8安装: Centos7下安装与卸载Jdk1.8

Mysql 5.7安装:CentOS7下YUM安装与配置MySQL5.7

  1. yum install -y mysql-server mysql mysql-devel
  2. service mysqld start #初始化及相关配置
  3. chkconfig –list | grep mysqld #查看mysql服务是不是开机自动启动
  4. chkconfig mysqld on #设置成开机启动
  5. mysqladmin -u root password ‘123456’  #通过该命令给root账号设置密码为 123456
  6. mysql -u root -p #登录mysql数据库
  7. create database confluence character set UTF8;
  8. grant all on confluence.* to confluence@“%” identified by “confluence”;
  9. grant all on confluence.* to confluence@“localhost” identified by “confluence”;
  10. FLUSH PRIVILEGES;
  11. quit #退出
  12. service mysqld stop #关闭mysql服务
  13. cd /etc/
  14. vi my.cnf
  15. 在[mysqld]下面加上character-set-server =utf8 #解决中文显示???的乱码问题
  16. service mysqld start #启动mysql服务

confluence安装

1)        下载atlassian-confluence-6.7.1-x64.bin安装包,

2)        修改文件权限chmod +xatlassian-confluence-6.3.1-x64.bin

3)        安装文件./atlassian-confluence-6.3.1-x64.bin

 2. 破解confluence

2.1        下载注册机

下载confluence_keygen.jar注册机,见附件

链接:https://pan.baidu.com/s/1gg85p4Z 密码:3t5b

2.2        破解jar包

将/opt/atlassian/confluence/confluence/WEB-INF/lib/atlassian-extras-decoder-v2-3.3.0.jarjar文件ftp到本地,并重命名为atlassian-extras-2.4.jar,运行confluence_keygen.jar,点击.patch,选择atlassian-extras-2.4.jar文件,点击打开,jar文件破解成功。

2.3        上传破解jar包

将破解后的atlassian-extras-2.4.jar上传到服务器/opt/atlassian/confluence/confluence/ WEB-INF/lib/目录下,并重命名为atlassian-extras-decoder-v2-3.3.0.jar

3         配置confluence

3.1        重启confluence服务

停止:sh /opt/atlassian/confluence/bin/stop-confluence.sh

启动:sh /opt/atlassian/confluence/bin/start-confluence.sh

3.2        访问confluence

登录http://192.168.137.121:8090/

3.3        选择中文界面

3.4        填写授权码

运行confluence_keygen.jar,随便填写NAME,输入Server ID,点击.gen,复制key到文本框中,点击下一步。

至此,confluence破解完成

3.5        配置其他操作

选择内置数据库

以上参考:

confluence6.7.1安装与破解

手把手教你实现Confluence6.7.1安装与破解

linux 破解版confluence安装

4. 遇到的问题以及解决方式

4.1 配置mysql的时候报如下错误:

 

解决方式:

打开mysql,设置@@global.tx_isolation, @@tx_isolation为READ-COMMITED。

4.2 打开报如下错误

解决方式:

先检查@@global.tx_isolation, @@tx_isolation为READ-COMMITED,用set方式设置貌似重启mysql后又会恢复为默认值REPEATABLE-READ,可参考官网指示:

https://confluence.atlassian.com/confkb/confluence-fails-to-start-and-throws-mysql-session-isolation-level-repeatable-read-is-no-longer-supported-error-241568536.html

修改mysql的my.cnf文件,在[mysqld]下添加:

我的修改后还是不行,原因是我在搭建起confluence后,将mysql远程访问给关闭了,还是一直报这个错误,后面讲mysql的远程访问给开启就好了。

4.3 启动confluence的时候报如下错:

原因:我的confluence目录权限是confluence,修改为root后就好了。

4.4 confluence启动过程中报错,提示无法加载confluence.cfg.xml,具体错误没有截图。

可能是由于confluence不支持openJDK,将openJDK卸载,安装JDK即可解决。

4.5 confluence服务器搭建成功后,新建page很慢,提示连接不上服务器

解决方式:在confluence界面上找到一般配置,将协同编辑关闭。

4.6 上传附件后预览显示乱码

将window上的中文相关字体安装到Linux服务器上,安装后即可正常显示。

4.7 confluence卸载

直接在/opt/atlassian/confluence目录下执行uninstall即可卸载。

[转]Long类型转json时前端js丢失精度解决方案

一、问题背景

Java后端开发过程中,尤其是id字段,因数值太大,通过json形式传输到前端后,在js解析时,会丢失精度。

如果对精度丢失没有什么概念,可以看一个知乎的帖子,来感受一下:https://www.zhihu.com/question/34564427?sort=created

二、解决思路

将id字段序列化为json时,转换为字符串类型,前端传输到后端,反序列化时,再重新转换为Long。

三、具体实现

在dto所在项目中,新建一个helper包(名字自定义,也可以放现有包里)。PS:为什么要建到dto项目中?因为,这个包最后可能会给其他组使用,这样以来,所有的处理规则逻辑都是统一的,方便对接。

在包里添加类LongJsonSerializer,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
 * Long 类型字段序列化时转为字符串,避免js丢失精度
 *
 */
public class LongJsonSerializer extends JsonSerializer<Long> {
    @Override
    public void serialize(Long value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
        String text = (value == null null : String.valueOf(value));
        if (text != null) {
            jsonGenerator.writeString(text);
        }
    }
}

然后在包里再添加类LongJsonDeserializer,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
 * 将字符串转为Long
 *
 */
public class LongJsonDeserializer extends JsonDeserializer<Long> {
    private static final Logger logger = LoggerFactory.getLogger(LongJsonDeserializer.class);
 
    @Override
    public Long deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        String value = jsonParser.getText();
        try {
            return value == null null : Long.parseLong(value);
        catch (NumberFormatException e) {
            logger.error("解析长整形错误", e);
            return null;
        }
    }
}

 

好了,接下来是使用这两个类。

在需要处理的id字段上,加上注解。比如如下代码:

1
2
3
4
5
6
/**
 * id
 */
@JsonSerialize(using = LongJsonSerializer.class)
@JsonDeserialize(using = LongJsonDeserializer.class)
private Long id;

[转]PHP7下的协程实现

转:https://segmentfault.com/a/1190000012457145?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io

相信大家都听说过『协程』这个概念吧。

但是有些同学对这个概念似懂非懂,不知道怎么实现,怎么用,用在哪,甚至有些人认为yield就是协程!

我始终相信,如果你无法准确地表达出一个知识点的话,我可以认为你就是不懂。

如果你之前了解过利用PHP实现协程的话,你肯定看过鸟哥的那篇文章:在PHP中使用协程实现多任务调度| 风雪之隅

鸟哥这篇文章是从国外的作者翻译来的,翻译的简洁明了,也给出了具体的例子了。

我写这篇文章的目的,是想对鸟哥文章做更加充足的补充,毕竟有部分同学的基础还是不够好,看得也是云头雾里的。

我个人,不喜欢写长篇文章,微博关注我 @码云 ,每天用微博分享知识。文章同时记录在我的博客:https://bruceit.com/p/A4kSfE

什么是协程

先搞清楚,什么是协程。

你可能已经听过『进程』和『线程』这两个概念。

进程就是二进制可执行文件在计算机内存里的一个运行实例,就好比你的.exe文件是个类,进程就是new出来的那个实例。

进程是计算机系统进行资源分配和调度的基本单位(调度单位这里别纠结线程进程的),每个CPU下同一时刻只能处理一个进程。

所谓的并行,只不过是看起来并行,CPU事实上在用很快的速度切换不同的进程。

进程的切换需要进行系统调用,CPU要保存当前进程的各个信息,同时还会使CPUCache被废掉。

所以进程切换不到非不得已就不做。

那么怎么实现『进程切换不到非不得已就不做』呢?

首先进程被切换的条件是:进程执行完毕、分配给进程的CPU时间片结束,系统发生中断需要处理,或者进程等待必要的资源(进程阻塞)等。你想下,前面几种情况自然没有什么话可说,但是如果是在阻塞等待,是不是就浪费了。

其实阻塞的话我们的程序还有其他可执行的地方可以执行,不一定要傻傻的等!

所以就有了线程。

线程简单理解就是一个『微进程』,专门跑一个函数(逻辑流)。

所以我们就可以在编写程序的过程中将可以同时运行的函数用线程来体现了。

线程有两种类型,一种是由内核来管理和调度。

我们说,只要涉及需要内核参与管理调度的,代价都是很大的。这种线程其实也就解决了当一个进程中,某个正在执行的线程遇到阻塞,我们可以调度另外一个可运行的线程来跑,但是还是在同一个进程里,所以没有了进程切换。

还有另外一种线程,他的调度是由程序员自己写程序来管理的,对内核来说不可见。这种线程叫做『用户空间线程』。

协程可以理解就是一种用户空间线程。

协程,有几个特点:

  • 协同,因为是由程序员自己写的调度策略,其通过协作而不是抢占来进行切换
  • 在用户态完成创建,切换和销毁
  • ⚠️ 从编程角度上看,协程的思想本质上就是控制流的主动让出(yield)和恢复(resume)机制
  • generator经常用来实现协程

说到这里,你应该明白协程的基本概念了吧?

PHP实现协程

一步一步来,从解释概念说起!

可迭代对象

PHP5提供了一种定义对象的方法使其可以通过单元列表来遍历,例如用foreach语句。

你如果要实现一个可迭代对象,你就要实现Iterator接口:

<?php
class MyIterator implements Iterator
{
    private $var = array();

    public function __construct($array)
    {
        if (is_array($array)) {
            $this->var = $array;
        }
    }

    public function rewind() {
        echo "rewinding\n";
        reset($this->var);
    }

    public function current() {
        $var = current($this->var);
        echo "current: $var\n";
        return $var;
    }

    public function key() {
        $var = key($this->var);
        echo "key: $var\n";
        return $var;
    }

    public function next() {
        $var = next($this->var);
        echo "next: $var\n";
        return $var;
    }

    public function valid() {
        $var = $this->current() !== false;
        echo "valid: {$var}\n";
        return $var;
    }
}

$values = array(1,2,3);
$it = new MyIterator($values);

foreach ($it as $a => $b) {
    print "$a: $b\n";
}

生成器

可以说之前为了拥有一个能够被foreach遍历的对象,你不得不去实现一堆的方法,yield关键字就是为了简化这个过程。

生成器提供了一种更容易的方法来实现简单的对象迭代,相比较定义类实现Iterator接口的方式,性能开销和复杂性大大降低。

<?php
function xrange($start, $end, $step = 1) {
    for ($i = $start; $i <= $end; $i += $step) {
        yield $i;
    }
}
 
foreach (xrange(1, 1000000) as $num) {
    echo $num, "\n";
}

记住,一个函数中如果用了yield,他就是一个生成器,直接调用他是没有用的,不能等同于一个函数那样去执行!

所以,yield就是yield,下次谁再说yield是协程,我肯定把你xxxx。

PHP协程

前面介绍协程的时候说了,协程需要程序员自己去编写调度机制,下面我们来看这个机制怎么写。

0)生成器正确使用

既然生成器不能像函数一样直接调用,那么怎么才能调用呢?

方法如下:

  1. foreach他
  2. send($value)
  3. current / next…

1)Task实现

Task就是一个任务的抽象,刚刚我们说了协程就是用户空间协程,线程可以理解就是跑一个函数。

所以Task的构造函数中就是接收一个闭包函数,我们命名为coroutine

/**
 * Task任务类
 */
class Task
{
    protected $taskId;
    protected $coroutine;
    protected $beforeFirstYield = true;
    protected $sendValue;

    /**
     * Task constructor.
     * @param $taskId
     * @param Generator $coroutine
     */
    public function __construct($taskId, Generator $coroutine)
    {
        $this->taskId = $taskId;
        $this->coroutine = $coroutine;
    }

    /**
     * 获取当前的Task的ID
     * 
     * @return mixed
     */
    public function getTaskId()
    {
        return $this->taskId;
    }

    /**
     * 判断Task执行完毕了没有
     * 
     * @return bool
     */
    public function isFinished()
    {
        return !$this->coroutine->valid();
    }

    /**
     * 设置下次要传给协程的值,比如 $id = (yield $xxxx),这个值就给了$id了
     * 
     * @param $value
     */
    public function setSendValue($value)
    {
        $this->sendValue = $value;
    }

    /**
     * 运行任务
     * 
     * @return mixed
     */
    public function run()
    {
        // 这里要注意,生成器的开始会reset,所以第一个值要用current获取
        if ($this->beforeFirstYield) {
            $this->beforeFirstYield = false;
            return $this->coroutine->current();
        } else {
            // 我们说过了,用send去调用一个生成器
            $retval = $this->coroutine->send($this->sendValue);
            $this->sendValue = null;
            return $retval;
        }
    }
}

2)Scheduler实现

接下来就是Scheduler这个重点核心部分,他扮演着调度员的角色。

/**
 * Class Scheduler
 */
Class Scheduler
{
    /**
     * @var SplQueue
     */
    protected $taskQueue;
    /**
     * @var int
     */
    protected $tid = 0;

    /**
     * Scheduler constructor.
     */
    public function __construct()
    {
        /* 原理就是维护了一个队列,
         * 前面说过,从编程角度上看,协程的思想本质上就是控制流的主动让出(yield)和恢复(resume)机制
         * */
        $this->taskQueue = new SplQueue();
    }

    /**
     * 增加一个任务
     *
     * @param Generator $task
     * @return int
     */
    public function addTask(Generator $task)
    {
        $tid = $this->tid;
        $task = new Task($tid, $task);
        $this->taskQueue->enqueue($task);
        $this->tid++;
        return $tid;
    }

    /**
     * 把任务进入队列
     *
     * @param Task $task
     */
    public function schedule(Task $task)
    {
        $this->taskQueue->enqueue($task);
    }

    /**
     * 运行调度器
     */
    public function run()
    {
        while (!$this->taskQueue->isEmpty()) {
            // 任务出队
            $task = $this->taskQueue->dequeue();
            $res = $task->run(); // 运行任务直到 yield

            if (!$task->isFinished()) {
                $this->schedule($task); // 任务如果还没完全执行完毕,入队等下次执行
            }
        }
    }
}

这样我们基本就实现了一个协程调度器。

你可以使用下面的代码来测试:

<?php
function task1() {
    for ($i = 1; $i <= 10; ++$i) {
        echo "This is task 1 iteration $i.\n";
        yield; // 主动让出CPU的执行权
    }
}
 
function task2() {
    for ($i = 1; $i <= 5; ++$i) {
        echo "This is task 2 iteration $i.\n";
        yield; // 主动让出CPU的执行权
    }
}
 
$scheduler = new Scheduler; // 实例化一个调度器
$scheduler->addTask(task1()); // 添加不同的闭包函数作为任务
$scheduler->addTask(task2());
$scheduler->run();

关键说下在哪里能用得到PHP协程。

function task1() {
        /* 这里有一个远程任务,需要耗时10s,可能是一个远程机器抓取分析远程网址的任务,我们只要提交最后去远程机器拿结果就行了 */
        remote_task_commit();
        // 这时候请求发出后,我们不要在这里等,主动让出CPU的执行权给task2运行,他不依赖这个结果
        yield;
        yield (remote_task_receive());
        ...
}
 
function task2() {
    for ($i = 1; $i <= 5; ++$i) {
        echo "This is task 2 iteration $i.\n";
        yield; // 主动让出CPU的执行权
    }
}

这样就提高了程序的执行效率。

关于『系统调用』的实现,鸟哥已经讲得很明白,我这里不再说明。

3)协程堆栈

鸟哥文中还有一个协程堆栈的例子。

我们上面说过了,如果在函数中使用了yield,就不能当做函数使用。

所以你在一个协程函数中嵌套另外一个协程函数:

<?php
function echoTimes($msg, $max) {
    for ($i = 1; $i <= $max; ++$i) {
        echo "$msg iteration $i\n";
        yield;
    }
}
 
function task() {
    echoTimes('foo', 10); // print foo ten times
    echo "---\n";
    echoTimes('bar', 5); // print bar five times
    yield; // force it to be a coroutine
}
 
$scheduler = new Scheduler;
$scheduler->addTask(task());
$scheduler->run();

这里的echoTimes是执行不了的!所以就需要协程堆栈。

不过没关系,我们改一改我们刚刚的代码。

把Task中的初始化方法改下,因为我们在运行一个Task的时候,我们要分析出他包含了哪些子协程,然后将子协程用一个堆栈保存。(C语言学的好的同学自然能理解这里,不理解的同学我建议去了解下进程的内存模型是怎么处理函数调用)

 /**
     * Task constructor.
     * @param $taskId
     * @param Generator $coroutine
     */
    public function __construct($taskId, Generator $coroutine)
    {
        $this->taskId = $taskId;
        // $this->coroutine = $coroutine;
        // 换成这个,实际Task->run的就是stackedCoroutine这个函数,不是$coroutine保存的闭包函数了
        $this->coroutine = stackedCoroutine($coroutine); 
    }

当Task->run()的时候,一个循环来分析:

/**
 * @param Generator $gen
 */
function stackedCoroutine(Generator $gen)
{
    $stack = new SplStack;

    // 不断遍历这个传进来的生成器
    for (; ;) {
        // $gen可以理解为指向当前运行的协程闭包函数(生成器)
        $value = $gen->current(); // 获取中断点,也就是yield出来的值

        if ($value instanceof Generator) {
            // 如果是也是一个生成器,这就是子协程了,把当前运行的协程入栈保存
            $stack->push($gen);
            $gen = $value; // 把子协程函数给gen,继续执行,注意接下来就是执行子协程的流程了
            continue;
        }

        // 我们对子协程返回的结果做了封装,下面讲
        $isReturnValue = $value instanceof CoroutineReturnValue; // 子协程返回`$value`需要主协程帮忙处理
        
        if (!$gen->valid() || $isReturnValue) {
            if ($stack->isEmpty()) {
                return;
            }
            // 如果是gen已经执行完毕,或者遇到子协程需要返回值给主协程去处理
            $gen = $stack->pop(); //出栈,得到之前入栈保存的主协程
            $gen->send($isReturnValue ? $value->getValue() : NULL); // 调用主协程处理子协程的输出值
            continue;
        }

        $gen->send(yield $gen->key() => $value); // 继续执行子协程
    }
}

然后我们增加echoTime的结束标示:

class CoroutineReturnValue {
    protected $value;
 
    public function __construct($value) {
        $this->value = $value;
    }
     
    // 获取能把子协程的输出值给主协程,作为主协程的send参数
    public function getValue() {
        return $this->value;
    }
}

function retval($value) {
    return new CoroutineReturnValue($value);
}

然后修改echoTimes

function echoTimes($msg, $max) {
    for ($i = 1; $i <= $max; ++$i) {
        echo "$msg iteration $i\n";
        yield;
    }
    yield retval("");  // 增加这个作为结束标示
}

Task变为:

function task1()
{
    yield echoTimes('bar', 5);
}

这样就实现了一个协程堆栈,现在你可以举一反三了。

4)PHP7中yield from关键字

PHP7中增加了yield from,所以我们不需要自己实现携程堆栈,真是太好了。

把Task的构造函数改回去:

    public function __construct($taskId, Generator $coroutine)
    {
        $this->taskId = $taskId;
        $this->coroutine = $coroutine;
        // $this->coroutine = stackedCoroutine($coroutine); //不需要自己实现了,改回之前的
    }

echoTimes函数:

function echoTimes($msg, $max) {
    for ($i = 1; $i <= $max; ++$i) {
        echo "$msg iteration $i\n";
        yield;
    }
}

task1生成器:

function task1()
{
    yield from echoTimes('bar', 5);
}

这样,轻松调用子协程

[转]中高级的一些PHP面试题

原文链接:https://zhuanlan.zhihu.com/p/27493130
公司1:

1、mysql_real_escape_string mysql_escape_string有什么本质的区别,有什么用处,为什么被弃用?

答:mysql_real_escape_string需要预先连接数据库,并可在第二个参数传入数据库连接(不填则使用上一个连接)

两者都是对数据库插入数据进行转义,但是mysql_real_escape_string转义时,会考虑数据库连接的字符集。

它们的用处都是用来能让数据正常插入到数据库中,并防止sql注入,但是并不能做到100%防止sql注入。

再问:为什么不能100%防止?

答;因为客户端编码以及服务器端编码不同,可能产生注入问题,但是其实这种场景不多见。

继续答:被弃用的原因是官方不再建议使用mysql_xx的数据库操作方式,建议使用pdo和mysqli,因为不管从性能跟安全来看,mysqli都比mysql要好。

衍生出来的问题是mysqli的连接复用(持久化)问题,这一块我并没有答好。

2、什么是内存泄漏,js内存泄漏是怎么产生的?

答:内存泄漏是因为一块被分配内存既不能被使用,也不能被回收,直到浏览器进程结束。

产生泄漏的原因是闭包维持函数内局部变量,不能被释放,尤其是使用闭包并存在外部引用还setInterval的时候危害很大。

备注:我觉得这块回答并不好,因为肯定不是闭包的原因。

我查了一下资料,从比较浅的方位来再回答一下这个问题:

产生泄漏的原因有好几种:

(1) 页面元素被删除,但是绑定在该元素上的事件未被删除;

(2) 闭包维持函数内局部变量(外部不可控),使其得不到释放;

(3) 意外的全局变量;

(4) 引用被删除,但是引用内的引用,还存在内存中。

从上述原因上看,内存泄漏产生的根本原因是引用无法正确回收,值类型并不能引发内存泄漏。

对于每个引用,都有自己的引用计数,当引用计数归零或被标记清除时,js垃圾回收器会认为该引用可以回收了。

3、什么是闭包,跟原型链、作用域链有什么关联

答:闭包是指存在于一个作用域链分支的函数域内的函数,该函数可以向上逐级访问作用域链上的变量,直到找到为止。当闭包存在外部引用时,js会维持闭包自身以及所在函数作用域链的内存状态。

备注:这个是我自己瞎说的。

继续答:跟原型链没有什么关联,函数的原型(prototype)主要用于实现继承,原型链可用于追溯继承关系,与作用域链类似,都是向上逐级访问属性,直到被找到,原型链的顶层是null,可以理解为所有的object都继承至null,所以null的类型是object。

继续答:作用域链可以看作是一个树形结构,由根节点window向下扩散,下层节点可以访问上层节点,但是上层节点无法访问下层节点,产生闭包的函数作用域属于节点中的一个,向下扩散后闭包函数产生叶子节点,叶子节点之间可以互相访问,当访问的变量在叶子节点中无法找到时,向上层节点查找,直到被找到为止,这个概念有点类似原型链上的属性查找。

4、一台电脑配置无限好,可以同时打开多少个网页

答:65535-1000 = 64535(端口数)

5、ip地址能被伪造吗?

答:http头部可以被篡改,但是只能修改X_FORWARDED_FOR,真实ip地址(REMOTE_ADDR)很难修改(除非是路由器去修改),因为真实ip是底层会话ip地址,而且因为TCP 3次握手的存在,连接无法建立,伪造的意义不大,至于UDP的话,一般是内网才使用UDP通信。

6、有100万个奖品,每个人可以中奖3次,先到先得,怎么控制并发,不能发超,并保证完全的先到先得模式

答:百万奖品在打乱后预先insert到数据库,所有中奖操作,均只能update,不能insert。进来抽奖的用户使用memcahe原子加锁,实现抽奖次数自增,当抽奖次数到达3时,返回不中奖。

再问:预先插入需要很多资源,如果奖品数量上了1亿怎么办?

答:使用redis队列存储请求,跑守护进程异步发奖,产生的问题是用户无法实时看到中奖情况。

再问:这样肯定不行。

再答:使用全局内存加锁确保抽奖过程是单进程在跑,但是会面临大并发阻塞问题。

再问:内存比较宝贵,在不用内存加锁的情况下怎么办,并且如果碰到1亿奖池的情况,预先插入数据库肯定不好,怎么办?

答:设置奖品概率,分三张表,都使用innodb引擎,一张存中奖记录(预先插入一行),一张存奖品发放概况,一张存用户抽奖情况(uin唯一索引),大并发情况下,利用mysql的排他锁进行并发控制。流程如下:

begin

查询用户抽奖次数,加排他锁

对用户抽奖次数的更新/插入

锁行查询发放情况

获得抽奖结果(某些奖品发完之后,动态变更概率)

更新发放表

插入中奖记录

commit

再问:遇到脏读怎么办?

答:这方面不是很了解

再问:innodb的master线程在什么情况下fork其他子线程?

答:不知道

7、数据链路层的数据是怎么校验的,有哪些校验方式?

答:crc32,别的校验可能是取模校验奇偶数吧。

备注:答个crc校验就行了。

8、b+树的查询时间复杂度是多少,哈希表是多少,为什么数据库索引用b+树存储,而不是哈希表,数据库索引存储还有其他数据结构吗?

答:O(log(n)),O(1)

因为哈希表是散列的,在遇到`key`>’12’这种查找条件时,不起作用,并且空间复杂度较高。

备注:b+数根据层数决定时间复杂度,数据量多的情况下一般4-5层,然后用二分法查找页中的数据,时间复杂度远小于log(n)。

9、apache是怎么跟php通讯的,sapi是什么

答:使用sapi通讯,sapi是php封装的对外数据传递接口,通常有cgi/fastcgi/cli/apache2handler四种运行模式。

10、php的垃圾回收机制?

答:垃圾回收是指当php运行状态结束时,比如遇到了exit/die/致命错误/脚本运行结束时,php需要回收运行过程中创建的变量、资源的内存。

ZEND引擎维护了一个栈zval,每个创建的变量和资源都会压入这个栈中,每个压入的数组结构都类似:[refcount => int, is_ref => 0|1, value => union, type => string],变量被unset时,ref_count如果变成0,则被回收。

当遇到变量循环引用自身时,使用同步回收算法回收。

备注:PHP7已经重写了zal的结构体。

11、jquery的sizzle引擎工作原理

答:除了直到是DOM元素查找引擎之外,一无所知。

12、seajs的工作原理,如何解决重复加载库的问题,如何进行资源的同步加载

答:建立映射关系并缓存起来;资源并不能真正同步加载,只是返回一个回调。

13、memcache跟redis的区别

答:可存储数据结构不同;redis支持持久化存储。

14、md5逆向原理

答:先用字典查找,再尝试暴力破解。

再问:没有更好的方法了吗?

答:没有了。

备注:嗯,事实上也确实没有特别好的办法,只能使用TB级的海量特征库用数据库存起来,然再分片查找。

15、父类方法是protected,子类重构为private,会发生什么?

答:会发生fatal错误,因为继承的方法或属性只能维持或放大权限,不能缩小,比如protected重载为public是可行的。

16、一个网页从输入地址回车,到完整展示网页内容这段时间里,做了哪些工作,越详细越好。

答:

0、浏览器本地缓存匹配;

1、本地hosts映射对比;

2、本地dns缓存解析;

3、远程dns解析获得服务器ip地址;

4、浏览器发送tcp连接请求包(syn);

5、请求包经过传输层、网络层、数据链路层封装通过网卡到达路由器;

6、路由器转发数据包到所属运营商服务器;

7、运营商服务器通过寻址最短路径通过中继节点到达指定ip地址;

8、服务器端可能存在反向代理或者负载均衡,都是直接转发请求至上游服务器,当然也可以制定安全防御规则直接丢弃请求包;

9、上游服务器收到连接请求,在自身可用的情况下,返回(syn+ack);

10、浏览器校验ack,再次发送(syn+ack);

11、服务器校验ack切换连接状态至established,然后根据请求传输数据包;

12、当transform-encoding为chunked时,浏览器开始渲染页面;

13、四次挥手,连接关闭;

14、渲染数据完成。

备注:还有很多东西不懂,一些东西完全是自己瞎蒙的,因为时间原因,以后有时间详细画一下。

17、keep-alive的概念

答:长连接机制,表示keep-alive-timeout时间内,如果连接没有closed,再次传输数据不再需要三次握手了。

备注:这里也有很多疑问,需要好好捋一捋。

18、linux文件压缩操作命令,shell脚本等

备注:因为平时开发都是在windows环境,对linux了解不足,这一块几乎是0分。

公司2:

这个是被鄙视最惨的一家了,首先会有笔试,相对来说并不复杂,但是有些坑,很多已经忘记了。

印象深刻的是我说自己熟悉常用设计模式,然后让我画UML类图,我就懵逼了,所以在写简历的时候,最好是写自己非常熟悉的,如果只是一知半解,并没有必要放到简历中。

公司3:

这里仅列举几个问到的问题:

1、设计一个中继服务器,转发客户A->客户B的请求;

2、myisam跟innodb有什么区别;

3、php进程死锁产生的原因是什么?怎么自动排查与自动恢复?

4、有class A { public function b($a, $b, $c){}};

怎么使用[‘b’ => 2, ‘a’ => 1, ‘c’ => 3],对进行A::b进行调用,并顺利赋值?

5、php5.2->php7.1的各版本演进历史,新增特性等?

6、画一个tcp三次握手图

[转]从密码到token, 一个授权的故事

1. 我把密码献给你

小梁开发了一个“信用卡管家”的程序 , 可以自动从邮箱中读取信用卡相关邮件,分析、汇总,形成一个报表。

小梁找到信用卡达人张大胖试用 : “你的信用卡那么多,看看我这个程序吧, 保准你会爱死它。”

张大胖尝试了几下说: “咦,你这个程序要读取我的网易邮箱啊,那需要用户名/密码吧”

“是啊 , 你把密码告诉输入程序不就行了, 我的程序替你加密保存,保证不会泄露。”

“得了吧你, 我可不会告诉你我的密码, 为了方便记忆, 我的密码都是通用的, 万一泄露了就完蛋了”

小梁说:“这样吧,我不保存,我就访问邮箱的时候使用一次, 用完就扔!”

“你以为你是阿里巴巴啊, 有信用背书, 你只是个小网站, 我把密码献给你,总是觉得不安全。就是我信任你,别人能信任你吗?”

小梁想想也是, 这是一个巨大的心理障碍, 每个人都要誓死捍卫自己的密码啊。

2. Token

过了一周, 小梁兴致勃勃地把张大胖拉来看“信用卡管家”的升级版。

“升级为2.0了啊, 这次不用问你要网易邮箱的用户名和密码了”

“那你怎么访问我的邮箱?”

“很简单,我提供了一个新的入口,使用网易账号登录, 你点了以后,其实就会重定向到网易的认证系统去登录,  网易的认证系统会让你输入用户名和密码,并且询问你是否允许信用卡管家访问网易邮箱, 你确认了以后,就再次重定向到我的‘信用卡管家’网站, 同时捎带一个‘token’ 过来, 我用这个token 就可以通过API来访问网易邮箱了。 在这个过程中, 我根本不会接触到你的用户名和密码,怎么样, 这下满意了吧?”

“你说得轻松, 你这个信用卡管家是个小网站,还没有什么名气, 网易怎么会相信你这个网站呢?”

“我当然要先在网易注册一下啊, 他们会给我发个app_id 和app_secret,  我重定向到网易的时候需要把这个东西发过去, 这样网易就知道是‘信用卡管家’这个应用在申请授权了。”

(点击看大图)

张大胖说: “ 你这重定向来重定向去的, 实际上不就是为了拿到一个token 吗?”

“对啊,因为你不信任我的信用卡管家, 不让它保存你的密码,只好用token的方法了 , 它是网易认证中心颁发的,实际上就代表了你对信用卡管家访问邮箱的授权,所以有了这个token 就可以访问你的邮箱了”

“对了”  张大胖问题, “你为什么用Javascript的方式来读取token啊”

“这样我的后端服务器就不用参与了,工作都在前端搞定, 你注意到那个URL中的#号了吗? www.a.com/callback#token=<网易返回的token>”

张大胖说: “我知道啊,这个东西叫做hash fragment,  只会停留在浏览器端, 只有Javascript 能访问它,并且它不会再次通过http request 发到别的服务器器, 我想这是为了提高安全性吧。”

小梁说: “没错, 那个token非常非常重要,得妥善保存,不能泄露!”

“可是在第6步通过重定向,这个token 以明文的方式发送给了我的浏览器, 虽然是https ,不会被别人窃取,可是浏览器的历史记录或者访问日志中就能找到, 岂不暴露了?”

小梁说: “这个…. , 我说你这个家伙,安全意识很强烈嘛, 让我想想,有没有更安全的方式。”

3. Authorization  Code + Token

又过了一周,小梁成功地把信用卡管家升级为3.0.

他对张大胖说: “这次我成功地把那个非常重要的、表示授权的token 给隐藏起来了, 你要不要看看?”

“你先说说你是怎么隐藏的?”

“其实整体思路和之前的类似,只是我引入了一个叫做Authorization  Code 的中间层。 当你用网易账号登录的时候, 网易认证中心这一次不给我直接发token,而是发一个授权码(authorization code) ,   我的信用卡管家服务器端取到这个code以后,在后台再次访问网易认证中心, 这一次他才发给我真正的token 。 还是直接上图吧:”

(点击看大图)

张大胖说: “还比较容易理解, 本质上就是你拿着这个返回的授权码在服务器后台‘偷偷地’完成申请token 的过程, 所以token 浏览器端根本就接触不到,对吧?”

“什么叫偷偷地申请token ? 这是我信用卡管家服务器和网易之间的正常交流, 只是你看不到而已。”

“开个玩笑了, 你虽然隐藏了token,但是这个授权码确是暴露了啊,你看第7步,我在浏览器中都能明文看到,  要是被谁取到, 不也是照样能取到token吗?”

小梁说: “我们肯定有防御措施, 比如这个授权码和我的信用卡管家申请的app_id,app_secret关联, 只有信用卡管家发出的token请求, 网易认证中心才认为合法; 还可以让授权码有时间限制,比如5分钟失效,还有可以让授权码只能换一次token, 第二次就不行了。 ”

“听起来似乎不错, 好吧, 这次我可以放心地使用了!”

4. 后记

本文讲的其实就是就是OAuth 中的三种认证方式,依次是:

1. Resource Owner Password Credentials Grant(资源所有者密码凭据许可)

2. Implicit Grant(隐式许可)

3. Authorization Code Grant(授权码许可)

还有一种叫做Client credentials ,用的较少,文章没有涉及。

知乎日报搜索小应用

博主每天都要刷知乎日报,感觉里面的内容很不错,但是有时候看到的文章并没有收藏,下次再找的时候十分不方便,这里做一个简单的小网页来提供知乎日报的搜索功能。

先放上网站链接:知乎日报搜索

前端样式比较丑,后期有时间进行改进。

一 抓取知乎日报内容

网上之前有调用http://zhihudaily.ahorn.me这个接口来返回知乎日报文章url,后来发现接口已经不可用,经过查找,发现可用的三个接口,还比较方便:

http://news-at.zhihu.com/api/3/news/latest  #获取最新消息

http://news.at.zhihu.com/api/3/news/before/20170401  #获取以前的消息,before后面要加日期

http://news-at.zhihu.com/api/3/news/  #获取指定消息,news后面加消息ID

后面的事情就好说了,随便写个爬虫,从2013年开始,抓到目前为止的所有内容,这里返回的都是json数据,更好处理。

二 使用sphinx作为中文搜索引擎

Sphinx的安装和使用

如果建立索引的时候报错,可以用一下参数:

/usr/local/sphinx-for-chinese/bin/indexer –all –rotate

三 搭建一个php代理处理图片盗链

把图片url抓取之后,发现知乎做了反盗链,图片没法直接显示,最后搭了简单的php代理,通过后端get_file_contents($image_url),或者通过curl设置referer, 请求原始图片数据echo 出来返回前端,这样就解决了知乎的反盗链。但是服务器需要下载和展示图片,流量会是原来的2倍,对于预算不足的服务器可能不太值当。另外可以把图片下载后放到微博的图床上,获取微博的未防盗链的图片,这样对服务器的性能会更好!


//防止别人用我的接口,这里判断refer,只有自己网站可用
if(isset($_SERVER['HTTP_REFERER']) &amp;amp;&amp;amp; (strpos($_SERVER['HTTP_REFERER'], 'http://zhihu.dadaaierer.com/') !== 0)) {
return;
}
if(!isset($_GET['url']) ||empty($_GET['url'])) {
return;
}
echo file_get_contents($_GET['url']);

文章中的img url通过


preg_replace('/src=\&amp;quot;(.*)\&amp;quot;/', 'src=&amp;quot;http://zhihu.dadaaierer.com/site/url?url=$1&amp;quot;', $tmp['content']);

替换,即可展示文章的图片。

957892883d1c4b6e7f469c69c4b2af6e

如图,搜索今日知乎日报小姐姐之后的结果。

PS:由于博主是单核1G阿里云服务器,图片请求全部打到php做的代理上已经出现扛不住,链接超时报502的错误,博主已经修改了php-fpm的默认max_children,希望近期能扛得住。

[php5.6]php扩展开发入门–helloworld

 

首先需要下载php源码

1,生成扩展骨架 php-5.6.30/ext$ ./ext_skel –extname=helloWorld
同时产生操作步骤
1. $ cd ..
2. $ vi ext/helloWorld/config.m4
3. $ ./buildconf
4. $ ./configure –[with|enable]-helloWorld
5. $ make
6. $ ./sapi/cli/php -f ext/helloWorld/helloWorld.php
7. $ vi ext/helloWorld/helloWorld.c
8. $ make

2,vim config.m4
把第10-13行
PHP_ARG_WITH(helloWorld, for helloWorld support,
Make sure that the comment is aligned:
[ –with-helloWorld Include helloWorld support])
注释dnl去掉

132b2005134ec7d02ac7b8137212d5c3

3,./buildconf –force
4,./configure –with-helloWorld
5 make 可选
6,进入到扩展vim helloWorld.c
修改144-147行
照着添加一个方法定义
PHP_FE(helloWorld, NULL)

同时添加一个实现
PHP_FUNCTION(helloWorld)
{
char *arg = NULL;
int arg_len;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “s”, &arg, &arg_len) == FAILURE) {
return;
}
RETURN_STRINGL(arg, arg_len, 0);

}

7 正常的添加扩展步骤
1)phpize
2)./configure –with-php-config=/home/users/wangchunwei/php/bin/php-config
3) make
4) make install

 

剩下的步骤就是配置php.ini的扩展,添加helloWorld.so。之后调用helloWorld(“string”)方法,亲测可用!

蔬菜水果价格走势图

本周一直在学习新的东西,虽然学习了很多,但大多是公司内部的资料。就把上周做的蔬菜水果查询图放出来。链接http://www.dadaaierer.com/fruit/

9703c312-e42d-4edc-a638-4c3eb422d05f

应该是从13年到现在每天的大众蔬菜和水果的价格图。如果筛选苹果会出:国光苹果等多个种类,还是选择一个开始查看。

原理很简单:每天爬新发地的蔬菜价格入库,然后展示。

前端使用的是百度出品的echarts JS插件。采用异步请求的方式。

感谢http://er.dadaaierer.com/对前端工作的支持!