0
Posted on Tuesday, June 30, 2020 by 醉·醉·鱼 and labeled under
前任留下了一个祖传代码,看着挺好的,通过一个build方法,就可以动态创建一个新的类。等到自己去实现第二个类的时候,出了问题。say方法被覆盖了。第一感觉就是,类被重新打开,然后被覆写了。

后来,在尝试打印self的时候,发现block是在顶级作用域main里面执行的。导致的结果就是,第二次会覆盖第一次的方法定义,而且所有地方都有这个方法,无论是类方法,还是实例方法。
问题到这里,就是切换context的问题了。用class_eval可以切换context到新建的类执行代码块,进而定义实例方法。

参考 https://stackoverflow.com/questions/19319138/dynamically-create-a-class-inherited-from-activerecord
0
Posted on Tuesday, June 16, 2020 by 醉·醉·鱼 and labeled under
看完http://sakurity.com/blog/2015/02/28/openuri.html,赶紧看一下自己的项目。还好还好,只有内部项目在用。要不回头黑客上传一个文件,然后再调用命令执行一下,简直不能够再欢喜了。
0
Posted on Thursday, March 12, 2020 by 醉·醉·鱼 and labeled under , ,

问题

支持访问http://localhost和https://localhost,把80和443端口都转发到Nginx server,通过Nginx server再转发给Rails App。

解决

首先搭建一个F5的load balance,把所有的request都转发到8680端口。
再搭建一个Nginx server,模拟服务器上面的80端口。把所有的request都转发到3000端口。

其实这个Nginx server有些鸡肋,本来就可以直接把F5转发到3000端口的,但是一般Rails都是搭配Nginx的,就单纯再模拟一次Nginx。

步骤

创建self-trusted cert on host machine
openssl req -x509 -sha256 -nodes -newkey rsa:2048 -days 365 -keyout localhost.key -out localhost.crt


docker-compose.yml
version: '2'
services:
  f5: 
    image: nginx:latest
    volumes:
      - ./tmp/nginx/f5_https_pool.conf:/etc/nginx/conf.d/f5_https_pool.conf
      - ./tmp/nginx/f5_http_pool.conf:/etc/nginx/conf.d/default.conf
      - ./tmp/ssl:/etc/nginx/ssl
    ports:
      - 80:80
      - 443:443

  nginx: 
    image: nginx:latest
    volumes:
      - ./tmp/nginx/nginx_server.conf:/etc/nginx/conf.d/default.conf
    ports:
      - 8680:8680


tmp/nginx/nginx_server.conf
upstream rails {  
   server host.docker.internal:3000;
}

server {
  listen 8680 default_server;

  server_name localhost;

  location / {    
    proxy_set_header X-Real-IP $remote_addr;    
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;         
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
    proxy_redirect off;
    proxy_pass http://rails;  
  }
}


tmp/nginx/f5_http_pool.conf
upstream nginx_server {  
   server host.docker.internal:8680;
}

server {
  listen 80;

  server_name localhost;
  
  # return 301 https://$host$request_uri;

  location / {    
    proxy_set_header X-Real-IP $remote_addr;    
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;         
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-Proto http;
    proxy_redirect off;
    proxy_pass http://nginx_server;  
  }
}

tmp/nginx/f5_https_pool.conf
server {
  listen 443 ssl default_server;

  server_name localhost;
  
  ssl_certificate /etc/nginx/ssl/localhost.crt;
  ssl_certificate_key /etc/nginx/ssl/localhost.key;
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";

  location / {    
    proxy_set_header X-Real-IP $remote_addr;    
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;         
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-Proto https;
    proxy_redirect off;
    proxy_pass http://nginx_server;  
  }
}
0
Posted on Wednesday, September 18, 2019 by 醉·醉·鱼 and labeled under

《流畅的python》一书中有一小节提到过,不要使用可变类型作为参数的默认值。 碰巧自己刷题的时候没有意识到这个,成功踩雷。在python里面,变量都是对象的引用而已。在下面的例子中,修改一个变量,另外一个变量也会变,因为他们其实是同一个对象。



def f(v = []):
    return v

a = f()
b = f()

a.append(1)

print(a == b) # don't use mutable obj as default param
print(a is b)

c = f()
d = f()

c += [1]
print(id(c))
print(id(d))
print(c == d)
print (c is d)


虽然说可以用 a = a + [1] 来避免,但还是不要用可变类型作为参数默认值。
0
Posted on Friday, June 28, 2019 by 醉·醉·鱼 and labeled under ,
最近在刷python的题,遇到经典的银行提款的问题。做完以后,想在Ruby上面也实验一番,进而发现了更多好玩的知识点,略微整理一下。

问题回顾


从银行账户里面取钱和存钱,多线程操作,看是否会导致账户余额出错。按道理来说,最后应该还是1000块钱。


class BankAccount(object):

    def __init__(self):
        self.balance = 0
        pass

    def withdraw(self, amount):           
        self.balance -= amount

    def deposit(self, amount):
        self.balance += amount



    def test_can_handle_concurrent_transactions(self):
        account = BankAccount()
        account.open()
        account.deposit(1000)

    self.adjust_balance_concurrently(account)

    self.assertEqual(account.get_balance(), 1000)

    def adjust_balance_concurrently(self, account):
        def transact():
            account.deposit(5)
            time.sleep(0.001)
            account.withdraw(5)

        # Greatly improve the chance of an operation being interrupted
        # by thread switch, thus testing synchronization effectively
        try:
            sys.setswitchinterval(1e-12)
        except AttributeError:
        # For Python 2 compatibility
            sys.setcheckinterval(1)

        threads = [threading.Thread(target=transact) for _ in range(1000)]
        for thread in threads:
            thread.start()
        for thread in threads:
            thread.join()

结果,上面这个python测试代码通常是失败的。根据https://opensource.com/article/17/4/grok-gil,`+=`不是原子性操作,中间可以GIL切换到其他线程上面去操作了,进而导致问题。要想避免这个问题,引入lock即以。



lock = threading.Lock()
class BankAccount(object):

    def __init__(self):
        self.balance = 0
        pass

    def withdraw(self, amount):           
        with lock:
            self.balance -= amount

    def deposit(self, amount):
        with lock:
            self.balance += amount


但是,Ruby代码却不会有这样的问题。根据https://www.jstorimer.com/blogs/workingwithcode/8100871-nobody-understands-the-gil-part-2-implementation,all methods in MRI are atomic. 所有操作都是原子性,没法在分割的,即不会出现中间GIL 切换到其他现成上面去。



class BankAccount
  attr_accessor :balance

  def initialize
    @balance = 0
    @semapore = Mutex.new
  end
  
  def deposit(amount)
    @balance += amount
  end

  def withdraw(amount)
    @balance -= amount
  end
  
end


account = BankAccount.new
account.deposit(1000)

threads = []

1000.times do
  threads << Thread.new do
    account.withdraw(1)
  end
end

threads.each(&:join)

p account.balance

那,有没有办法在Ruby里面去模拟这种情况呢?有,而且应用还颇为广泛。
如下面这段代码,先把balance缓存起来,然后sleep,再做操作,就会出现GIL切换线程的现象,进而出现线程安全问题。这种情况在很多地方都会出现,中间的sleep常常表现为系统进行其他的操作,但时间不可预知。回过头再把缓存的数据进行操作的时候,其他线程早就已经更改了。这种情况最典型的例子就是秒杀时候inventory的问题。

将注释去掉,引入`semapore.synchronize`即可。


  def withdraw(amount)
    # @semapore.synchronize do
      balance = @balance
      sleep rand(10) / 10000.0
      @balance = balance - amount
    # end
  end

0
Posted on Friday, November 09, 2018 by 醉·醉·鱼 and labeled under
You may expect IE will redirect 302 for POST to a GET request. The truth is IE does it. However, the default developer tools sucks.

As you could see from the screenshot below, you will always see POST after 302 response in IE developer tools.

If you see details for these request, you will see both POST and GET requests. How could it be?


While, if you use other network monitor tools to monitor the network, you will see only GET request.


It will bring developers into wrong directions as they would think it's the HTTP method error. For example: https://stackoverflow.com/questions/28513449/ie-ignores-303-redirect-in-post-redirect-get-scenario 

And I would share another IE bug. If there is underscore in your domain, no cookies will be set.

See https://blogs.msdn.microsoft.com/ieinternals/2009/08/20/internet-explorer-cookie-internals-faq/