东东东 陈煜东的博客

月份存档: 四月 2013

SQL内连接外连接区别

以前学过数据库基本原理的,但是许多地方没怎么用,都忘记了。对于SQL的一些知识也忘记了。

最被经常碰见的是(inner join)内连接和(outer join)外连接。这两个到底有什么区别呢?

我们看看两张表

表:A
nameage
John20
Smithnull
Marry19
表:B
nameaddress
JohnUS
SmithChina
nullUS
Tomnull

看看内连接的SQL句子

select * from A, B where A.name = B.name; --或者下面的句子
SELECT * from A INNER JOIN B where A.name = B.name; 
内连接的结果
nameagenameaddress
John20JohnUS
SmithnullSmithChina

内连接是返回两个表都存在的记录.(在连接的条件位置,null的不在结果中)。可以看出来,内连接是在取交集,需要两个表都出现的记录,并且符合where条件。

外连接

我看先看看左外连接的SQL语句

SELECT * from A left JOIN B on A.name=B.name;
左外连接的结果
nameagenameaddress
John20JohnUS
SmithnullSmithChina
Marry19nullnull

外连接是返回一个表(或两个表)的所有的记录(在连接的条件位置,null出现在结果中)。然后以左表为基础,列出符合左表的部分。

扩展阅读:SQL连接 – wikipedia

分类: 软件

nginx 反向代理 proxy

上次说了,有个需求,是使用nginx做反向代理用的。可以看看具体的nginx的安装。另外还有一篇介绍Squid的正向代理

基本的nginx反向代理设置

要配置一下nginx,进入到${nginx}/conf/nginx.conf,增加一个服务器。

增加一条server记录

server {
        listen 80;  # 端口
        server_name testproxy; # 域名

        location / {
                proxy_pass http://www.chenyudong.com/;  # 需要访问的网站
                proxy_redirect off;                 # 重定向关闭
                proxy_set_header Host $host;        # 设置一下访问头
        }
    }

注意:通过上面的方法,只是服务器简单的将文件下载下来,然后就未加工的提交给用户了。也就是里面的链接是不会出现替换的,原来是什么就该是什么。

那么要替换某些URL要怎么做的?

替换网页里某些关键字或链接

如果需要替换关键字或者链接,该怎么办呢?

我们需要重新编译一下nginx,对,重新编译。在编译的时候添加参数--with-http_sub_module。也就是编译的命令./configure --with-http_sub_module。然后的安装过程见这篇文章。

接下来需要修改一下${nginx}/conf/nginx.conf配置。

server {
        listen 80;  # 端口
        server_name testproxy; # 域名

        ##将页面出现的www.baidu.com改成google.com
        sub_filter 'www.baidu.com' 'google.com';

        ## off:在符合的地方,替换所有出现的地方。默认为on:只替换第一个出现的地方
        sub_filter_once off;

        location / {
                proxy_pass http://www.google.com/;  # 需要访问的网站
                proxy_redirect off;                 # 重定向关闭
                proxy_set_header Host $host;        # 设置一下访问头
        }
    }

需要关心用于的真实IP了

说明:这个用户的真实IP指的是,直接和我们服务器连接的那个IP,用户可以躲在代理后面,这种情况获取不到用户真实IP,想要获得,成本很高,还不一定能获得到。下文说的都是基于这个前提条件的。

以上配置,是多么美好啊,应该能正常工作了。但是还是有些不足,就是我们对用户真实IP的获取上,已经无法通过_SERVER['REMOTE_ADDR']获取,因为这个获取的值是前台的nginx的IP。

nginx反向代理

nginx反向代理

对于我们backend nginx代理,只能看到所有的请求是来自front nginx的,所以PHP程序想要获得用户的真实IP,需要做一些设置。

## front nginx 设置
server {
    listen          80;
    server_name     server.com;
    #access_log      /data/home/log/server.access.log main;

    location / {
        proxy_pass http://www.chenyudong.com/;
        proxy_redirect off;
        proxy_set_header X-real-ip $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

从HTTP_X_REAL_IP获取

通过增加HTTP_X_REAL_IPHTTP_X_FORWARDED_FOR这两个变量,让后端的backend nginx从这里获取用户的真实IP。

设置用户真实IP头的结果

设置用户真实IP头的结果

我们可以看到,PHP中的HTTP_X_REAL_IPHTTP_X_FORWARDED_FOR已经获取到用户的真实IP了。

这还不够完美,这需要我们的程序重新使用_SERVER["HTTP_X_REAL_IP"]这样来取值。但是程序不是从头写起的,大家都在使用_SERVER['REMOTE_ADDR']获取,要替换,工程浩大,不适宜。那么就只能在程序的入口做个变量的替换了。

<?php
    _SERVER['REMOTE_ADDR'] = _SERVER["HTTP_X_REAL_IP"];
....
?>

来个狠的,就要从REMOTE_ADDR获取

如果你觉得上面还是不太爽,我就是从那里取值。

在上面front nginx的配置基础上不变。这次我们对backend nginx做配置:

 location ~ ^.+\.php {
          fastcgi_pass 127.0.0.1:9000;
          fastcgi_index index.php;
          fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
          include fastcgi_params;
          fastcgi_param REMOTE_ADDR $HTTP_X_REAL_IP;
  }

就是高亮的第六行,添加fastcgi_param REMOTE_ADDR $HTTP_X_REAL_IP;,把REMOTE_ADDR这个变量在这里就给他替换了。看看替换的结果:

替换REMOTE_ADDR变量

替换REMOTE_ADDR变量

经过这么一折腾,我们的PHP就可以和以前一样工作了。

这样就好了吗?

如果你觉得这样,就可以了,那就错了。虽然我们的PHP正确获得了用户的IP,但是nginx要打access的log,看看他的log,你会发现请求的IP还是front nginx的那个IP。

要解决这个问题,我们就要再修改一把。并且这个解决方案比上一步来的更好一些。完全可以替换上一部的方案。

前端的front nginx的配置和前面一样。

## front nginx 设置
server {
    listen          80;
    server_name     server.com;
    #access_log      /data/home/log/server.access.log main;

    location / {
        proxy_pass http://www.chenyudong.com/;
        proxy_redirect off;
        proxy_set_header X-real-ip $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

后台的backend nginx要使用set_real_ip_fromreal_ip_header这两个指令,这两个是在nginx的HttpRealipModule中,这个模块默认不被编译,因此需要在编译是添加--with-http_realip_module参数

## 源码编译增加参数
./configure --with-http_realip_module

## yum 安装默认是有安装的,可以
nginx -V ##查看一下编译的参数

后端的backend nginx配置

## backend nginx
location ~ ^.+\.php {
          fastcgi_pass 127.0.0.1:9000;
          fastcgi_index index.php;
          fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
          include fastcgi_params;
          set_real_ip_from   10.221.0.197;
          #set_real_ip_from   100.100.0.0/16;
          #set_real_ip_from  2001:0db8::/32;
          real_ip_header     X-Real-IP;
}

第7行,set_real_ip_from定义了一个信任的IP地址,从这个信任的IP发送的可以替换用户的IP地址。如果值是unix,所有来自UNIX域的sockets都被信任。

IPv6 addresses are supported starting from versions 1.3.0 and 1.2.1.

第10行,real_ip_header定义了一个请求头,用于替换用户的真实IP。

这样的配置,我们的_SERVER['REMOTE_ADDR']还是用户的真实IP,而不是front nginx的IP。并且log里面的IP也是用户的真实IP。还等什么,赶快试一试吧。

参考资料:

分类: 网站建设

nginx编译安装教程

有个需求,要是用nginx的反向代理功能。以前有使用过lnmp,但是这些还用不上,就先自己安装一个单独的nginx服务器就好了。 下载nginx 首先从http://wiki.nginx.org/InstallChs下载源代码。我下载的是http://nginx.org/download/nginx-1.0.2.tar.gz wget http://nginx.org/download/nginx-1.0.2.tar.gz 解压缩安装

tar -zxvf nginx-1.0.2.tar.gz
./configure

出现问题。说

checking for PCRE library … not found checking for PCRE library in /usr/local/ … not found checking for PCRE library in /usr/include/pcre/ … not found checking for PCRE library in /usr/pkg/ … not found checking for PCRE library in /opt/local/ … not found ./configure: error: the HTTP rewrite module requires the PCRE library. You can either disable the module by using –without-http_rewrite_module option, or install the PCRE library into the system, or build the PCRE library statically from the source with nginx by using –with-pcre=<path> option.

运行yum命令

yum install pcre-devel

也可以参考安装教程http://www.asep.us/2011/05/30/nginx-instalation-pcre-library-not-found/ 出现gzip 类库

./configure: error: the HTTP gzip module requires the zlib library. You can either disable the module by using –without-http_gzip_module option, or install the zlib library into the system, or build the zlib library statically from the source with nginx by using –with-zlib=<path> option.

yum安装

yum install zlib zlib-devel

或者自己编译安装,下载地址:http://zlib.net/zlib-1.2.7.tar.gz 继续安装

你可能还会使用openssl组件,

yum install openssl openssl-devel
./configure --with-http_ssl_module --with-ipv6
make
make install

启动nginx /usr/local/nginx/sbin/nginx 关闭nginx /usr/local/nginx/sbin/nginx -s stop 几个常用的命令

  Nginx -s stop         快速关闭Nginx,可能不保存相关信息,并迅速终止web服务。(quick exit)
  Nginx -s quit         平稳关闭Nginx,保存相关信息,有安排的结束web服务。(graceful exit)
  Nginx -s reload       因改变了Nginx相关配置,需要重新加载配置而重载。(changing configuration,start a new worker,quitting an old worker gracefully.)
  Nginx -s reopen       重新打开日志文件。(reopenging log files)

分类: 网站建设

Java Singleton 单例模式

在设计模式中,单例模式(Singleton)是最长见得一种设计模式之一。什么是单例模式呢?就是在整个系统中,只有一个唯一存在的实例。这样的情况可以干什么用呢?比如可以统计网站的访问量,一些连接池(数据库连接池等)。

一个最简单的单例模式 – 饿汉模式

那么怎么能保证只有一个对象的存在呢?首先得有一个static的实例,这个方法保证了一个class只有一个实例。还得防止外界使用构造器来new一个实例。

//一个没有封装的单例模式
public class Singleton {
    public static final Singleton singleton = new Singleton();
    private Singleton(){}
}

外界就可以使用Singleton.singleton这样的方法来调用了。但是这样存在的问题就是分装不够好,添加一个方法,返回singleton的引用。如下

//最简单的封装单例改进版。饿汉模式
public class Singleton {
    public static final Singleton singleton = new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){ return singleton; }
}

当代码写到这,我们终于可以松一口气了,原来单例模式也很简单呀,就这么几行的代码。对,其实这个是最简单的一种方式,能够应付大部分的场景的。

不过,其他class在引用Singleton而不使用的时候,虚拟机会自动加载这个类,并且实例化这个对象(这点知道Java虚拟机的类加载就会了解那么一些)。于是我们就有了下面的写法。

延迟实例化(懒汉模式) – 在调用时进行实例化

经常能看见其他的单例模式会教下面的代码,这样的人估计是从《设计模式 – 可复用面向对象软件的基础》那本书看来的。这本书使用的是C++语言写的,然后就将其转到了Java平台来。

首先他们会说我们的代码应该先这样,在调用的时候,发现为null,再进行实例化该类。

public class Singleton {
    public static final Singleton singleton = null;
    private Singleton(){}
    public static Singleton getInstance(){
        if(singleton == null){ //如果singleton为空,表明未实例化
           singleton = new Singleton();
        }
        return singleton;
    }
}

然后他们还会说,这样在多线程的情况下会出现这样的情况:两个进程都进入到if (singleton == null),于是两个线程都对这个进行实例化,这样就出问题啦。

所以他们又说应该使用synchronize关键字,在实例化之前,进行加锁行为。于是又产生了一下的代码

public class Singleton {
    public static final Singleton singleton = null;
    private Singleton(){}
    public static Singleton getInstance(){
        if(singleton == null){ //如果singleton为空,表明未实例化
           synchronize (Singleton.class){
               if( singleton == null ) { // double check 进来判断后再实例化。
                   singleton = new Singleton(); 
               } 
        }
        return singleton;
    }
}

他们就会说,他们的这个代码使用了double check (双重检测),以防止这样的情况:当两个线程执行完第一个singleton == null 后等待锁, 其中一个线程获得锁并进入synchronize后,实例化了,然后退出释放锁,另外一个线程获得锁,进入又想实例化,会判断是否进行实例化了,如果存在,就不进行实例化了。

他们会说,这样的代码perfect,很完美,既解决了多线程带来的问题,又解决了延迟实例化的方式。

我觉得这样的代码只是将C++版的单例模式复制到Java平台,没有Java的特色。第一个就是一个Java特色的代码,它解决了多线程的问题,因为JLS(Java Language Specification)中规定了一个类(Singleton.class)只会被初始化一次,但是不能解决延迟实例化的情况。如需要延迟实例化,可以看下面的方法,使用内部类来实现。

使用内部类的单例模式 (懒汉模式)

刚才说了,第一个不能解决延迟实例化Singleton对象的问题。所以我们使用内部类来进行,看看代码。

//一个延迟实例化的内部类的单例模式
public final class Singleton {
    
    //一个内部类的容器,调用getInstance时,JVM加载这个类
    private static final class SingletonHolder {
        static final Singleton singleton =  new Singleton();
    }

    private Singleton() {}

    public static Singleton getInstance() {
        return SingletonHolder.singleton;
    }
 }

我们来看看这个代码。首先,其他类在引用这个Singleton的类时,只是新建了一个引用,并没有开辟一个的堆空间存放(对象所在的内存空间)。接着,当使用Singleton.getInstance()方法后,Java虚拟机(JVM)会加载SingletonHolder.class(JLS规定每个class对象只能被初始化一次),并实例化一个Singleton对象。

这样做就可以解决前面说的对线程多次实例化对象延迟实例化对象的问题了。

缺点:不过你会使用这样复杂的方式嘛?代码那么多。只是为了延迟一个对象的实例化,引入另外一个class。就为了延迟那么一次对象延迟的实例化,延缓Java的heap堆内存。为此付出的代价是引入一个class,需要在Java的另外一个内存空间(Java PermGen 永久代内存,这块内存是虚拟机加载class文件存放的位置)占用一个大块的空间。

还存在的一些问题

好了,在许多情况其实用第一种方法就差不多可以了。在特殊情况下,还是存在着一些问题。

用反射生成对象

如果使用Java的反射机制来生成对象的话,那么单例模式就会被破坏。

//使用反射破坏单例模式
Class c = Class.forName(Singleton.class.getName());  
Constructor constructor = c.getDeclaredConstructor();  
constructor.setAccessible(true);  
Singleton singleton = (Singleton)ct.newInstance();  

对于用反射破坏单例模式的,是不对其进行代码保护的,即由此造成的后果,由写反射的构建单例实例的人负责。所以我们就不用担心反射带来的问题了。

分布式上,解决单例模式

对于分布式上的单例模式,应该使用RMI(Remote Method Invocation 远程方法调用)来进行。或者使用web serivce,在单个服务器上存在单例,其他的机器使用SOAP协议进行访问。

不同的ClassLoader(类加载器)加载Singleton

在不同的ClassLoader加载Singleton,他们是不一样的。就像在不同的package中相同的类名,他们是不同的类。同样的,在不同的ClassLoader上加载的类,他们尽管代码一样,还是属于不同的类。

这样,需要自己写classloader,保证Singleton.class的加载唯一。

参考文章:http://www.oschina.net/question/9709_102019

单例模式的对象是否会被JVM回收?

对于这个问题,在还没实例化单例的时候,对象不存在,单实例化后,那么singleton的引用就存在的,只要Singleton.class存在虚拟机中。那么什么时候Singleton.class会被回收呢?对于这个问题,牵扯了许多的问题。因为Singleton对象存在,所以Singleton.class就也存在,这样形成了相互依赖,所以不会被JVM垃圾回收。

网上有个文章验证了他的想法 http://blog.csdn.net/zhengzhb/article/details/7331354

使用反序列化生成对象

如果你的Singleton序列化了,那么通过反序列化方式可以生成一个对象。通过增加readResolve方法来解决。如下

//最简单的封装单例改进版。饿汉模式。序列化及反序列化解决
public class Singleton implements java.io.Serializable {
    public static final Singleton singleton = new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){ return singleton; }
    private Object readResolve() {  return singleton;   }  
}

使用clone()克隆对象

Object的clone()方法默认是抛出CloneNotSupportedException异常的,所以只要不覆盖该方法,调用的时候,也会抛出异常。

effective java作者提到用enum枚举来实现单例模式。

扩展阅读:使用enum来进行Java的单例模式:http://coolxing.iteye.com/blog/1446648

分类: Java

sina weibo qq oauth2 login

resource owner
      An entity capable of granting access to a protected resource.
      When the resource owner is a person, it is referred to as an
      end-user.

   resource server
      The server hosting the protected resources, capable of accepting
      and responding to protected resource requests using access tokens.

   client
      An application making protected resource requests on behalf of the
      resource owner and with its authorization.  The term "client" does
      not imply any particular implementation characteristics (e.g.,
      whether the application executes on a server, a desktop, or other
      devices).

   authorization server
      The server issuing access tokens to the client after successfully
      authenticating the resource owner and obtaining authorization.

分类: 未分类

java struts 2伪静态url rewrite重写

在开发网页的过程中,需要改写(url rewrite)一些URL地址,避免暴露一些具体的文件夹位置。

比如使用http访问/archives/2.html,实际上是访问/post?id=2这个页面,但是后者对搜索引擎不好,而且将来如果想用/article?articleID=2来替换/post?id=2,这样需要对许多页面进行更改,不方便,搜索引擎的搜录也会减少。通过此方法还可以隐藏jsp后缀名。所以使用URL rewrite技术可以实现我们的这个需求。

如果你不知道什么是url重写这个概念,可以看看相关的资料。好了,我们继续。

在struts 2项目中,使用URL rewrite需要使用的UrlRewriteFilter这个类库,下载地址http://tuckey.org/urlrewrite/

官网上的步骤写的很清楚,先添加jar包,配置过滤器,添加url rewrite规则。

要注意是

1.在web.xml中配置filter过滤器,UrlRewriteFilter 要先写,struts后写。

2.在web.xml中struts的过滤器要这样写

<filter-mapping>
    <filter-name>struts2</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>INCLUDE</dispatcher>
</filter-mapping>

如果没有REQUESTINCLUDE,在映射到action的时候,会出现404 not found错误。如果是映射jsp文件,则不会发生错误。对于REQUEST和INCLUDE在页面使用一些东西的时候,会出现错误。比如异常The Struts dispatcher cannot be found或者 This is usually caused by using Struts tags without the associated filter。所以,当出现问题的时候,请务必加上REQUESTINCLUDE

我就因为这样的原因,郁闷了一段时间,redirect可以工作,forward却不能工作。

3.在urlrewrite.xml中,对应的规则,如果是接收的参数有多个,不用&,而是使用&amp;进行分开。

例如,应该这样写

<rule>
     <from>/products/([0-9]+)/([0-9]+).html</from>
     <to>/products/index.jsp?product_category=$1&amp;id=$2</to>
</rule>

分类: 网站建设

2013年9月 GoDaddy域名优惠码

GoDaddy是一家提供域名注册和互联网主机服务的美国公司,是目前全球最大的域名注册商。据多家监测机构显示,放置在Go Daddy上的网站数量已经位居全球第一位。Go Daddy同时提供独立IP,SSL证书。

国内许多人的域名是在它上面注册的,因为优惠码很多。国内注册的域名随时可能出现问题。门户网站的域名也是在国外注册的。

com org等域名优惠码

注意:域名注册费需要增加ICANN Fees: $0.18 这是ICANN的规定,这个钱要给ICANN的。

$7.99 .CO/COM 新注册域名 cjc749chp
5个域名,每个$5.99 .Com! iap599v1
域名com续费44%优惠 BACON3

Godaddy SSL Certificates 证书

We’ve Got Your Site Protected! $5.99 SSL Sale! iap599s

分类: 网站建设

校企合作

当今,校企合作的案例是很多的。高校与企业之间合作,增加企业的优势,也增加学生与实际企业接轨,使学生了解行业的运行模式。

有许多校办企业是高校自己投资建设的。校办企业通过在自己本校里招生,提供更高的起薪,对外可以说自己的本科生达到了90%以上,硕士生达到了多少多少等。与某某高校有着密切的合作,以某某高校的技术力量为依托等。这类的校企的确有很多的优势,因为老师不上班的时候,就在校企里工作。而且学生可以很早就跟着老师做项目,老师有了研究成果,就可以将研究成果转换成产品进行出售。或者通过与其他企业合作,其他企业如果需要什么产品,找专门的研究老师进行研究,进行定制产品。只是现在的校企挺盘大的,因为许多老师都加入了校企,使得更方面的人才都有,只是老师别光想着赚钱了,好好带学生才是主线。

还有就是外面的企业与学校合作,这类企业有大型的也有小型的,大型的比较少,一般是研究型的。大部分都是中小型企业,这类企业通过与学校建立合作,可以提高自身企业的水平,对外也可以说我们与某某高校有着密切的合作,甚至还可能夸大事实。这类公司,可以将简单的工作交给学校里的学生来进行,让学生帮公司完成简单的代码功能,并给学生或者学校一些钱,这样可以通过廉价的价格完成一些基本的软件功能,公司的员工就可以完成更高级的功能。如果学生在开发工程中,表现得好,那么毕业后可以直接到公司里工作。只是我觉得如果真的平时动手能力强的学生,毕业后还真不一定会去这样的公司,应该会去大的、好的公司才对。

对了,企业还有可能进入校董会。企业给学校出谋划策,筹资献力。而企业享受的权利是:优先挑选各类优秀毕业生,优惠获得学校提供的各类人才开发和培训项目;利用大学的优势,开展国际学术交流合作与高层次人才培养;优先获得大学新技术、新产品、新工艺的转让权,共同进行技术攻关和创新;要求学校提供长期、稳定的科技服务与支持。

总之,别让我们的学生成为利益的牺牲者,让高校多多培养一批人才,培育建设祖国的栋梁,为社会输送人才。

分类: 生活

Copyright © 2017 东东东 陈煜东的博客 粤ICP备13059639号-1

SITEMAP回到顶部 ↑