内容简介:ReadCheck out the
It is time to proudly announce the 1.0 release of AnyCable —a drop-in turbo-extension for Action Cable that relies on the same API and also works outside of Rails. It took me four years to turn a demented (last name pun intended) idea into a robust backbone for real-time Ruby applications. Discover new features, learn from our wins and fails, peek into the AnyCable’s future, and see how using Ruby and Go together gives you the best of both worlds!
AnyCable brings performance and scalability to real-time applications built with Ruby and Rails. It achieves that by moving low-level WebSocket handling from Ruby to Go but leaving all the business logic in your Rails (or plain Ruby) codebase. In other words, AnyCable makes your application performant without sacrificing productivity.
Read AnyCable: Action Cable on steroids in our blog.
Check out the introductory post on AnyCable to learn more about its architecture and performance characteristics and visit the official website that is full of interactive explanations.
The project passed a long way from a Ruby gem and a Go binary to a family of tools , from a lone maintainer to the awesome team of collaborators and dozens of contributors, from a ~200 words README to the documentation website with more than 50 articles. But let’s leave la nostalgie behind and discuss the following topics today:
- AnyCable 1.0 highlights
- Lessons learned from 0.1.0 to 1.0.0
- AnyCable meets GitHub Sponsors
-
Make AnyCable truly yours
AnyCable 1.0: The highlights
The main idea behind AnyCable v1 was to make a transition from Action Cable to AnyCable as smooth as possible. Even though I stated right from the beginning that AnyCable was a plug-n-play solution for Rails applications, that wasn’t true for most use cases. People wrote long-reads about potential caveats, posted issues, shared workarounds and hacks. We made a tiny step towards a brighter future with v0.6.0 release , and now, with v1.0, we are making a giant leap!
AnyCable 1.0 aims to make a transition from Action Cable as smooth as possible.
You can find everything in the official release notes , but I am here to highlight some of them.
Interactive Quickstart
Configuring an existing project for use with AnyCable has not always been straightforward. A simple “add-a-gem-and-bundle-install” approach does not apply to such an advanced tool: we need to install a WebSocket server, update the framework configuration for development and production, and notify users about potential incompatibilities.
So, we came up with an interactive Rails generator ( rails g anycable:setup
), which automates most of the initial set up tasks, including:
- Adding required configuration files and settings for AnyCable.
- Preconfiguring AnyCable to support authentication with Devise (if used in a project).
-
Installing
anycable-go
server (or providing a snippet to add it todocker-compose.yml
if Docker is detected in development). - Preparing the application to run on Heroku (if needed).
- Running static compatibility checks .
We used the following rule of thumb when designing this script: every reported issue with the historic AnyCable setup should be covered by the generator.
So, if you encounter a problem after running anycable:setup
, let us know, and we’ll fix it!
Better Action Cable compatibility
We reduced the number of unsupported Action Cable features
from four to just two by introducing remote disconnection support ( ActionCable.server.remote_connections.where(user: user).disconnect
) and the ability to keep channel states
between actions (usually done via instance variables in Action Cable):
class RoomChannel < ApplicationCable::Channel # AnyCable API similar to attr_accessor state_attr_accessor :room def subscribed self.room = Room.find(params["room_id"]) stream_for room end def speak(data) broadcast_to room, message: data["message"] end end
Initially, I was thinking of automagically detecting instance variables and attribute readers/writers, and hijacking them to add AnyCable state management functionality. I even started to implement the following algorithm:
-
Parse source code of channel classes and collect all known instance variables names (explicit or defined via
attr_{reader|writer|accessor}
). -
Implicitly register these names as state accessors (via
state_attr_accessor
). -
Rewrite all direct instance variables usage with readers/writers,
@room
→self.room
(yeah, I am a big fan of transpiling).
Can you imagine the number of possible edge cases? I could and decided to step back. state_attr_accessor
is first and only API method AnyCable adds to Action Cable.
Another major improvement related to compatibility is the ability to use Rack middlewares to enhance Rack request information.
A typical use-case is a Devise-backed authentication. Devise uses Warden
under the hood, which, in turn, relies on middleware. This middleware initializes a Warden::Manager
instance and stores it in the request.env["warden"]
(which is later used by Devise).
Before v1.0, request objects in AnyCable didn’t take any application middlewares into account. That made such a popular scenario as authentication as complex as in the following example:
# AnyCable <1.0 def connect self.user = find_verified_user || reject_unauthorized_connection end def find_verified_user app_cookies_key = Rails.application.config.session_options[:key] || raise("No session cookies key in config") env["rack.session"] = cookies.encrypted[app_cookies_key] Warden::SessionSerializer.new(env).fetch(:user) end
With Rack middleware support in AnyCable 1.0, the code above is very similar to the one you may already have for Action Cable:
# AnyCable >=1.0 def connect self.user = env["warden"].user(:user) || reject_unauthorized_connection end
This feature also made request.session
accessible in AnyCable out-of-the-box, which helped us solve another compatibility issue that we are going to discuss next.
AnyCable :heart: Stimulus Reflex
Stimulus Reflex and its fellow project CableReady are gaining more and more popularity in the Ruby on Rails community. It doesn’t surprise me. Building interactive real-time web applications using a dead-simple server-rendered HTML-over-the-wire approach that does not lock you into any frontend framework is an exciting idea.
Unfortunately, the first attempts to run Stimulus Reflex with AnyCable v0.6 revealed a bunch of issues we haven’t seen in classic Action Cable apps. I decided to fix them all at any cost and as of today we have StimulusReflexExpo running on AnyCable without any issues!
New demo application
A good example application is worth more than tons of documentation.
We had a demo application since the very first release, but it quickly turned into an unhelpful monster. The main mistake was in trying to combine all the possible use cases in a single codebase and not having proper test coverage and CI configuration (thus, making upgrades painful).
So, instead of trying to revamp the old app, I decided to build a new one from scratch: meet AnyWork: AnyCable Rails Demo .
Built with modern tools such as Rails 6, Stimulus , and TailwindCSS , this example is what I call an “omni-application” : an application with multiple variations living in separate branches and representing different usage scenarios. Every variation has a PR attached with a thorough description. See, for example, From Action to Any .
Thus, the demo application acts as an additional form of documentation, closer to code. We plan to link most of the documentation articles to the dedicated demo app variations. Subscribe to the repo updates and stay tuned!
Improved Heroku deployment instructions
Read how we helped a YC-funded startup to scale on Heroku in Big on Heroku: Scaling Fountain without losing a drop .
Deploying AnyCable on Heroku still requires having two applications. While investigating potential workarounds (including a Heroku add-on), we’ve been improving our existing documentation based on our production Heroku experience.
In my opinion, the most interesting new addition to it is the “ Choosing the right formation ” chapter providing the formula to calculate the required number for dynos depending on the application load:
Four years of cables: Lessons learned
Besides AnyCable, I worked onAction Cable testing for Rails, Lite Cable for beyond-Rails world and Action Cable CLI for those who prefer terminals over GUI.
During the last four years, I spent a decent amount of my spare time on AnyCable and other cables . I had to make a lot of decisions along the way, and not all of them made me happy in the end.
Before building a bright new future, we need to re-evaluate the past. So, let’s do that!
“A very cool project”—DHH
The first documented usage of the name “AnyCable” dates back to the June 10th, 2016. I made it up while ruminating over an upcoming conference talk: I wanted to speak at RailsClub Moscow (now RubyRussia ), but I had no idea what to talk about. So, I shared a few ideas with my colleagues at Evil Martians, they liked the one on better Action Cable and encouraged me to propose it as well as to work on the prototype. That’s how I began to speak at conferences and maintain an open source project to speak at conferences!
Neither me nor other Martians were using AnyCable (or even plain Action Cable) in production until late 2017. For more than a year, I was flying blind: all I had was a handful of user-posted issues on GitHub and Gitter . So, I almost lost all the motivation to work on a very cool project and turned into a conference-driven developer : v0.5.0 was timed to my RubyConfMY talk and v0.6.0 to the RubyConf one. The hardest question for me during Q&A sessions was “How do you use AnyCable in production?” Telling “ A dinnae, ye ken ” in response made me feel like I’m selling snake oil.
The situation changed by the end of 2018: more and more projects (including but not limited to those that Evil Martians did for commercial clients like eBay) started adopting Action Cable, some of them grew big enough to realize they need AnyCable to cope with high loads. Sometimes they came to us, Evil Martians, for commercial support , and I finally got an opportunity to battle-test a fun project I conceived years ago.
Open source for the sake of open source is not fun.
Although AnyCable survived the crisis, I told myself to never again deal with a massive open source project that is not directly related to my personal pain .
AnyCable-Go: From miss to bliss
The first server implementation for AnyCable was written in Erlang. Switching to Go was the right decision: better gRPC tooling, easier distribution (what could be simpler than a single binary?), a larger community (which means more contributors ).
And developing with Go was amazingly fast in the beginning: it took me about a week and eight
commits to build the first working version! I was pretty new to Go, and Go was rather young, so, it was much harder to discover best practices for code organization (compared to the Ruby community). So, I chose a typical Go way
: a single main
package, a dozen of files in the repo’s root, high coupling, zero or less tests (I had black-box testing though, see below). Should I say that this path led to a hardly maintainable mess?
I had a lot of planned features for v0.6.0, and it was clear that I wouldn’t be able to implement them with the “architecture” (or lack of it) we had then. So, the Great Refactoring began.
The refactoring was inspired by a “ Structuring applications in Go ” blog post—a needle in the hay of articles on Go code micro optimizations,error handling, and use of pointers. I also started looking for open source Go projects with a well-designed, in my very Ruby-ish opinion, architecture. Obviously, Faktory got on the list as well as Centrifugo and Telegraf . Using other people’s code as a reference (and sometimes borrowing it) helped me transform the mess into a codebase that is now a pleasure to work with.
Writing complex reliable software cannot be easy.
Why didn’t I try to absorb the ancestral knowledge in the first place? Probably, due to the reputation of Go being easy to learn and fast to ship things with (which turned out to be closer to the “slap shit together and deploy” approach). No, Go, writing complex reliable software could not be easy.
AnyCable-bility
One thing I never regret was writing a conformance testing tool for Action Cable-compatible WebSocket servers— AnyT . Technically, it’s a collection of integration tests describing different client-server communication scenarios and a CLI to run them along with a server under testing.
Investing in development tools pays off in the long term.
Without such a tool, writing new server implementations (e.g., AnyCable Rack server ) or refactoring the existing ones would be much more difficult. AnyT helps me to avoid regressions and to keep AnyCable in sync with Action Cable (we also run tests against it—this is our baseline).
One fun fact about AnyT: initially, I made up a different name for it— anycablebility . I even made a release under that name, but then the strangest thing happened: my RubyGems ownership was stolen :scream_cat:! I wasn’t able to restore the access and decided to change the name instead. And the new one is much better, don’t you think so?
The duality of Rails compatibility
As I already mentioned, AnyCable was designed as an Action Cable add-on and not a replacement. We still rely on Action Cable’s Ruby code and its client JavaScript libraries.
Without an intentional Action Cable support, we would not get a lot of users. So, that was a right strategy.
On the other hand, this dependency is slowing down the evolution of AnyCable: we need to invest resources in compatibility and cannot add all the new features we want. That would require hacking Action Cable code as well as writing custom clients.
The situation we’re currently in is similar to the story of hub , the CLI for GitHub. And the lessons we learned, too…
From Any to Many: The future of cables and how you can help
The v1.0 release has two purposes. First, we’re stating that AnyCable has stabilized and ready for production (although it was ready a while ago). Secondly, and that’s more important to me, we can now start working on v2!
Let’s turn the fantasy mode on and talk about the future of AnyCable.
AnyCable 2: Improved protocol, own channels framework for Ruby and other languages , modernized JS client, WebSocket gateway.
AnyCable 2.0 is going to be a revolution, a paradigm shift. We are no longer going to mimic Action Cable.
First of all, I want to re-visit the protocol
. For example, add unique session identifiers, monotonic (within a stream) message IDs, action ( perform
) acknowledgments, batch operations, etc. These changes will help us to add such features as, for example, reliable delivery and a true RPC experience.
Changing the protocol would require writing the new client-side code. And we need a rewrite not only to support a new protocol but to provide a better developer experience.
Here is an example of a theoretical AnyCable JS client:
// channels/chat.js import { Channel } from 'anycable' export default class extends Channel { static identifier = 'chat'; fetchHistory = () => this.perform('fetchHistory') } // index.js import ChatChannel from 'channels/chat' const roomId = 42 // JS camelCased keys are automatically transformed to Ruby snake_case // at the server side const channel = new ChatChannel({roomId}) // All async calls are Promise-based, // so you can use await (of course, from within an async function) await channel.connect() // An alternative events API channel.on('connect', () => console.log('Connected')) // Here is where message acks are used const messages = await channel.fetchHistory() // Subscribing to incoming messages channel.on('message', message => console.log(message))
Another useful feature we plan to support from the very beginning is sharing a connection between browser tabs (like Logux does).
Note that, in the example above, we have a simple string as a channel identifier ( "chat"
), not a Ruby class name. I believe that explicitly defined channel identifiers are much better than leaky abstractions. A client application should not care about my Ruby code.
We are going to eliminate this problem (as well as many others) by providing our custom framework for channels .
Nevertheless, we plan to have an API that is close just enough
to Action Cable, so that, in most cases, a migration would be as simple as changing ActionCable::Channel::Base
to AnyCable::Channel::Base
in application_cable/channel.rb
.
In other words, AnyCable Ruby API will become a superset of the currently supported Action Cable API.
The framework will be cable-agnostic : it will be only responsible for business-logic and will have no knowledge of a particular transport or server implementation. Thus, you will be able to use it not only with AnyCable but, for example, with Falcon or Iodine .
Some popular real-time features will be included out of the box, or via plugins: we are talking things like presence tracking and “channel-less” subscriptions.
And we’re not going to stop here.
We have a new shiny protocol and we are no longer Rails-dependent—maybe, it’s time to think about moving beyond Ruby? What about AnyCable for Python or PHP ?
Thinking of AnyCable for different languages led to another mindblowing idea—AnyCable as WebSocket Gateway! By adding a routing mechanism to AnyCable-Go, we can serve different channels via different backends. Your client shouldn’t know about your microservice architecture details, one connection to consume them all! Just like Apollo Federation but for WebSockets.
OK, that went too far. I spent four years just on an Action Cable add-on . How long could it take make all the dreams above come true? Ten years or so? Remember, this is an open source project, and I am currently paid for doing “boring” commercial work…
AnyCable meets GitHub Sponsors
That’s why we decided to give GitHub Sponsors program a try and launched our sponsorship program that will help me and other core contributors to keep dedicating valuable time to AnyCable outside of billable hours. Let’s make a better real-time future together :heart:!
Make AnyCable truly yours
AnyCable is also easily extendable and configurable: if you need it fine-tuned for the specific needs of your startup—we offer custom solutions and commercial support. Be sure to check out the AnyCable’s website and always feel freeto drop us a line if you need to discuss your production needs!
AnyCable brings turbo speed and improved reliability to the easy-to-use Action Cable API. That’s why we originally called it “ Action Cable on steroids ”. If your Ruby or Rails application relies on real-time features, using AnyCable is the simplest way to reduce infrastructure costs while offering your users a better real-time experience. AnyCable also provides a lot of production-required features out of the box: analytics, Prometheus integration, disconnect-less deployments, non-Rails applications support, and more.
以上所述就是小编给大家介绍的《AnyCable 1.0: Four years of real-time web with Ruby and Go》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
神经网络与机器学习(原书第3版)
[加] Simon Haykin / 申富饶、徐烨、郑俊、晁静 / 机械工业出版社 / 2011-3 / 79.00元
神经网络是计算智能和机器学习的重要分支,在诸多领域都取得了很大的成功。在众多神经网络著作中,影响最为广泛的是Simon Haykin的《神经网络原理》(第3版更名为《神经网络与机器学习》)。在本书中,作者结合近年来神经网络和机器学习的最新进展,从理论和实际应用出发,全面、系统地介绍了神经网络的基本模型、方法和技术,并将神经网络和机器学习有机地结合在一起。 本书不但注重对数学分析方法和理论的探......一起来看看 《神经网络与机器学习(原书第3版)》 这本书的介绍吧!