JAVA 多用戶商城系統b2b2c-eureka 實現微服務入門程序

在寫代碼以前先了解幾個概念。java

單系統架構mysql

須要JAVA Spring Cloud大型企業分佈式微服務雲構建的B2B2C電子商務平臺源碼 一零三八七七四六二六nginx

將業務邏輯層、數據庫訪問層、控制層放入在一個項目中。 優勢:適合於我的或者小團隊開發,不適合大團隊開發。程序員

分佈式項目架構web

根據業務需求進行拆分紅N個子系統,多個子系統相互協做才能完成業務流程子系統之間通信使用RPC遠程通信技術。 優勢:spring

1.把模塊拆分,使用接口通訊,下降模塊之間的耦合度。sql

2.把項目拆分紅若干個子項目,不一樣的團隊負責不一樣的子項目。數據庫

3.增長功能時只須要再增長一個子項目,調用其它系統的接口就能夠。apache

4.能夠靈活的進行分佈式部署。api

有優勢就有缺點,缺點以下:

1.系統之間交互須要使用遠程通訊,接口開發增長工做量。

2.各個模塊有一些通用的業務邏輯沒法共用。

爲了解決上面分佈式架構的缺點,咱們引入了soa架構,SOA:Service Oriented Architecture面向服務的架構。也就是把工程拆分紅服務層、表現層兩個工程。服務層中包含業務邏輯,只須要對外提供服務便可。表現層只須要處理和頁面的交互,業務邏輯都是調用服務層的服務來實現。

什麼是項目集羣

多臺服務器部署相同應用構成一個集羣

做用:經過負載均衡設備共同對外提供服務

RPC遠程調用

RPC 的全稱是 Remote Procedure Call 是一種進程間通訊方式。

它容許程序調用另外一個地址空間(一般是共享網絡的另外一臺機器上)的過程或函數,而不用程序員顯式編碼這個遠程調用的細節。即不管是調用本地接口/服務的仍是遠程的接口/服務,本質上編寫的調用代碼基本相同。

好比兩臺服務器A,B,一個應用部署在A服務器上,想要調用B服務器上應用提供的函數或者方法,因爲不在一個內存空間,不能直接調用,這時候須要經過就能夠應用RPC框架的實現來解決

restful、soap、rpc

(1)restful是一種架構設計風格,提供了設計原則和約束條件,而不是架構。而知足這些約束條件和原則的應用程序或設 計就是 RESTful架構或服務。

(2)soap象訪問協議是一種數據交換協議規範,是一種輕量的、簡單的、基於XML的協議的規範。SOAP協議和HTTP協議 同樣,都是底層的通訊協議,只是請求包的格式不一樣而已,SOAP包是XML格式的。

soap基於xml並封裝成了符合http協議,所以,它符合任何路由器、 防火牆或代理服務器的要求。

soap可使用任何語言來完成,只要發送正確的soap請求便可,基於soap的服務能夠在任何平臺無需修改便可正常使用。

(3)RPC就是從一臺機器(客戶端)上經過參數傳遞的方式調用另外一臺機器(服務器)上的一個函數或方法(能夠統稱爲服務)並獲得返回的結果。

RPC 會隱藏底層的通信細節(不須要直接處理Socket通信或Http通信)

RPC 是一個請求響應模型。客戶端發起請求,服務器返回響應(相似於Http的工做方式)

RPC 在使用形式上像調用本地函數(或方法)同樣去調用遠程的函數(或方法)。

rpc遠程調用框架

幾種比較典型的RPC的實現和調用框架。

(1)RMI實現,利用java.rmi包實現,基於Java遠程方法協議(Java Remote Method Protocol) 和java的原生序列化。

(2)Hessian,是一個輕量級的remoting onhttp工具,使用簡單的方法提供了RMI的功能。 基於HTTP協議,採用二進制編解碼。

(3)thrift是一種可伸縮的跨語言服務的軟件框架。thrift容許你定義一個描述文件,描述數據類型和服務接口。依據該文件,編譯器方便地生成RPC客戶端和服務器通訊代碼。

(4)SpringCloud 爲開發人員提供了快速構建分佈式系統的一些工具,包括配置管理、服務發現、斷路器、路由、微代理、事件總線、全局鎖、決策競選、分佈式會話等等。

(4) Dubbo是阿里巴巴公司開源的一個高性能優秀的服務框架,使得應用可經過高性能的 RPC 實現服務的輸出和輸入功能,能夠和 Spring框架無縫集成。

面向於服務架構(SOA)

業務系統分解爲多個組件,讓每一個組件都獨立提供離散,自治,可複用的服務能力,經過服務的組合和編排來實現上層的業務流程 做用:簡化維護,下降總體風險,伸縮靈活

什麼是微服務架構

架構設計概念,各服務間隔離(分佈式也是隔離),自治(分佈式依賴總體組合)其它特性(單一職責,邊界,異步通訊,獨立部署)是分佈式概念的跟嚴格執行 SOA到微服務架構的演進過程

做用:各服務可獨立應用,組合服務也可系統應用(巨石應用[monolith]的簡化實現策略-平臺思想)

服務提供者與消費關係

服務提供者:提供服務被人調用

消費者:調用被人服務

服務的註冊與發現(Eureka )

在這裏,咱們須要用的的組件上Spring Cloud Netflix的Eureka ,eureka是一個服務註冊和發現模塊。

eureka.png

什麼是Eureka

官方的介紹在這裏Eureka wiki。Eureka是Netflix開源的一個RESTful服務,主要用於服務的註冊發現。Eureka由兩個組件組成:Eureka服務器和Eureka客戶端。Eureka服務器用做服務註冊服務器。Eureka客戶端是一個java客戶端,用來簡化與服務器的交互、做爲輪詢負載均衡器,並提供服務的故障切換支持。Netflix在其生產環境中使用的是另外的客戶端,它提供基於流量、資源利用率以及出錯狀態的加權負載均衡。

在我看來,Eureka的吸引力來源於如下幾點:

開源:你們能夠對實現一探究竟,甚至修改源碼。

可靠:通過Netflix多年的生產環境考驗,使用應該比較靠譜省心

功能齊全:不但提供了完整的註冊發現服務,還有Ribbon等能夠配合使用的服務。

基於Java:對於Java程序員來講,使用起來,內心比較有底。

Spring Cloud,與Eureka進行了很好的集成,使用起來很是方便。

代碼實現

Eureka服務端,代碼結構以下:

Eureka2.png
EurekaApplication.java

package com.example.eureka;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer //這個註釋必定要加 eureka服務端
public class EurekaApplication {

    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class, args);
    }
}
複製代碼

application.yml

server:
  port: 8888
eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      #eureka 訪問地址爲 http://localhost:8888
複製代碼

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>service-member</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>service-member</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>
複製代碼

啓動 EurekaApplication.java

訪問http://localhost:8888,如圖:

eureka3.png
因爲沒有如今尚未服務註冊進來,因此實例爲空。

下面咱們寫個服務註冊到eureka上面:

代碼結構以下圖:

eureka4.png

MemberController.java

package com.example.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

@RestController
public class MemberController {
    @Value("${server.port}")
    private String port; //port 參數用來測試 當這個服務啓動多個的時候 客戶端訪問的時候採用輪詢的方式

    @RequestMapping("/getUser")
    public List getUser(){
        List list = new ArrayList();
        list.add("aaa");
        list.add("bbb");
        list.add(port);
        return list;
    }
}
複製代碼

ServiceMemberApplication.java

package com.example.servicemember;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@EnableEurekaClient //eureka 客戶端
@ComponentScan("com.example.controller")//掃描包
public class ServiceMemberApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServiceMemberApplication.class, args);
    }
}
複製代碼

application.yml

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8888/eureka/
      #將服務註冊到上面的地址
server:
  port: 8762
spring:
  application:
    name: service-member
複製代碼

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>service-member</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>service-member</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.M9</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>


</project>
複製代碼

啓動ServiceMemberApplication.java ,啓動完畢,這時候這個服務就被註冊到eureka中了。打開http://localhost:8888/,如圖:

eureka5.png
如圖,服務被註冊進來了。

接下來,寫一個調用SERVICE-MEMBER服務的服務,即消費者服務。

項目結構以下圖:

eureka6.png
OrderController.java

package com.example.controller;

import com.example.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class OrderController {
    @Autowired
    private OrderService orderService;

    @RequestMapping("/getUser")
    public List<String> getUser(){
        return orderService.getMember();
    }



}
複製代碼

OrderService.java

package com.example.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@Service
public class OrderService {
    @Autowired
    private RestTemplate restTemplate;

    public List<String> getMember(){
        return restTemplate.getForObject("http://service-member/getUser",List.class);
    }
}
複製代碼

ServiceOrderApplication.java

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@EnableEurekaClient
@SpringBootApplication
public class ServiceOrderApplication {

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

    @Bean
    @LoadBalanced //ribbon實現負載均衡,須要加入ribbon依賴
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
複製代碼

application.yml

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8888/eureka/
      #將該服務註冊到上面地址的eureka中
server:
  port: 8764
  tomcat:
    max-threads: 50
spring:
  application:
    name: service-order
複製代碼

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.10.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>
複製代碼

好了,啓動ServiceOrderApplication ,再打開http://localhost:8888/ 會發現又多了一個服務,以下圖:

eureka7.png
咱們訪問getUser這個接口,獲得結果:如圖:

eureka8.png

這樣咱們一個簡單的微服務就寫完了。

咱們能夠將服務提供者service-member的端口改掉,如今是8762,咱們將端口改成8763,再啓動一下service-member,以前啓動的8762不要停,如今至關於service-member這個服務有兩個。再刷新一下http://localhost:8888/ 這個地址,發現service-member變成了兩個。以下圖:

eureka9.png
這是咱們重複的去訪問getUser這個接口,發現返回的port這個參數在8762和8763之間來回切換,這個就說明是對service-member服務的輪詢訪問。

zuul攔截

Zuul的主要功能是路由轉發和過濾器。路由功能是微服務的一部分,好比/api/user轉發到到user服務,/api/shop轉發到到shop服務。zuul默認和Ribbon結合實現了負載均衡的功能, 相似於nginx轉發。

新建一個工程用來實現zuul轉發功能。

項目結構以下圖:

eureka10-zuul.png
依賴:pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>service-zuul</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>service-zuul</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.RC1</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>


</project>
複製代碼

ServiceZuulApplication.java

package com.example.servicezuul;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@EnableZuulProxy //必需要加這個註解
@EnableEurekaClient
@SpringBootApplication
public class ServiceZuulApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServiceZuulApplication.class, args);
    }
}
複製代碼

application.yml

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8888/eureka/
server:
  port: 8769
spring:
  application:
    name: service-zuul
zuul:
  routes:
    api-a:
      path: /api-member/**
      #path自定義便可
      service-id: service-member 
      #service-id是對應得服務工程名,這裏表示訪問/api-member/** 會轉發到service-member這個服務
    api-b:
      path: /api-order/**
      service-id: service-order
複製代碼

以上,zuul轉發就配置好了,當訪問http://localhost:8769/api-member/getUser 是就會轉發到service-member這個服務的getUser接口。

ZuulFilter攔截器

對全部請求進行攔截過濾,新建攔截器,MyFilter.java

package com.example.servicezuul;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
@Component
public class MyFilter extends ZuulFilter {
    private static Logger logger = LoggerFactory.getLogger(MyFilter.class);

    /**
     * filterType:返回一個字符串表明過濾器的類型,在zuul中定義了四種不一樣生命週期的過濾器類型,具體以下:
     * pre:能夠在請求被路由以前調用
     * route:在路由請求時候被調用
     * post:在route和error過濾器以後被調用
     * error:處理請求時發生錯誤時被調用
     * @return
     */
    @Override
    public String filterType() {
        return "pre";// 前置過濾器
    }

    /**
     * filterOrder:經過int值來定義過濾器的執行順序
     * @return
     */
    @Override
    public int filterOrder() {
        return 0;// 優先級爲0,數字越大,優先級越低
    }

    /**
     * shouldFilter:返回一個boolean類型來判斷該過濾器是否要執行,因此經過此函數可實現過濾器的開關。在上例中,咱們直接返回true,因此該過濾器老是生效
     * @return
     */
    @Override
    public boolean shouldFilter() {
        return true;// 是否執行該過濾器,此處爲true,說明須要過濾
    }

    /**
     * run:過濾器的具體邏輯。須要注意,這裏咱們經過ctx.setSendZuulResponse(false)令zuul過濾該請求,不對其進行路由,而後經過ctx.setResponseStatusCode(401)設置了其返回的錯誤碼
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        logger.info(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));
        String refer=request.getHeader("refer");
        Object accessToken = request.getParameter("token");
        if (accessToken != null) {
            return null;
        }
        logger.warn("token is empty");
        ctx.setSendZuulResponse(false);//過濾該請求,不對其進行路由
        ctx.setResponseStatusCode(401);
        try {
            ctx.getResponse().getWriter().write("token is empty");
        } catch (Exception e) {
        }
        return null;
    }

    public static void main(String[] args) {
        logger.info(String.format("%s >>> %s", "jiangjz", "www.baidu.con"));
    }
}
複製代碼

java B2B2C Springcloud多租戶電子商城系統

相關文章
相關標籤/搜索