ruby gserver源码阅读

gserver作为通用服务器的实现,最关键的就是start方法

def start(maxConnections = -1)
  raise "running" if !stopped?
  @shutdown = false
  @maxConnections = maxConnections if maxConnections > 0
  @@servicesMutex.synchronize  {
    if GServer.in_service?(@port,@host)
      raise "Port already in use: #{host}:#{@port}!"
    end
    @tcpServer = TCPServer.new(@host,@port)
    @port = @tcpServer.addr[1]
    @@services[@host] = {} unless @@services.has_key?(@host)
    @@services[@host][@port] = self;
  }
  @tcpServerThread = Thread.new {
    begin
      starting if @audit
      while !@shutdown
        @connectionsMutex.synchronize  {
           while @connections.size >= @maxConnections
             @connectionsCV.wait(@connectionsMutex)
           end
        }
        client = @tcpServer.accept
        @connections << Thread.new(client)  { |myClient|
          begin
            myPort = myClient.peeraddr[1]
            serve(myClient) if !@audit or connecting(myClient)
          rescue => detail
            error(detail) if @debug
          ensure
            begin
              myClient.close
            rescue
            end
            @connectionsMutex.synchronize {
              @connections.delete(Thread.current)
              @connectionsCV.signal
            }
            disconnecting(myPort) if @audit
          end
        }
      end
    rescue => detail
      error(detail) if @debug
    ensure
      begin
        @tcpServer.close
      rescue
      end
      if @shutdown
        @connectionsMutex.synchronize  {
           while @connections.size > 0
             @connectionsCV.wait(@connectionsMutex)
           end
        }
      else
        @connections.each { |c| c.raise "stop" }
      end
      @tcpServerThread = nil
      @@servicesMutex.synchronize  {
        @@services[@host].delete(@port)
      }
      stopping if @audit
    end
  }
  self
end

第5-13行检查host的port是否被占用,如果没有的话,则根据host和port实例化TCPServer,并通过@@services[@host][@port]记录。

第14-65行将服务器的服务交由一个单独的线程处理,这样当当前进程退出的时候,该线程也会被强制退出。

第18-22行和第35-38行是用来管理线程池的,当要加入一个新的线程之前,检查当前的线程数量,如果达到线程数最大值,则挂起当前线程;当删除一个线程时,通知某个被挂起的线程继续执行。

第23-41行是一个tcp服务的全过程,获取client的连接,启动一个线程来处理当前的连接,执行serve方法,最终关闭当前的连接。

第46-63行是tcp服务器关闭时执行的过程,服务器关闭有两种方法:

一是shutdown,tcp服务器会等待所有的线程都执行完毕再关闭

二是close,tcp服务器会中断所有的线程,强制关闭

其它的方法,如shutdown, close都写得很简单,不再一一叙述。

Posted in  ruby


ruby通用服务器gserver

之前随手翻了几页Programming Ruby 1.9,看到了gserver,查看ruby-doc,原来gserver是一个通用服务器的实现,提供了线程池管理,简单的日志和多服务器管理。

先看看怎么使用吧

require 'gserver'

class TimeServer < GServer
  def initialize(port=10001, *args)
    super(port, *args)
  end

  def serve(io)
    io.puts(Time.now.to_i)
  end
end

server = TimeServer.new
server.audit = true

['INT', 'TERM'].each { |signal|
  trap(signal) { server.stop }
}
server.start.join

上面这段代码就是一个简单的tcp服务器,返回系统当前时间与1970年之间的秒数差。

其中3-11行是定义服务器,继承GServer,必须实现serve方法,来实现服务器的行为。

14行启动服务器的日志功能。

16-18行定义当前进程收到中断信号时,关闭服务器。

最后一行,启动服务器,并且保持当前进程active,直到服务器线程被关闭为止。

下一章将介绍gserver的源代码

Posted in  ruby


enable ssl for tomcat

昨天参加barcamp会议,介绍了contactlist,有不少人质疑服务的安全性,希望使用https来增强安全性。

想起研究生阶段研究的就是web service安全,https属于最简单的实现,赶紧加上吧。

首先是在本地生成keystore,

keytool -genkey -alias tomcat -keyalg RSA

按照提示,输入密码,姓名等等信息,就会在HOME目录下生成.keystore文件,其中包括了你的公私钥。

接下来,就是开启tomcat的8443端口,

<connector port="8443" protocol="HTTP/1.1" sslenabled="true" maxthreads="150" scheme="https" secure="true" keystorefile="/home/flyerhzm/.keystore" keystorepass="changeit" clientauth="false" sslprotocol="TLS"></connector>

最后重启tomcat,并将访问地址由原来的http://mysite.com:8080改成https://mysite.com:8443即可。

Posted in  java web


oauth for twitter

twitter是Twitter的ruby gem,它提供了两种身份验证的方法,一是oauth,二是http auth。http auth非常简单,只要提供账号和密码就可以了,而oauth就稍微复杂一些了。

首先,你需要到http://twitter.com/oauth_clients去注册你的应用,并得到相应的consumer token和consumer secret。

接着就可以使用twitter gem了

def oauth
  oauth = Twitter::OAuth.new(consumer_token, consumer_secret)
  request_token = oauth.set_callback_url 'http://www.huangzhimin.com/'
  session[:rtoken] = request_token.token
  session[:rsecret] = request_token.secret

  redirect_to request_token.authorize_url
end

注意,这里除了传入consumer_token和consumer_secret之外,还设置了callback_url,它表示twitter身份验证完毕之后返回的页面。

然后就是跳转到authorize_url,你会进入twitter的验证页面,点击Allow之后,将跳转到之前设置的callback_url页面

def callback
  oauth = Twitter::OAuth.new(consumer_token, consumer_secret)
  oauth.authorize_from_request(session[:rtoken], session[:rsecret], params[:oauth_verifier])
  session[:rtoken] = nil
  session[:rsecret] = nil
  session[:atoken] = oauth.access_token.token
  session[:asecret] = oauth.access_token.secret
end

可以看到通过返回的oauth_verifier,我们就完成了twitter的身份验证,在记录了session[:atoken]和session[:asecret]之后就可以使用twitter的提供的接口,比如

oauth = Twitter::OAuth.new(consumer_token, consumer_secret)
oauth.authorize_from_access(session[:atoken], session[:asecret])
client = Twitter::Base.new(oauth)
client.update('twitter oauth test')

Posted in  rubygems twitter ruby


routing-filter

routing-filter提供了强大的用于扩展Rails routing的功能。

来看看两个例子

  1. 分页,一般使用经典的will_paginate插件生成的url是这样的: /posts?page=2,不过/posts/page/2如何呢?

  2. locale,比如我们根据用户不同的locale,显示不同语言版本的页面,这样的url很可能是/posts?locale=zh,不过/zh/posts如何呢?

我个人都比较倾向于后面一种写法。用routing-filter可以帮助我们快速地完成这个转换工作。

routing-filter默认自带了以上两种url的转换器。用起来也是超级简单,只要在config/routes.rb文件中定义

ActionController::Routing::Routes.draw do |map|
  map.filter 'pagination'
  ...
end
ActionController::Routing::Routes.draw do |map|
  map.filter 'locale'
  ...
end

你也可以很容易的创造自己的url转换器

module RoutingFilter
  class Awesomeness < Base
    def around_recognize(route, path, env)
      # Alter the path here before it gets recognized.
      # Make sure to yield (calls the next around filter if present and
      # eventually `recognize_path` on the routeset):
      returning yield do |params|
        # You can additionally modify the params here before they get passed
        # to the controller.
      end
    end

    def around_generate(controller, *args, &block)
      # Alter arguments here before they are passed to `url_for`.
      # Make sure to yield (calls the next around filter if present and
      # eventually `url_for` on the controller):
      returning yield do |result|
        # You can change the generated url_or_path here. Make sure to use
        # one of the "in-place" modifying String methods though (like sub!
        # and friends).
      end
    end
  end
end

感觉这东西对于seo非常有用

Posted in  rails plugins


Fork me on GitHub