package main
import (
"context"
"fmt"
"log"
"time"
"github.com/jackc/pgx"
)
type DatabaseInstance struct {
conn *pgx.Conn
connConfig pgx.ConnConfig
maxConnectAttempts int
}
func NewConn(uri string, maxAttempts int) (*DatabaseInstance, error) {
connConfig, err := pgx.ParseURI(uri)
if err != nil {
return nil, err
}
instance := DatabaseInstance{}
instance.connConfig = connConfig
instance.maxConnectAttempts = maxAttempts
return &instance, err
}
func (db *DatabaseInstance) reconnect() (*pgx.Conn, error) {
conn, err := pgx.Connect(db.connConfig)
if err != nil {
return nil, fmt.Errorf("unable to connection to database: %v", err)
}
if err = conn.Ping(context.Background()); err != nil {
return nil, fmt.Errorf("couldn't ping postgre database: %v", err)
}
return conn, err
}
func (db *DatabaseInstance) GetConn() *pgx.Conn {
var err error
if db.conn == nil {
if db.conn, err = db.reconnect(); err != nil {
log.Fatalf("%s", err)
}
}
if err = db.conn.Ping(context.Background()); err != nil {
attempt := 0
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for range ticker.C {
if attempt >= db.maxConnectAttempts {
log.Fatalf("connection failed after %d attempt\n", attempt)
}
attempt++
log.Println("reconnecting...")
db.conn, err = db.reconnect()
if err == nil {
return db.conn
}
log.Printf("connection was lost. Error: %s. Waiting for 5 sec...\n", err)
}
}
return db.conn
}
Код довольно прост: мы перед взятием соединения пингуем БД, если этого не удалось сделать, то ждём 5 секунд и пытаемся снова.
@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
Встречайте годный контент!
В этой статье детально и подробно описана работа планировщика Go, разобраны основные моменты, связанные с горутинами
Вот некоторые факты из статьи:
M
горутин к N
потокам ядра, формируя модель M:N
@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
Для тех, у кого руки ещё не дошли до свежей февральской версии Go — держите годное видео от разраба из Авито, рассказывает про некоторые нововведения и всё такое
В частности про:
split
из пакета strings
, позволяющую разделить строку на частиrand
@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Держите полезный ролик, в котором на разные вопросы, связанные с тестированием Go-кода, отвечает разработчик с опытом 8 лет.
О чём говорится в видео?
— Краткое введение в тестирование: что такое тесты и какие виды тестов существуют.
— Должен ли разработчик писать тесты?
— Что такое TDD?
— «Утром код — вечером тесты» или «Утром тесты — вечером код»?
— Какую логику и какие данные нужно тестировать, а когда тесты не нужны.
— Как TDD помогает увеличить скорость и снизить затраты на разработку.
— Забиваете на best practices во время инцидентов? Остановитесь и подумайте.
— Как хорошие тесты становятся документацией к коду.
— Что такое test cases и как они связаны с use cases.
— Как методика TDD влияет на структуру команды и процессы.
— Что такое технический долг.
— Как «постмортем» помогает не воспроизводить одни и те же фейлы.
— Что ещё почитать по про test driven development.
В целом, полезное видео, рекомендую
@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
Интерфейсы в Go — это способ реализации полиморфизма
package main
import (
"math"
"fmt"
)
// Круг.
type Circle struct {
x, y, r float64
}
// Прямоугольник.
type Rectangle struct {
x1, y1, x2, y2 float64
}
// Интерфейс фигуры, которому удовлетворяют все типы, имеющие соответствующий
// метод вычисления площади Area().
type Figure interface {
Area() float64
}
// Реализация интерфейса Figure для Circle.
func (c *Circle) Area() float64 {
return math.Pi * c.r * c.r
}
// Реализация интерфейса Figure для Rectangular.
func (r *Rectangle) Area() float64 {
return math.Abs(r.x2 - r.x1) * math.Abs(r.y2 - r.y1)
}
func main() {
figures := make([]Figure, 0) // Срез фигур.
// Мы можем добавлять в этот срез все, что удовлетворяет интерфейсу
// Figure, несмотря на то, что это элементы разных типов:
figures = append(figures, &Circle{0, 0, 10})
figures = append(figures, &Rectangle{0, 0, 10, 20})
// И теперь мы можем единообразно обрабатывать эти данные разных типов.
for _, figure := range figures {
fmt.Printf("Area of %#v = %f\n", figure, figure.Area())
}
}
$ go run figures.go
Area of &main.Circle{x:0, y:0, r:10} = 314.159265
Area of &main.Rectangle{x1:0, y1:0, x2:10, y2:20} = 200.000000
@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
v1 := Struct{}
v2 := &Struct{}
В чём отличие? Понятно, что во втором случае передается указатель, но когда что нужно использовать?
Struct
и присваивает его переменной v1
. В этом случае переменная v1
будет содержать саму структуру, а не указатель на нее.Struct
с помощью оператора &
, который возвращает указатель на структуру, и присваивает этот указатель переменной v2
. В этом случае переменная v2
будет содержать указатель на структуру, а не саму структуру.type Struct struct {
Value int
}
func main() {
v1 := Struct{} // Создаем структуру
v1.Value = 10 // Изменяем поле Value
v2 := &Struct{} // Создаем указатель на структуру
v2.Value = 20 // Изменяем поле Value через указатель
fmt.Println(v1.Value) // Выводит 10
fmt.Println(v2.Value) // Выводит 20
}
Struct
и изменяем поле Value
сначала через саму структуру, а затем через указатель на структуру. Оба вывода будут отличаться, потому что изменения сделанные через указатель на структуру будут отражаться на самой структуре, в то время как изменения, сделанные через саму структуру, не влияют на указатель на нее.@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
"Second"
и "Third"
(хотя они должны идти после "First"
), а "Fourth"
как раз лишь только после "First"
? Ведь
defer
должен срабатывать перед закрытием функции, в которой он объявлен.package main
import "fmt"
func main() {
defer fmt.Println(changePointer())
fmt.Println("First")
}
func changePointer() string {
defer fmt.Println("Third")
fmt.Println("Second")
return "Fourth"
}
changePointer()
вызывается, потому что это необходимо для получения результата, который может быть использован в fmt.Println(changePointer())
. Функция
changePointer
выполняется, чтобы получить результат, который возвращается в main
, и только после этого запланированные defer
— вызовы выполняются.Вот такие дела
@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
fmt.Scanln()
, тут все понятно. Далее необходимо вывести
True
, если строка состоит из цифр и False
во всех остальных случаях. Как это можно сделать?^\d*$
или, что аналогично: ^[0-9]*$
.test:="1233455677789900066554436433"
pattern:=`^\d*$`
matched,err:= regexp.Match(pattern,
[]byte(test))
Ну и неплохо бы ещё прикрутить обработку ошибок
@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
Согласно Statista, больше 80% организаций со штатом от 5000 сотрудников используют микросервисную архитектуру, или микросервисы. Разработка микросервисов помогает бизнесу масштабироваться, делают приложения надежнее и упрощают кодовую базу. Но подходят они не всем.
Держите полезную статью о том, что такое микросервисы, в чем их преимущества и когда для бизнеса есть смысл на них переходить.
@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Вот первая идея, которая приходит в голову; тут используются регулярные выражения:
import (
"regexp"
)
func main() {
str := "Некая строка c кириллическими символами"
re := regexp.MustCompile("[А-Яа-я]+?") //проверяем на киррилические символы
isRussian := re.MatchString(str)
if isRussian {
...
}
}
Есть ли более эффективный способ узнать, на каком языке написана строка? Языков только два - русский и английский.
func IsEngByLoop(str string) bool {
for i := 0; i < len(str); i++ {
if str[i] > unicode.MaxASCII {
return false
}
}
return true
}
Раз мы уверены, что имеем дело только с 2 языками, то достаточно определить, являются ли все символы строки ASCII символами. Если да, то строка написана на Английском, если нет - то на каком-то другом (в вашем случае, это Русский).
Вот и все дела
@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
package main
import "fmt"
func main() {
slice := []int{1}
fmt.Println(slice[1:])
}
Мы ведь обращаемся к элементу слайса с индексом 1, которого нет
Вот цитата:
"For arrays or strings, the indices are in range if 0 <= low <= high <= len(a), otherwise they are out of range. For slices, the upper index bound is the slice capacity cap(a) rather than the length."
Особенно важно последнее предложение
Ну и небольшой пример для полного понимания:
package main
import "fmt"
func main() {
slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
slice1 := slice[5:6]
slice2 := slice1[1:10]
fmt.Println(slice1, slice2, cap(slice2), len(slice2))
}
Подробнее
@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
Repeater
, Controller
и Executor
.Также нужно сделать 1 модуль, который они будут импортировать с некоторым набором общих для них функций.
Как организовать структуру такого проекта?
main.go
под каждый сервис. Кладём их каждый в свою подпапку в директории, например cmd
.А для общих библиотек используем отдельную директорию, например
pkg
, и будем импортировать отсюда функционал во все 3 сервиса.cmd/
repeater/
main.go
controller/
main.go
executor/
main.go
pkg/
c-library/
clibrary.go
go.mod
go build ./cmd/repeater
go build ./cmd/controller
go build ./cmd/executor
main.go
:package main
import c_library "test/pkg/c-library"
func main() {
c_library.HelloWorld()
}
package c_library
import "fmt"
func HelloWorld() {
fmt.Println("HELLO WORLD")
}
@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
Вот неплохая статья, которая обсуждает параллельные вычисления на одном процессоре и масштабирование с ограничением количества одновременных запросов.
Рекомендуется использовать первый паттерн для выполнения параллельных вычислений без ограничений с использованием
errgroup
.Если возможен бесконтрольный рост удаленных запросов, следует ограничить одновременное выполнение и подобрать лимит опытным путем.
Ну и для ускорения сложных вычислений можно использовать
runtime.NumCPU
в качестве лимита для распараллеливания на все ядра процессора.@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Держите годный контент
Это будет не игрушечный проект, а полностью готовый к использованию:
— для него будет выбран актуальный http-роутер
— важное место отведено о логам
— будут написаны тесты: unit-тесты, тесты хэндлеров и функциональные
— будет настроен автоматический деплой через GitHub Actions и др.
@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
⚡️ 4 Репозитория на Github для подготовки к 4 различным типам собеседований по разработке программного обеспечения:
1. Собеседования по системному дизайну: https://github.com/ashishps1/awesome-system-design-resources
2. Собеседования по низкоуровневому проектированию: https://github.com/ashishps1/awesome-low-level-design
3. Собеседования по программированию: https://github.com/ashishps1/awesome-leetcode-resources
4. Поведенческое собеседование: https://github.com/ashishps1/awesome-behavioral-interviews
@golang_interview
1. Собеседования по системному дизайну: https://github.com/ashishps1/awesome-system-design-resources
2. Собеседования по низкоуровневому проектированию: https://github.com/ashishps1/awesome-low-level-design
3. Собеседования по программированию: https://github.com/ashishps1/awesome-leetcode-resources
4. Поведенческое собеседование: https://github.com/ashishps1/awesome-behavioral-interviews
@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Держите полезный ресурс, здесь показана большая часть того, что понадобится в реальной работе с Go: структуры, интерфейсы, создание своего веб-сервера, межсайтовый скриптинг, работа с БД и много всего ещё.
Очень полезно, рекомендую
@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM