<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                # 8. actioncable 進階 #### 1. 介紹 上一篇講了actioncable的基本使用,也搭建了一個簡易的聊天室。但是只是照著代碼敲那是不行的,要知其然并知其所以然,這節來講講actioncable進階方面的內容,對actioncable有一個更高的理解。 #### 2. 使用 下面會分別從幾個方面來講解actioncable,首先從安全領域來說說。 ##### 2.1 跨域 之前在本地測試環境,應用都是跑在3000端口上的,現在把它跑在4000端口上,看會是怎樣的。 后臺會不斷地提示下面這行信息。 ``` Request origin not allowed: http://localhost:4000 ``` 其實跑在4000端口的時候,websocket是連不上的。 因為actioncable默認只在3000端口上開放websocket服務,這個可以查看其源碼得到: ``` #https://github.com/rails/rails/blob/master/actioncable/lib/action_cable/engine.rb#L25 options.allowed_request_origins ||= "http://localhost:3000" if ::Rails.env.development? ``` actioncable也提供了機制來解決這個問題。 比如在配置文件(比如: application.rb)中添加一行: ``` config.action_cable.allowed_request_origins = ['http://rubyonrails.com', /http:\/\/ruby.*/] ``` 或者干脆關閉了跨域的檢查 ``` config.action_cable.disable_request_forgery_protection = true ``` 源碼可見于此處: ``` #https://github.com/rails/rails/blob/71657146374595b6b9b04916649922fa4f5f512d/actioncable/lib/action_cable/connection/base.rb#L195 def allow_request_origin? return true if server.config.disable_request_forgery_protection if Array(server.config.allowed_request_origins).any? { |allowed_origin| allowed_origin === env['HTTP_ORIGIN'] } true else logger.error("Request origin not allowed: #{env['HTTP_ORIGIN']}") false end end ``` ##### 2.2 用戶系統 剛才從整個網站的安全出發講解了websocket的安全問題,現在要從剛細顆粒的地方講解安全,那就是用戶系統,意思就是說,不是每個使用網站的用戶都能使用websocket,比如登錄的用戶才能使用,不登錄的用戶就過濾掉。 做一切的關鍵的文件在于`app/channels/application_cable/connection.rb`這個文件。 現在我們把其改寫一下: ``` # Be sure to restart your server when you modify this file. Action Cable runs in a loop that does not support auto reloading. module ApplicationCable class Connection < ActionCable::Connection::Base identified_by :current_user def connect self.current_user = find_verified_user end protected def find_verified_user cookies.signed[:username] || reject_unauthorized_connection end end end ``` 意思就是說,帶有`cookies.signed[:username]`的用戶才是被允許的,不然就是拒絕連接`reject_unauthorized_connection`。 現在我們先創建一個登錄界面: ``` # app/views/sessions/new.html.erb <%= form_for :session, url: sessions_path do |f| %> <%= f.label :username, 'Enter a username' %><br/> <%= f.text_field :username %><br/> <%= f.submit 'Start chatting' %> <% end %> ``` ``` # app/controllers/sessions_controller.rb class SessionsController < ApplicationController def create cookies.signed[:username] = params[:session][:username] redirect_to "/rooms/show" end end ``` ``` # config/routes.rb root 'sessions#new' ``` 登錄界面是這樣的: ![](https://box.kancloud.cn/81c0b8bed1be2317ff1b0a0fafce6562_406x161.png) 登錄之后,后臺的日志信息就會多了這行: ``` Registered connection (隨風) ``` 如果把cookies信息清掉,也就是沒有登錄的情況,后臺就會提示下面的信息: ``` An unauthorized connection attempt was rejected Failed to upgrade to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket) ``` 表示無法連接到websocket服務。 這個就解決了用戶系統登錄的問題的。 ##### 2.3 適配器 在actioncable源碼里定義了好幾種適配器,比如redis的pub/sub,還有postgresql的notify。 源碼可見于:[https://github.com/rails/rails/tree/master/actioncable/lib/action\_cable/subscription\_adapter。](https://github.com/rails/rails/tree/master/actioncable/lib/action_cable/subscription_adapter%E3%80%82) 先不管什么是適配器,我們先用redis來試試。 改變`config/cable.yml`文件,內容如下: ``` # Action Cable uses Redis by default to administer connections, channels, and sending/receiving messages over the WebSocket. production: adapter: redis url: redis://localhost:6379/1 development: adapter: redis url: redis://localhost:6379/1 # adapter: async test: adapter: async ``` 在`Gemfile`文件里添加下面這行: ``` gem 'redis' ``` 執行`bundle`并重啟服務器。 再用`redis-cli monitor`命令進入redis的終端界面,并監控redis的運行情況。 當我登錄聊天室的時候,`monitor`監控界面會出現下面一行: ``` 1461656197.311821 [1 127.0.0.1:58177] "subscribe" "room_channel" ``` 表示在訂閱`room_channel`這個通道。 因為我們之前`app/channels/room_channel.rb`文件的內容是這樣的: ``` class RoomChannel < ApplicationCable::Channel def subscribed stream_from "room_channel" end ... end ``` 我們也定義了一個叫`room_channel`的通道,所以跟之前redis的數據對應起來。 現在我們鍵入聊天信息,并回車。 `monitor`界面會出現類似下面的信息: ``` 1461656387.284232 [1 127.0.0.1:58179] "publish" "room_channel" "{\"message\":\"\\u003cdiv class=\xe2\x80\x9cmessage\xe2\x80\x9d\\u003e\\n \\u003cp\\u003e11111\\u003c/p\\u003e\\n\\u003c/div\\u003e\\n\"}" ``` 表示正在`room_channel`通道上廣播消息。 redis的pub/sub機制就是一種廣播機制,它能夠把一個消息向多個客戶端傳遞,我們實現聊天室正是需要這樣的功能,所以actioncable就可以利用它來當適配器,類似的機制也可以使用postgresql的notify機制,也是一樣的道理,就是多個客戶端訂閱一個通道,能夠同時接收通道的信息。 不像actioncable自己封裝了redis的pub/sub機制,在[websocket之用tubesock在rails實現聊天室(五)](http://www.rails365.net/articles/websocket-zhi-yong-tubesock-zai-rails-shi-xian-liao-tian-shi-wu)這篇文章有介紹過直接用redis的pub/sub機制。 比如下面的代碼: ``` def chat hijack do |tubesock| redis_thread = Thread.new do Redis.new.subscribe "chat" do |on| on.message do |channel, message| tubesock.send_data message end end end tubesock.onmessage do |m| Redis.new.publish "chat", m end tubesock.onclose do redis_thread.kill end end end ``` 也可以自己實現最簡單的適配器,其實就是用一個數組。比如默認的async適配器,就是用類似的方法實現的。原理是這樣的,比如一個websocket連接進到服務器來了,就把這個socket存進數組中,每個數組的內容都是socket的連接,比如要廣播消息的話,就是直接循環這個數據,分別往里面發送信息即可,比如,socket.write("hello")。 ##### 2.4 服務器運行 可以有兩種方式來運行actioncable提供的websocket服務。第一種是以`Rack socket hijacking API`的方式來運行,這個跟之前[tubesock](http://www.rails365.net/articles/websocket-zhi-yong-tubesock-zai-rails-shi-xian-liao-tian-shi-wu)這個gem是一樣的,它跟web進程集成在一起,以掛載的方式掛載到一個路徑中。 正如上文所說的,可以在路由中掛載,比如: ``` # config/routes.rb Rails.application.routes.draw do mount ActionCable.server => '/cable' end ``` 還有另外一種是在配置文件中修改。 ``` # config/application.rb class Application < Rails::Application config.action_cable.mount_path = '/websocket' end ``` 另一種運行websocket的方式是`Standalone`。它的意思是把websocket服務運行在另一個進程中,因為它仍然是一個rack應用程序,只要支持`Rack socket hijacking API`的應用程序都可以運行,比如puma,unicorn等。 新建`cable/config.ru`文件,內容如下: ``` require ::File.expand_path('../../config/environment', __FILE__) Rails.application.eager_load! run ActionCable.server ``` 然后再新建`bin/cable`可執行文件,內容如下: ``` #!/bin/bash bundle exec puma -p 28080 cable/config.ru ``` 使用`bin/cable`命令可運行。 關于websocket的服務器部署后續有另外的章節介紹。 ##### 2.5 js客戶端 瀏覽器要與客戶端保持鏈接,必須像之前那樣主動發送websocket請求。 `rails 5`中默認生成了一個文件,叫`app/assets/javascripts/cable.coffee`,把下面兩行注釋拿掉: ``` @App ||= {} App.cable = ActionCable.createConsumer() ``` 默認情況下,websocket服務器的地址是`/cable`。 可以從源碼上看到這個實現。 ``` # https://github.com/rails/rails/blob/52ce6ece8c8f74064bb64e0a0b1ddd83092718e1/actioncable/app/assets/javascripts/action_cable.coffee.erb#L8 @ActionCable = INTERNAL: <%= ActionCable::INTERNAL.to_json %> createConsumer: (url) -> url ?= @getConfig("url") ? @INTERNAL.default_mount_path new ActionCable.Consumer @createWebSocketURL(url) ``` 其中,`@INTERNAL.default_mount_path`就是`/cable`。 ``` # https://github.com/rails/rails/blob/7f043ffb427c1beda16cc97a991599be808fffc3/actioncable/lib/action_cable.rb#L38 INTERNAL = { message_types: { welcome: 'welcome'.freeze, ping: 'ping'.freeze, confirmation: 'confirm_subscription'.freeze, rejection: 'reject_subscription'.freeze }, default_mount_path: '/cable'.freeze, protocols: ["actioncable-v1-json".freeze, "actioncable-unsupported".freeze].freeze } ``` 按照前文所說,可以把服務器部署到另外一臺主機上,或者說,我不想用默認的`/cable`路徑,有時候,開發環境和生產環境的情況根本就是兩碼事,本地可以隨意,但線上也許是另外的服務器,或者說,本地可以是ws協議,線上是wss協議。 actioncable也提供了一個簡單的配置參數。 ``` config.action_cable.url = "ws://example.com:28080" ``` 不過,這個需要在layout上加上這行: ``` <%= action_cable_meta_tag %> ``` 它的源碼是這樣的: ``` def action_cable_meta_tag tag "meta", name: "action-cable-url", content: ( ActionCable.server.config.url || ActionCable.server.config.mount_path || raise("No Action Cable URL configured -- please configure this at config.action_cable.url") ) end ``` 就只是生成一個html的標簽,被js的`createConsumer`利用,具體可以看`createConsumer`的方法。 本篇完結。 下一篇:[websocket之actioncable實現重新連接功能(九)](http://www.rails365.net/articles/websocket-zhi-actioncable-shi-xian-chong-xin-lian-jie-gong-neng-jiu)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看