鹅厂企业文化

愿景(VISION):最受尊敬的互联网企业

  • 不断倾听和满足用户需求,引导并超越用户需求,赢得用户尊敬;
  • 通过提升企业地位与品牌形象,使员工具有高度的企业荣誉感和自豪感,赢得员工尊敬;
  • 推动互联网行业的健康发展,与合作伙伴共同成长,赢得行业尊敬;
  • 注重企业责任,关爱社会、回馈社会,赢得社会尊敬。

使命(MISSION):通过互联网服务提升人类生活品质

  • 使产品和服务像水和电一样源源不断融入人们的生活,为人们带来便捷和愉悦;
  • 关注不同地域、不同群体,并针对不同对象提供差异化的产品和服务;
  • 打造开放共赢平台,与合作伙伴共同营造健康的互联网生态环境。

价值观(VALUES):正直、进取、合作、创新

正直 :
  • 遵守国家法律与公司制度,绝不触犯企业高压线;
  • 做人德为先,坚持公正、诚实、守信等为人处事的重要原则;
  • 用正直的力量对周围产生积极的影响。
进取
  • 尽职尽责,高效执行;
  • 勇于承担责任,主动迎接新的任务和挑战;
  • 保持好奇心,不断学习,追求卓越。
合作
  • 具有开放共赢心态,与合作伙伴共享行业成长;
  • 具备大局观,能够与其他团队相互配合,共同达成目标;
  • 乐于分享专业知识与工作经验,与同事共同成长。
创新
  • 创新的目的是为用户创造价值;
  • 人人皆可创新,事事皆可创新;
  • 敢于突破,勇于尝试,不惧失败,善于总结。

我们的经营理念: 一切以用户价值为依归

  • 注重长远发展,不因商业利益伤害用户价值;
  • 关注并深刻理解用户需求,不断以卓越的产品和服务满足用户需求;
  • 重视与用户的情感沟通,尊重用户感受,与用户共成长。

我们的管理理念:关心员工成长

  • 为员工提供良好的工作环境和激励机制;
  • 完善员工培养体系和职业发展通道,使员工获得与企业同步成长的快乐;
  • 充分尊重和信任员工,不断引导和鼓励,使其获得成就的喜悦。

(杂篇)几个概念说明

用户空间与内核空间


现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方)。操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。为了保证用户进程不能直接操作内核(kernel),保证内核的安全,操心系统将虚拟空间划分为两部分,一部分为内核空间,一部分为用户空间。针对linux操作系统而言,将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为内核空间,而将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF),供各个进程使用,称为用户空间。

进程切换


为了控制进程的执行,内核必须有能力挂起正在CPU上运行的进程,并恢复以前挂起的某个进程的执行。这种行为被称为进程切换。因此可以说,任何进程都是在操作系统内核的支持下运行的,是与内核紧密相关的。

从一个进程的运行转到另一个进程上运行,这个过程中经过下面这些变化:
1. 保存处理机上下文,包括程序计数器和其他寄存器。
2. 更新PCB信息。
3. 把进程的PCB移入相应的队列,如就绪、在某事件阻塞等队列。
4. 选择另一个进程执行,并更新其PCB。
5. 更新内存管理的数据结构。
6. 恢复处理机上下文。

注:总而言之就是很耗资源,具体的可以参考这篇文章:进程切换

进程的阻塞


正在执行的进程,由于期待的某些事件未发生,如请求系统资源失败、等待某种操作的完成、新数据尚未到达或无新工作做等,则由系统自动执行阻塞原语(Block),使自己由运行状态变为阻塞状态。可见,进程的阻塞是进程自身的一种主动行为,也因此只有处于运行态的进程(获得CPU),才可能将其转为阻塞状态。当进程进入阻塞状态,是不占用CPU资源的

文件描述符fd


文件描述符(File descriptor)是计算机科学中的一个术语,是一个用于表述指向文件的引用的抽象化概念。

文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。

缓存 I/O


缓存 I/O 又被称作标准 I/O,大多数文件系统的默认 I/O 操作都是缓存 I/O。在 Linux 的缓存 I/O 机制中,操作系统会将 I/O 的数据缓存在文件系统的页缓存( page cache )中,也就是说,数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。

缓存 I/O 的缺点:
数据在传输过程中需要在应用程序地址空间和内核进行多次数据拷贝操作,这些数据拷贝操作所带来的 CPU 以及内存开销是非常大的。

进程间通信的方式——信号、管道、消息队列、共享内存

    1. 匿名管道pipe:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
    2. 命名管道FIFO:有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
    3. 消息队列MessageQueue:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
    4. 共享存储SharedMemory:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
    5. 信号量Semaphore:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
    6. 套接字Socket:套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同主机间的进程通信。
    7. 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。

注意:在第6点socket的基础上发展出一种IPC机制,就是UNIX Domain Socket。虽然网络socket也可用于同一台主机的进程间通讯(通过loopback地址127.0.0.1),但是UNIX Domain Socket用于IPC更有效率:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。这是因为,IPC机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的。

UNIX Domain Socket也提供面向流面向数据包两种API接口,类似于TCP和UDP,但是面向消息的UNIX Domain Socket也是可靠的,消息既不会丢失也不会顺序错乱

怎样理解阻塞非阻塞与同步异步的区别?

“阻塞”与”非阻塞”与”同步”与“异步”不能简单的从字面理解,提供一个从分布式系统角度的回答。
1.同步与异步
同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)
所谓同步,就是在发出一个*调用*时,在没有得到结果之前,该*调用*就不返回。但是一旦调用返回,就得到返回值了。
换句话说,就是由*调用者*主动等待这个*调用*的结果。

而异步则是相反,*调用*在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在*调用*发出后,*被调用者*通过状态、通知来通知调用者,或通过回调函数处理这个调用。

典型的异步编程模型比如Node.js

举个通俗的例子:
你打电话问书店老板有没有《分布式系统》这本书,如果是同步通信机制,书店老板会说,你稍等,”我查一下”,然后开始查啊查,等查好了(可能是5秒,也可能是一天)告诉你结果(返回结果)。
而异步通信机制,书店老板直接告诉你我查一下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。然后查好了,他会主动打电话给你。在这里老板通过“回电”这种方式来回调。

2. 阻塞与非阻塞
阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.

阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。

还是上面的例子,
你打电话问书店老板有没有《分布式系统》这本书,你如果是阻塞式调用,你会一直把自己“挂起”,直到得到这本书有没有的结果,如果是非阻塞式调用,你不管老板有没有告诉你,你自己先一边去玩了, 当然你也要偶尔过几分钟check一下老板有没有返回结果。
在这里阻塞与非阻塞与是否同步异步无关。跟老板通过什么方式回答你结果无关。

作者:严肃
链接:https://www.zhihu.com/question/19732473/answer/20851256
来源:知乎

(原创)关于Tomcat默认最大占用内存的一系列问题

今天舍友问我tomcat啥都不改的话,内存最大能占用到多少,我脱口而出四分之一物理内存,然而事实真的是这样吗?

1、查阅官方说明文档

JDK文档传送门,当然全是英文看起来需要一点时间,所以我找来了别人的大概翻译

client模式下,JVM初始和最大堆大小为:
在物理内存达到192MB之前,JVM最大堆大小为物理内存的一半,否则,在物理内存大于192MB,在到达1GB之前,JVM最大堆大小为物理内存的1/4,大于1GB的物理内存也按1GB计算,举个例子,如果你的电脑内存是128MB,那么最大堆大小就是64MB,如果你的物理内存大于或等于1GB,那么最大堆大小为256MB。
Java初始堆大小是物理内存的1/64,但最小是8MB。

server模式下:
与client模式类似,区别就是默认值可以更大,比如在32位JVM下,如果物理内存在4G或更高,最大堆大小可以提升至1GB,,如果是在64位JVM下,如果物理内存在128GB或更高,最大堆大小可以提升至32GB。

2、实践是校验真理的唯一标准

在Linux下,我们可以通过以下命令来查看堆内存的默认最大值

java -XX:+PrintFlagsFinal -version | grep MaxHeapSize

输出如下:

上图是我在2G RAM的虚拟机里的结果,可以看到522190848 Byte = 498 MB,而这虚拟机除了给硬件保留的内存外总内存空间为 2039552 KB ,其四分之一也就是509888KB= 497MB,可以说理论和实际值非常接近,误差可忽略。

然而在Windows上,我没有通过命令行去找出最大堆,而是选择了写个Java类来获取。

package test;

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;

public class MaxHeapTest {
    public static void main(String[] args) {
        MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();  
        MemoryUsage heapMemory= memoryMXBean.getHeapMemoryUsage();  
        long maxHeap = heapMemory.getMax();
        // 单位:字节
        System.out.println(maxHeap +" byte");
    }
}

输出为:

有趣的地方来了,根据换算,3771203584 byte = 3596.5 MB,而我windows的机器上除去硬件保留的内存,可用的有 16178MB,其四分一为 4044.5 MB,和上图的输出值存在不可忽略的大小差异,难道是windows系统特别些?我们继续探索。

3、探索、求真

上述的获取方式不一样,我们先来统一下方式,

在CMD下以同样的方式打印JVM参数,找MaxHeapSize字样:

其中,4242538496 byte = 4046MB,嗯,这个值就才对嘛。

那么现在的问题转变成了,为啥通过java编程方式获取到的最大堆内存会和真实值有出入的?小弟不才,没有了进一步探索的毅力,只想站在前辈的肩膀上看看风景。

这里是别人留下的宝贵经验:传送门2
经过计算和检验,的确差了一个Survivor区的大小,加上这个也就对得上了。

引用别人的结论:通过MemoryMXBean 获取的最大堆内存,应该是JVM可以使用的最大堆内存。因为两个幸存区,采用的是标记拷贝的GC算法,实际分配时,永远有一个幸存区是不能被使用的,所以最大堆内存没有包含这一部分

结尾:

以上结论仅针对Hot Spot虚拟机,其它JVM请自行实验。

本文参考链接:https://segmentfault.com/q/1010000007235579?_ea=1280245

进程、线程与协程的比较

进程、线程和协程是三个在多任务处理中常听到的概念,三者各有区别又相互联系。

进程

进程是一个程序在一个数据集中的一次动态执行过程,可以简单理解为“正在执行的程序”,它是CPU资源分配和调度的独立单位。
进程一般由程序、数据集、进程控制块三部分组成。我们编写的程序用来描述进程要完成哪些功能以及如何完成;数据集则是程序在执行过程中所需要使用的资源;进程控制块用来记录进程的外部特征,描述进程的执行变化过程,系统可以利用它来控制和管理进程,它是系统感知进程存在的唯一标志。
进程的局限是创建、撤销和切换的开销比较大。

线程

线程是在进程之后发展出来的概念。 线程也叫轻量级进程,它是一个基本的CPU执行单元,也是程序执行过程中的最小单元,由线程ID、程序计数器、寄存器集合和堆栈共同组成。一个进程可以包含多个线程。
线程的优点是减小了程序并发执行时的开销,提高了操作系统的并发性能,缺点是线程没有自己的系统资源,只拥有在运行时必不可少的资源,但同一进程的各线程可以共享进程所拥有的系统资源,如果把进程比作一个车间,那么线程就好比是车间里面的工人。不过对于某些独占性资源存在锁机制,处理不当可能会产生“死锁”。

协程

协程是一种用户态的轻量级线程,又称微线程,英文名Coroutine,协程的调度完全由用户控制。人们通常将协程和子程序(函数)比较着理解。
子程序调用总是一个入口,一次返回,一旦退出即完成了子程序的执行。
协程的起始处是第一个入口点,在协程里,返回点之后是接下来的入口点。在python中,协程可以通过yield来调用其它协程。通过yield方式转移执行权的协程之间不是调用者与被调用者的关系,而是彼此对称、平等的,通过相互协作共同完成任务。其运行的大致流程如下:

第一步,协程A开始执行。 
第二步,协程A执行到一半,进入暂停,通过yield命令将执行权转移到协程B。 
第三步,(一段时间后)协程B交还执行权。 
第四步,协程A恢复执行。

协程的特点在于是一个线程执行,与多线程相比,其优势体现在:

  1. 协程的执行效率非常高。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。
  2. 协程不需要多线程的锁机制。在协程中控制共享资源不加锁,只需要判断状态就好了。

Tips : 利用多核CPU最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。

对象与对象之间的四种关系

  • 依赖关系(Dependency)
    所谓依赖就是某个对象的功能依赖于另外的某个对象,而被依赖的对象只是作为一种工具在使用,而并不持有对它的引用
    举例:一个人自创生就需要不停的呼吸,而人的呼吸功能之所以能维持生命就在于吸进来的气体发挥了作用,所以说空气只不过是人类的一个工具,而人并不持有对它的引用。
  • 关联关系(Association)
    某个对象会长期的持有另一个对象的引用,而二者的关联往往也是相互的。
    关联的两个对象彼此间没有任何强制性的约束,只要二者同意,可以随时解除关系或是进行关联,
    它们在生命期问题上没有任何约定。被关联的对象还可以再被别的对象关联,所以关联是可以共享的。
    举例:人从生至死都在不断的交朋友,然而没有理由认为朋友的生死与我的生死有必然的联系,故他们的生命期没有关联,我的朋友又可以是别人的朋友,所以朋友可以共享。
  • 聚合关系(Aggregation)
    聚合是强版本的关联。
    它暗含着一种所属关系以及生命期关系。
    被聚合的对象还可以再被别的对象关联,所以被聚合对象是可以共享的。
    虽然是共享的,聚合代表的是一种更亲密的关系。
    举例:我的家和我之间具有着一种强烈的所属关系,
    我的家是可以分享的,而这里的分享又可以有两种。
    其一是聚合间的分享,这正如你和你媳妇儿都对这个家有着同样的强烈关联;
    其二是聚合与关联的分享,如果你的朋友来家里吃个便饭,估计你不会给他配一把钥匙。
  • 组合关系(Compostion)
    组合是关系当中的最强版本,它直接要求包含对象对被包含对象的拥有以及包含对象与被包含对象生命期的关系。
    被包含的对象还可以再被别的对象关联,
    所以被包含对象是可以共享的,
    然而绝不存在两个包含对象对同一个被包含对象的共享。
    举例:组合关系就是整体与部分的关系,部分属于整体,整体不存在,部分一定不存在,然而部分不存在整体是可以存在的,说的更明确一些就是部分必须创生于整体创生之后,而销毁于整体销毁之前。

Spring声明式事务管理

在 spring 中一共定义了六种事务传播属性

  • PROPAGATION_REQUIRED — 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
  • PROPAGATION_SUPPORTS — 支持当前事务,如果当前没有事务,就以非事务方式执行。
  • PROPAGATION_MANDATORY — 支持当前事务,如果当前没有事务,就抛出异常。
  • PROPAGATION_REQUIRES_NEW — 新建事务,如果当前存在事务,把当前事务挂起。
  • PROPAGATION_NOT_SUPPORTED — 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • PROPAGATION_NEVER — 以非事务方式执行,如果当前存在事务,则抛出异常。
  • PROPAGATION_NESTED — 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。

前六个策略类似于EJB CMT,第七个(PROPAGATION_NESTED)是Spring所提供的一个特殊变量。
它要求事务管理器或者使用JDBC 3.0 Savepoint API提供嵌套事务行为(如Spring的DataSourceTransactionManager)