Ruby와 Go 간의 포털(FFI 사용)

5627 단어 gorubyffi
일부 거대한 REST API를 Ruby에서 Go로 마이그레이션하는 것을 고려하면서 Ruby 코드를 Go 코드로 점진적으로 대체하는 방법을 조사했습니다.

가능한 FFI 기반 프로토 타입으로 끝났습니다. 해결책은 Ruby 코드가 Go 라이브러리를 로드하고 Ruby 클래스 및 메서드에서 Go 함수를 래핑하도록 하는 것입니다.

루비 쪽



Ruby 쪽은 매우 간단합니다. Go 라이브러리를 로드하고 Ruby 클래스 및 메서드에서 Go 함수를 가져와야 합니다.

require 'ffi'

module Portal
  extend FFI::Library

  ffi_lib './libexample.so'

  class Example < FFI::Struct
    # This must be completely in sync with the C struct defined in Go code.
    layout :id, :int, :prefix, :pointer

    def initialize(prefix, id)
      self[:prefix] = FFI::MemoryPointer.from_string(prefix)
      self[:id] = id
    end

    # This feels convoluted, but it hides the fact that our function is loaded
    # outside of the "struct mirror" class.
    def greet
      Portal.greet(self)
    end
  end

  attach_function 'greet', [Example.by_value], :void
end

ex = Portal::Example.new('C', 137)
ex.greet


이동 측



Go 쪽은 좀 더 복잡합니다. C 호환 구조체를 정의하고 Ruby에서 사용하려는 함수를 내보내야 합니다.

멋진 점은 구조체를 수신기로 사용하여 함수를 정의할 수 있다는 것입니다.

package main

/*
struct example {
    int ID;
    char *Prefix;
};
*/
import "C"
import "fmt"

// This declaration is just an alias to the C struct.
type Example C.struct_example

//export greet
func (e Example) greet() {
    fmt.Printf("Hello from %s-%d\n", C.GoString(e.Prefix), e.ID)
}

func main() {}


빌드



빌드도 직관적입니다. 우리는 다음을 수행해야 합니다.
  • Go 코드를 공유 라이브러리로 컴파일합니다. 이 명령은 CGO 바인딩을 생성합니다.
  • 루비 코드를 실행합니다.

  • go build -buildmode=c-shared -o libexample.so example.go
    ruby portal.rb
    


    결과



    코드를 실행하면 다음과 같은 결과가 나타납니다.

    Hello from C-137



    혜택



    이 접근 방식의 주요 이점은 Ruby 코드를 Go로 점진적으로 마이그레이션할 수 있다는 것입니다. 순수한 Go 서비스로 전환할 준비가 될 때까지 부분적으로 교체하여 시작할 수 있습니다.

    기존 Ruby 코드를 대체하는 것만 가능한 것은 아닙니다. 이제 RSpec/Minitest 사양을 재사용하여 새로운 Go 코드를 테스트할 수 있습니다. 테스트 없이 완전히 다시 작성하는 대신 기존 코드로 새 코드를 테스트하는 것으로 시작할 수 있습니다.

    (가능한) 단점



    성능 측면에서 이 접근 방식은 이상적이지 않습니다. 우리는 간접 레이어를 추가하고 있습니다. 이것은 아마도 호출에 약간의 오버헤드를 추가할 것입니다. 이는 트래픽이 많은 서비스에 적용할 경우 측정해야 할 사항입니다.

    좋은 웹페이지 즐겨찾기