Um Tutorial para a Linguagem de Programação Go – parte 9
Multiplexação
Multiplexação
Com channels, é possível servir múltiplos clientes goroutines independentes sem escrever
um multiplexador explícito. O truque é enviar ao servidor um channel na mensagem,
o quel será então usado para responder ao sender original.
Um programa cliente-servidor real tem muito código, então aqui está um substituto bastante
simples para ilustrar a idéia. Ele começa definindo um tipo “request”, que embute um channel
que será usado para responder.
type request struct {
a, b int;
replyc chan int;
}
O servidor será trivial: ele fará operações simples com inteiros. Aqui está o código
que invoca a operação e responde à requisição:
type binOp func(a, b int) int
func run(op binOp, req *request) {
reply := op(req.a, req.b);
req.replyc <- reply;
}
A linha 18 define o nome "binOp" para a função recebendo dois inteiros e
retornando um terceiro.
A rotina "server" executa um loop infinito, recebendo requisições e, para prevenir
bloqueio devido a operações muito demoradas, "starta" uma goroutine para fazer o trabalho de verdade.
func server(op binOp, service chan *request) {
for {
req := <-service;
go run(op, req); // Não espera
}
}
Construimos um servidor de uma forma familiar, "startando-o" e retornando um channel
conectado a ele:
func startServer(op binOp) chan *request {
req := make(chan *request);
go server(op, req);
return req;
}
Aqui está um teste simples. Ele "starta" um servidor com um operador de adição e
envia N requisições sem esperar por suas respostas. Apenas depois de que todas as
requisições enviadas sejam executadas, checa seus resultados.
func main() {
adder := startServer(func(a, b int) int { return a + b });
const N = 100;
var reqs [N]request;
for i := 0; i < N; i++ {
req := &reqs[i];
req.a = i;
req.b = i + N;
req.replyc = make(chan int);
adder <- req;
}
for i := N-1; i >= 0; i-- { // Não importa a ordem
if <-reqs[i].replyc != N + 2*i {
fmt.Println("fail at", i);
}
}
fmt.Println("done");
}
Um contra-tempo com este programa é que ele não desliga o servidor de forma limpa; quando "main" retorna
existe um número de goroutines prolongadas bloqueadas na comunicação. Para resolver isto, podemos
prover um segundo channel "quit" para o servidor:
func startServer(op binOp) (service chan *request, quit chan bool) {
service = make(chan *request);
quit = make(chan bool);
go server(op, service, quit);
return service, quit;
}
Ele passa o channel quit para a função "server", que o utiliza desta forma:
func server(op binOp, service chan *request, quit chan bool) {
for {
select {
case req := <-service:
go run(op, req); // Não espera
case <-quit:
return;
}
}
}
Dentro da função "server", a declaração "select" escolhe qual das múltiplas comunicações
listadas pelos seus "cases" pode continuar. Se todos estiverem bloqueados, aguarda até que um
possa continuar; se vários puderem prosseguir, escolhe um aleatoriamente. Neste exemplo, o
"select" permite ao servidor reconhecer requisições até que receba a mensagem "quit", no ponto
em que retorna, terminando sua execução.
Tudo que resta é focalizar o channel "quit" no fim de main:
adder, quit := startServer(func(a, b int) int { return a + b });
quit <- true;
Existe muito mais sobre programação Go e programação concorrente em geral, mas este "quick tour"
deve lhe oferecer alguns dos princípios básicos.
Bom, esta foi a última parte da minha tradução do Tutorial disponibilizado pela equipe do Go.
Meu inglês anda meio enferrujado, então se alguém puder contribuir sugerindo alterações
no texto, agradeço.
Um forte abraço a todos.
\\//
yIn nI' yISIQ 'ej yIchep
André Luiz de Oliveira Vasconcelos
[...] Multiplexação [...]
[...] Continua… [...]