如何配置Nginx的Websocket?

  • Post category:Linux

配置Nginx的Websocket

Websocket是一种在Web浏览器和Web服务器之间建立实时、双向、基于消息的连接的技术。Nginx可以用作Websocket的代理服务器,本文将介绍如何配置Nginx的Websocket。

安装Nginx

如果您还没有安装Nginx,请按以下步骤安装:

# Ubuntu或Debian
$ sudo apt-get update
$ sudo apt-get install nginx

# CentOS或Red Hat
$ sudo yum update
$ sudo yum install nginx

配置Nginx

1. 增加HTTP/HTTPS支持

当Nginx作为Websocket代理时,需要开启HTTP/HTTPS支持。请在Nginx配置文件中增加以下配置:

# HTTP配置
server {
    listen 80;
    server_name yourdomain.com;
    location / {
        proxy_pass http://localhost:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

# HTTPS配置
server {
    listen 443 ssl;
    server_name yourdomain.com;
    ssl_certificate /path/to/yourdomain.com.crt;
    ssl_certificate_key /path/to/yourdomain.com.key;
    location / {
        proxy_pass http://localhost:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

2. 增加Websocket支持

请在Nginx配置文件中增加以下配置:

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

server {
    listen 80;
    server_name yourdomain.com;
    location / {
        proxy_pass http://localhost:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_read_timeout 86400;
    }
}

以上配置使得Nginx支持Websocket连接,当有Websocket连接请求时,Nginx将把连接转发到指定的后端服务。

示例1:配置Nginx代理到Node.js服务器

以下为配置Nginx代理到Node.js服务器的示例。

# 安装Node.js
$ sudo apt-get update
$ sudo apt-get install nodejs npm
$ sudo ln -s /usr/bin/nodejs /usr/bin/node

# 安装WebSocket模块
$ npm install ws

创建Node.js服务器文件server.js如下:

const http = require('http');
const WebSocket = require('ws');

const server = http.createServer();
const wss = new WebSocket.Server({ server });

wss.on('connection', (ws) => {
  ws.on('message', (message) => {
    console.log(`Received message => ${message}`)
  });
  ws.send('Hello! Message received.');
});

server.listen(8081, () => {
  console.log('Websocket server listening on http://localhost:8081');
});

创建Nginx配置文件/etc/nginx/sites-available/yourdomain.com如下:

upstream nodejs {
  server localhost:8081;
}

server {
  listen 80;
  server_name yourdomain.com;
  location / {
    proxy_pass http://nodejs;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
  }
}

启动Node.js服务器和Nginx:

$ node server.js &
$ sudo service nginx start

以上配置使得Nginx代理Websocket连接到Node.js服务器,将收到的消息打印到控制台并回复一个“Hello! Message received.”的消息。

示例2:配置Nginx代理到Java服务器

以下为配置Nginx代理到Java服务器的示例。

创建Java Maven项目,并添加以下依赖:

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
  </dependency>
  <dependency>
    <groupId>org.webjars</groupId>
    <artifactId>sockjs-client</artifactId>
    <version>0.3.4</version>
  </dependency>
  <dependency>
    <groupId>org.webjars</groupId>
    <artifactId>stomp-websocket</artifactId>
    <version>2.3.3</version>
  </dependency>
</dependencies>

创建Java WebSocket服务器文件WebSocketServer.java如下:

package com.example.websocketdemo;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketServer extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/my-websocket-endpoint").withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic");
        registry.setApplicationDestinationPrefixes("/app");
    }

}

创建Java WebSocket客户端文件WebSocketClient.java如下:

package com.example.websocketdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.messaging.simp.stomp.StompSession;
import org.springframework.messaging.simp.stomp.StompSessionHandler;
import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.messaging.WebSocketStompClient;

import java.lang.reflect.Type;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;

@Controller
@SpringBootApplication
public class WebSocketClient {

    private static final String WEBSOCKET_ENDPOINT = "ws://localhost:8082/my-websocket-endpoint";

    private WebSocketStompClient webSocketStompClient;

    @RequestMapping(value="/test", method=RequestMethod.GET)
    public @ResponseBody String test() throws InterruptedException, ExecutionException, TimeoutException {
        webSocketStompClient = new WebSocketStompClient(new StandardWebSocketClient());
        webSocketStompClient.setMessageConverter(new MyMessageConverter());
        StompSessionHandler stompSessionHandler = new MyStompSessionHandler();
        StompSession stompSession = webSocketStompClient.connect(WEBSOCKET_ENDPOINT, stompSessionHandler).get();
        stompSession.subscribe("/topic/my-topic", new MyStompFrameHandler());
        stompSession.send("/app/my-url", "Hello!");
        return "OK";
    }

    public static void main(String[] args) {
        SpringApplication.run(WebSocketClient.class, args);
    }

    private class MyStompSessionHandler extends StompSessionHandlerAdapter {
        @Override
        public void afterConnected(StompSession session, StompHeaders connectedHeaders) {
            System.out.println("Connected to WebSocket server");
        }
    }

    private class MyStompFrameHandler implements StompFrameHandler {
        @Override
        public Type getPayloadType(StompHeaders headers) {
            return String.class;
        }

        @Override
        public void handleFrame(StompHeaders headers, Object payload) {
            System.out.println("Received message => " + payload);
        }
    }

    private class MyMessageConverter extends MappingJackson2MessageConverter {
        MyMessageConverter() {
            List<MediaType> supportedMediaTypes = new ArrayList<>();
            supportedMediaTypes.add(MediaType.TEXT_PLAIN);
            setSupportedMediaTypes(supportedMediaTypes);
        }
    }

}

创建Nginx配置文件/etc/nginx/sites-available/yourdomain.com如下:

upstream java {
  server localhost:8083;
}

server {
  listen 80;
  server_name yourdomain.com;
  location /my-websocket-endpoint {
    proxy_pass http://java;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
  }
}

启动Java WebSocket服务器、Java WebSocket客户端和Nginx:

$ ./mvnw spring-boot:run -Dserver.port=8083 &
$ ./mvnw spring-boot:run -Dserver.port=8084 &
$ sudo service nginx start

以上配置使得Nginx代理Websocket连接到Java WebSocket服务器,Java WebSocket客户端向服务器发送一个消息并收到回复,并将收到的消息打印到控制台。