日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

在 Docker 上运行一个 RESTful 风格的微服务

發布時間:2024/9/20 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 在 Docker 上运行一个 RESTful 风格的微服务 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

tags: Microservice Restful Docker

Author: Andy Ai
Weibo: NinetyH
GitHub: https://github.com/aiyanbo/do...


實現構思

  • 使用 Maven 進行項目構建

  • 使用 Jersey 實現一個 RESTful 風格的微服務

  • 在 Docker 里面執行 mvn package 對項目打包

  • 在 Docker 容器里運行這個微服務

  • 實現一個 RESTful 風格的微服務

    如果你對 RESTful 風格的 API 設計有疑惑,可以參考我的文章 RESTful Best Practices。

    場景 & 需求

    在 Maven 倉庫里面有許多的組件,我們現在暫且稱之為 Stack。在我們模擬的系統里面有下面2個需求:

  • 列出倉庫里的所有 Stack

  • 根據 Stack 的 ID 找到某一個組件,如果沒有找到則返回 Not Found

  • 現在,我們就根據這個需求一起踏入 Jersey 打造微服務的奇幻之旅。

    Step0. 準備

    使用 mvn 命令創建一個簡單工程

    mvn archetype:generate -DgroupId=org.jmotor -DartifactId=docker-restful-demo -DinteractiveMode=false

    在 pom.xml 加入 Jersey 等依賴

    <properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><junit.version>4.12</junit.version><jersey.version>2.18</jersey.version><javax.servlet.version>3.1.0</javax.servlet.version> </properties> <dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>${junit.version}</version><scope>test</scope></dependency><dependency><groupId>org.glassfish.jersey.containers</groupId><artifactId>jersey-container-grizzly2-http</artifactId><version>${jersey.version}</version></dependency><dependency><groupId>org.glassfish.jersey.media</groupId><artifactId>jersey-media-json-jackson</artifactId><version>${jersey.version}</version></dependency> </dependencies>

    Step1. 構建 Model

    Stack 包含了以下幾個屬性: id, groupId, artifactId, version。同時,Stack 類里面包含了一個 Builder 用來比較方便地創建一個 Stack 對象。這些都可以使用 IDE 自動生成,無需手動編寫。

    package org.jmotor.model;/*** Component:* Description:* Date: 2015/6/18** @author Andy Ai*/ public class Stack {private Integer id;private String groupId;private String artifactId;private String version;...getter and setter...public static class Builder {private Integer id;private String groupId;private String artifactId;private String version;public Builder id(Integer id) {this.id = id;return this;}...public Stack build() {Stack stack = new Stack();stack.setId(id);...return stack;}public static Builder newBuilder() {return new Builder();}} }

    Step2 創建一個 Restlet

    剛剛我們已經把 Model 做好了,現在我們就開始使用 Jersey 實現一個 Service,在 JAX-RS 中這樣的一個服務稱為 Resource。在這里,這個 Service(Resource) 提供了一個 RESTful 風格的接口訪問。所以我們稱之為 restlet。restlet 在 JAX-RS 或 Jersey 中并沒有這個概念,這是我們附加上去的用法。

    import javax.ws.rs.Path;@Path("/v1/stacks") public class StacksRestlet {}

    我們需要使用 javax.ws.rs.Path 這個注解來申明 Restlet 的根路徑是什么。在上面的代碼中, Restlet 的跟路徑是 /v1/stacks。

    Step3. 實現服務接口

    在 Jersey 里實現一個服務接口非常簡單,你只需要創建一個 public 的方法就可以了。

    接口1:

    @GET @Produces("application/json") public List<Stack> stacks() {return Arrays.asList(Stack.Builder.newBuilder().id(1).groupId("javax.servlet").artifactId("javax.servlet-api").version("3.1.0").build(),Stack.Builder.newBuilder().id(2).groupId("com.google.guava").artifactId("guava").version("18.0").build()); }

    我們使用 javax.ws.rs.GET 這個注解來申明接口接受的是 HTTP 請求的 GET 方法。javax.ws.rs.Produces("application/json") 用來表示我們這個接口返回的是 application/json 類型的數據。

    接口2:

    @GET @Path("{id}") @Produces("application/json") public Stack filterByArtifactId(@NotNull @PathParam("id") Integer id) {switch (id) {case 1:return Stack.Builder.newBuilder().id(1).groupId("javax.servlet").artifactId("javax.servlet-api").version("3.1.0").build();case 2:return Stack.Builder.newBuilder().id(2).groupId("com.google.guava").artifactId("guava").version("18.0").build();default:throw new WebApplicationException("Stack not found, id: " + id, 404);} }

    在上面的示例中:

  • @Path("{id}") 表示 id 是一個 url 上的動態參數,因為 id 是變化的,所以我們要做成一個 url 變量。然后在方法里面使用 @PathParam("id") 來獲得這個參數。JAX-RS 可以有許多類型的參數,例如:QueryParam 用來獲取 url 問號? 后面的查詢參數。你可以在 javax.ws.rs 這個包中找到其他的參數!

  • 使用 WebApplicationException 拋一個任何狀態的異常,例如: 404 或 500。

  • Step4. 運行微服務

    這里,我們使用 Jersey 的內置的 Grizzly 容器運行。

    final URI uri = UriBuilder.fromUri("http://localhost/").port(9998).build(); final ResourceConfig config = new ResourceConfig(); config.packages("org.jmotor.restlet"); final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(uri, config); Runtime.getRuntime().addShutdownHook(new Thread() {@Overridepublic void run() {server.shutdown();} }); try {server.start(); } catch (IOException e) {e.printStackTrace();System.exit(1); }

    Step5. 測試

    獲取 Stacks 列表

    $ curl http://localhost:9998/v1/stacks [{"id":1,"groupId":"javax.servlet","artifactId":"javax.servlet-api","version":"3.1.0"},{"id":2,"groupId":"com.google.gua va","artifactId":"guava","version":"18.0"}]

    根據 ID 獲取 Stack

    $ curl http://localhost:9998/v1/stacks/1 {"id":1,"groupId":"javax.servlet","artifactId":"javax.servlet-api","version":"3.1.0"}

    找不到的 Stack

    $ curl -I http://localhost:9998/v1/stacks/5 HTTP/1.1 404 Not Found Content-Length: 0 Date: Tue, 23 Jun 2015 06:04:19 GMT

    在 Docker 中運行

    首先,我們需要安裝一個 Docker 環境,你可以從 Docker Docs 上找到如何安裝它。

    安裝完成后,我們需要把我們的微服務打包成一個 Docker Image。下面,我們就使用 Dockerfile 來構建我們的 Docker Image。

    Step0. 準備

    剛剛我們已經成功地在 IDE 中運行了我們的微服務。但是如果需要讓它能獨立運行,我們需要把我們的工程通過 mvn 做成一個可以運行的包。但是因為我們在 Docker 中運行,所以我們只需要把相關的依賴復制到一個特地的地方就可以了。在下面的代碼中,我們把依賴放到了 ${project.build.directory}/lib 下。

    在 pom.xml 加入下面的代碼:

    <build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-dependency-plugin</artifactId><executions><execution><id>copy-dependencies</id><phase>package</phase><goals><goal>copy-dependencies</goal></goals><configuration><excludeScope>provided</excludeScope><outputDirectory>${project.build.directory}/lib</outputDirectory></configuration></execution></executions></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>1.7</source><target>1.7</target></configuration></plugin></plugins> </build>

    Step1. Dockerfile

    FROM jamesdbloom/docker-java8-mavenMAINTAINER Andy Ai "yanbo.ai@gmail.com"WORKDIR /codeADD pom.xml /code/pom.xml ADD src /code/src ADD settings.xml /root/.m2/settings.xmlRUN ["mvn", "package"]CMD ["java", "-cp", "target/lib/*:target/docker-restful-demo-1.0-SNAPSHOT.jar", "org.jmotor.StackMicroServices"]EXPOSE 9998

    Tips

  • ADD settings.xml /root/.m2/settings.xml,這是加入了本地的 maven settings。在這個文件里面你可能會使用到一些特定的配置,例如:maven 倉庫的代理鏡像。代理鏡像可以加快你的 Docker Build。

  • EXPOSE 9998 Docker 對外暴露的端口需要跟服務的端口是一致的。

  • Step2. Build Image

    cd docker-restful-demo docker build -t docker-restful-demo .

    上面代碼中,-t 是在 Docker Build 的時候指定 Image Tag。

    Step3. 運行 Image

    docker run -d -p 9998:9998 docker-restful-demo

    Tips
    -p 是發布一個 Docker 容器的端口到 Docker 運行的主機上。

    Step4. Docker 容器測試

    檢查 Docker 容器是否在運行

    $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTSNAMES bdda2408484a docker-restful-demo:latest "java -cp target/lib 31 seconds ago Up 29 seconds 0.0.0.0:9 998->9998/tcp fervent_swartz

    檢查 Docker 容器內的服務是否已經啟動:

    • 登錄到 Docker 容器:

    docker exec -i -t bdda2408484a bash
    • 查看服務端口信息

    $ ss -a Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port nl UNCONN 0 0 rtnl:kernel * nl UNCONN 4352 0 tcpdiag:ss/92 * nl UNCONN 768 0 tcpdiag:kernel * nl UNCONN 0 0 6:kernel * nl UNCONN 0 0 10:kernel * nl UNCONN 0 0 12:kernel * nl UNCONN 0 0 15:kernel * nl UNCONN 0 0 16:kernel * u_str ESTAB 0 0 * 9590 * 0 tcp LISTEN 0 128 ::ffff:127.0.0.1:9998 :::*
    • 測試接口

    $ curl -i http://localhost:9998/v1/stacks HTTP/1.1 200 OK Content-Type: application/json Date: Tue, 23 Jun 2015 07:51:15 GMT Content-Length: 163[{"id":1,"groupId":"javax.servlet","artifactId":"javax.servlet-api","version":"3.1.0"},{"id":2,"groupId":"com.google.gua va","artifactId":"guava","version":"18.0"}]
    • 退出 Docker 容器

    exit

    Step5. 遠程調用測試

    • 如果你使用的是 boot2docker, 需要拿到 boot2docker 虛擬機的IP

    $ boot2docker ip 192.168.59.103
    • 調用遠程接口

    $ curl http://192.168.59.103:9998/v1/stacks curl: (7) Failed to connect to 192.168.59.103 port 9998: Connection refused

    如果遇到上面的錯誤,我們可以通過2種方式去解決它:
    方法1: 將程序綁定全零IP的端口

    檢查 Docker 容器綁定的端口:

    $ docker port bdda2408484a 9998/tcp -> 0.0.0.0:9998

    我們看到的是 9998 這個端口綁定在 0.0.0.0 上,這時需要把 Jersey 容器的 URI 改成 0.0.0.0 就可以,像這樣:

    final URI uri = UriBuilder.fromUri("http://localhost/").port(9998).build();--->final URI uri = UriBuilder.fromUri("http://0.0.0.0/").port(9998).build();

    方法2: 將程序綁定到非回路的IP端口上

    查看 Docker 容器 IP 地址的方法:

    boot2docker sshdocker inspect --format '{{.NetworkSettings.IPAddress}}' $container_id

    使用 Java 接口獲取本機的非回路IP地址:

    final URI uri = UriBuilder.fromUri("http://localhost/").port(9998).build();--->InetAddress inetAddress = localInet4Address(); String host = "0.0.0.0"; if (inetAddress != null) {host = inetAddress.getHostAddress(); } final URI uri = UriBuilder.fromUri("http://" + host + "/").port(9998).build();private static InetAddress localInet4Address() throws SocketException {Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();while (networkInterfaces.hasMoreElements()) {NetworkInterface networkInterface = networkInterfaces.nextElement();Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();while (inetAddresses.hasMoreElements()) {InetAddress inetAddress = inetAddresses.nextElement();if (!inetAddress.isLoopbackAddress() && inetAddress instanceof Inet4Address) {return inetAddress;}}}return null; }

    使用上面的任何一種方法,服務都能正常調用:

    $ curl -i http://192.168.59.103:9998/v1/stacks HTTP/1.1 200 OK Content-Type: application/json Date: Tue, 23 Jun 2015 07:53:24 GMT Content-Length: 163[{"id":1,"groupId":"javax.servlet","artifactId":"javax.servlet-api","version":"3.1.0"},{"id":2,"groupId":"com.google.gua va","artifactId":"guava","version":"18.0"}]

    加速器

    在撰寫此文的時候,我使用的是 DaoColud 的鏡像在做加速。 具體步驟請查看 DaoColud Mirror 文檔。

    如果你在 Windows 上使用 Boot2Docker, 可以按照下列步驟設置你的 Docker 鏡像倉庫:

    boot2docker sshsudo su echo "EXTRA_ARGS=\"--registry-mirror=http://98bc3dca.m.daocloud.io\"" >> /var/lib/boot2docker/profile exitboot2docker restart

    參考資料

    https://dashboard.daocloud.io...
    http://martinfowler.com/artic...
    https://jersey.java.net/docum...
    https://blog.giantswarm.io/ge...

    未經同意不可轉載, 轉載需保留原文鏈接與作者署名。

    總結

    以上是生活随笔為你收集整理的在 Docker 上运行一个 RESTful 风格的微服务的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。