Hadoop的Map側join

来源:http://www.cnblogs.com/sdaassjjsd/archive/2016/06/17/5593157.html
-Advertisement-
Play Games

寫了關於Hadoop的Map側join 和Reduce的join,今天我們就來在看另外一種比較中立的Join。 SemiJoin,一般稱為半鏈接,其原理是在Map側過濾掉了一些不需要join的數據,從而大大減少了reduce的shffule時間,因為我們知道,如果僅僅使用Reduce側連接,那麼如果 ...


寫了關於Hadoop的Map側join 
和Reduce的join,今天我們就來在看另外一種比較中立的Join。 

SemiJoin,一般稱為半鏈接,其原理是在Map側過濾掉了一些不需要join的數據,從而大大減少了reduce的shffule時間,因為我們知道,如果僅僅使用Reduce側連接,那麼如果一份數據中,存在大量的無效數據,而這些數據,在join中,並不需要,但是因為沒有做過預處理,所以這些數據,直到真正的執行reduce函數時,才被定義為無效數據,而這時候,前面已經執行過shuffle和merge和sort,所以這部分無效的數據,就浪費了大量的網路IO和磁碟IO,所以在整體來講,這是一種降低性能的表現,如果存在的無效數據越多,那麼這種趨勢,就越明顯。 

之所以會出現半連接,這其實也是reduce側連接的一個變種,只不過我們在Map側,過濾掉了一些無效的數據,所以減少了reduce過程的shuffle時間,所以能獲取一個性能的提升。 

具體的原理也是利用DistributedCache將小表的的分發到各個節點上,在Map過程的setup函數里,讀取緩存裡面的文件,只將小表的鏈接鍵存儲在hashset里,在map函數執行時,對每一條數據,進行判斷,如果這條數據的鏈接鍵為空或者在hashset裡面不存在,那麼則認為這條數據,是無效的數據,所以這條數據,並不會被partition分區後寫入磁碟,參與reduce階段的shuffle和sort,所以在一定程式上,提升了join性能。需要註意的是如果 
小表的key依然非常巨大,可能會導致我們的程式出現OOM的情況,那麼這時候我們就需要考慮其他的鏈接方式了。 

測試數據如下: 
模擬小表數據: 
1,三劫散仙,13575468248 
2,鳳舞九天,18965235874 
3,忙忙碌碌,15986854789 
4,少林寺方丈,15698745862 


模擬大表數據: 
3,A,99,2013-03-05 
1,B,89,2013-02-05 
2,C,69,2013-03-09 
3,D,56,2013-06-07 
5,E,100,2013-09-09 
6,H,200,2014-01-10 

代碼如下: 

Java代碼【下載地址】   複製代碼 收藏代碼
  1. package com.semijoin;  
  2.   
  3. import java.io.BufferedReader;  
  4. import java.io.DataInput;  
  5. import java.io.DataOutput;  
  6. import java.io.FileReader;  
  7. import java.io.IOException;  
  8. import java.net.URI;  
  9. import java.util.ArrayList;  
  10. import java.util.HashSet;  
  11. import java.util.List;  
  12.   
  13. import org.apache.hadoop.conf.Configuration;  
  14. import org.apache.hadoop.filecache.DistributedCache;  
  15. import org.apache.hadoop.fs.FileSystem;  
  16. import org.apache.hadoop.fs.Path;  
  17. import org.apache.hadoop.io.LongWritable;  
  18. import org.apache.hadoop.io.Text;  
  19. import org.apache.hadoop.io.WritableComparable;  
  20.   
  21. import org.apache.hadoop.mapred.JobConf;  
  22. import org.apache.hadoop.mapreduce.Job;  
  23. import org.apache.hadoop.mapreduce.Mapper;  
  24. import org.apache.hadoop.mapreduce.Reducer;  
  25. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;  
  26. import org.apache.hadoop.mapreduce.lib.input.FileSplit;  
  27. import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;  
  28. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;  
  29. import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;  
  30.   
  31. /*** 
  32.  *  
  33.  * Hadoop1.2的版本 
  34.  *  
  35.  * hadoop的半鏈接 
  36.  *  
  37.  * SemiJoin實現 
  38.  *  
  39.  * @author qindongliang 
  40.  *  
  41.  *    大數據交流群:376932160 
  42.  *  搜索技術交流群:324714439 
  43.  *  
  44.  *  
  45.  *  
  46.  * **/  
  47. public class Semjoin {  
  48.       
  49.       
  50.       
  51.     /** 
  52.      *  
  53.      *  
  54.      * 自定義一個輸出實體 
  55.      *  
  56.      * **/  
  57.     private static class CombineEntity implements WritableComparable<CombineEntity>{  
  58.   
  59.           
  60.         private Text joinKey;//連接key  
  61.         private Text flag;//文件來源標誌  
  62.         private Text secondPart;//除了鍵外的其他部分的數據  
  63.           
  64.           
  65.         public CombineEntity() {  
  66.             // TODO Auto-generated constructor stub  
  67.             this.joinKey=new Text();  
  68.             this.flag=new Text();  
  69.             this.secondPart=new Text();  
  70.         }  
  71.           
  72.         public Text getJoinKey() {  
  73.             return joinKey;  
  74.         }  
  75.   
  76.         public void setJoinKey(Text joinKey) {  
  77.             this.joinKey = joinKey;  
  78.         }  
  79.   
  80.         public Text getFlag() {  
  81.             return flag;  
  82.         }  
  83.   
  84.         public void setFlag(Text flag) {  
  85.             this.flag = flag;  
  86.         }  
  87.   
  88.         public Text getSecondPart() {  
  89.             return secondPart;  
  90.         }  
  91.   
  92.         public void setSecondPart(Text secondPart) {  
  93.             this.secondPart = secondPart;  
  94.         }  
  95.   
  96.         @Override  
  97.         public void readFields(DataInput in) throws IOException {  
  98.             this.joinKey.readFields(in);  
  99.             this.flag.readFields(in);  
  100.             this.secondPart.readFields(in);  
  101.               
  102.         }  
  103.   
  104.         @Override  
  105.         public void write(DataOutput out) throws IOException {  
  106.             this.joinKey.write(out);  
  107.             this.flag.write(out);  
  108.             this.secondPart.write(out);  
  109.               
  110.         }  
  111.   
  112.         @Override  
  113.         public int compareTo(CombineEntity o) {  
  114.             // TODO Auto-generated method stub  
  115.             return this.joinKey.compareTo(o.joinKey);  
  116.         }  
  117.           
  118.           
  119.           
  120.     }  
  121.       
  122.       
  123.       
  124.       
  125.     private static class JMapper extends Mapper<LongWritable, Text, Text, CombineEntity>{  
  126.           
  127.         private CombineEntity combine=new CombineEntity();  
  128.         private Text flag=new Text();  
  129.         private  Text joinKey=new Text();  
  130.         private Text secondPart=new Text();  
  131.         /** 
  132.          * 存儲小表的key 
  133.          *  
  134.          *  
  135.          * */  
  136.         private HashSet<String> joinKeySet=new HashSet<String>();  
  137.           
  138.           
  139.         @Override  
  140.         protected void setup(Context context)throws IOException, InterruptedException {  
  141.            
  142.             //讀取文件流  
  143.             BufferedReader br=null;  
  144.             String temp;  
  145.             // 獲取DistributedCached裡面 的共用文件  
  146.             Path path[]=DistributedCache.getLocalCacheFiles(context.getConfiguration());  
  147.               
  148.             for(Path p:path){  
  149.                   
  150.                 if(p.getName().endsWith("a.txt")){  
  151.                     br=new BufferedReader(new FileReader(p.toString()));  
  152.                     //List<String> list=Files.readAllLines(Paths.get(p.getName()), Charset.forName("UTF-8"));  
  153.                       
  154.                     while((temp=br.readLine())!=null){  
  155.                         String ss[]=temp.split(",");  
  156.                         //map.put(ss[0], ss[1]+"\t"+ss[2]);//放入hash表中  
  157.                         joinKeySet.add(ss[0]);//加入小表的key  
  158.                     }  
  159.                 }  
  160.             }  
  161.               
  162.               
  163.         }  
  164.           
  165.           
  166.           
  167.         @Override  
  168.         protected void map(LongWritable key, Text value,Context context)  
  169.                 throws IOException, InterruptedException {  
  170.               
  171.           
  172.                //獲得文件輸入路徑  
  173.             String pathName = ((FileSplit) context.getInputSplit()).getPath().toString();  
  174.           
  175.             if(pathName.endsWith("a.txt")){  
  176.                   
  177.                 String  valueItems[]=value.toString().split(",");  
  178.                   
  179.                   
  180.                 /** 
  181.                  * 在這裡過濾必須要的連接字元 
  182.                  *  
  183.                  * */  
  184.                 if(joinKeySet.contains(valueItems[0])){  
  185.                     //設置標誌位  
  186.                     flag.set("0");     
  187.                     //設置鏈接鍵  
  188.                     joinKey.set(valueItems[0]);            
  189.                     //設置第二部分  
  190.                     secondPart.set(valueItems[1]+"\t"+valueItems[2]);  
  191.                       
  192.                     //封裝實體  
  193.                     combine.setFlag(flag);//標誌位  
  194.                     combine.setJoinKey(joinKey);//鏈接鍵  
  195.                     combine.setSecondPart(secondPart);//其他部分  
  196.                       
  197.                      //寫出  
  198.                     context.write(combine.getJoinKey(), combine);     
  199.                 }else{  
  200.                     System.out.println("a.txt里");  
  201.                     System.out.println("在小表中無此記錄,執行過濾掉!");  
  202.                     for(String v:valueItems){  
  203.                         System.out.print(v+"   ");  
  204.                     }  
  205.                       
  206.                     return ;  
  207.                       
  208.                 }  
  209.                   
  210.                   
  211.                   
  212.             }else if(pathName.endsWith("b.txt")){  
  213.                 String  valueItems[]=value.toString().split(",");  
  214.                   
  215.                 /** 
  216.                  *  
  217.                  * 判斷是否在集合中 
  218.                  *  
  219.                  * */  
  220.                 if(joinKeySet.contains(valueItems[0])){  
  221.                       
  222.   
  223.                     //設置標誌位  
  224.                     flag.set("1");     
  225.                       
  226.                     //設置鏈接鍵  
  227.                     joinKey.set(valueItems[0]);  
  228.             
  229.                     //設置第二部分註意不同的文件的列數不一樣  
  230.                     secondPart.set(valueItems[1]+"\t"+valueItems[2]+"\t"+valueItems[3]);  
  231.                       
  232.                     //封裝實體  
  233.                     combine.setFlag(flag);//標誌位  
  234.                     combine.setJoinKey(joinKey);//鏈接鍵  
  235.                     combine.setSecondPart(secondPart);//其他部分  
  236.                       
  237.                      //寫出  
  238.                     context.write(combine.getJoinKey(), combine);  
  239.                       
  240.                       
  241.                 }else{                    
  242.                     //執行過濾 ......  
  243.                     System.out.println("b.txt里");  
  244.                     System.out.println("在小表中無此記錄,執行過濾掉!");  
  245.                     for(String v:valueItems){  
  246.                         System.out.print(v+"   ");  
  247.                     }  
  248.                       
  249.                     return ;  
  250.                       
  251.                       
  252.                 }  
  253.                   
  254.               
  255.                   
  256.                   
  257.             }  
  258.               
  259.               
  260.               
  261.               
  262.                
  263.            
  264.               
  265.         }  
  266.           
  267.     }  
  268.       
  269.       
  270.     private static class JReduce extends Reducer<Text, CombineEntity, Text, Text>{  
  271.           
  272.           
  273.         //存儲一個分組中左表信息  
  274.         private List<Text> leftTable=new ArrayList<Text>();  
  275.         //存儲一個分組中右表信息  
  276.         private List<Text> rightTable=new ArrayList<Text>();  
  277.           
  278.         private Text secondPart=null;  
  279.           
  280.         private Text output=new Text();  
  281.           
  282.           
  283.            
  284.         //一個分組調用一次  
  285.         @Override  
  286.         protected void reduce(Text key, Iterable<CombineEntity> values,Context context)  
  287.                 throws IOException, InterruptedException {  
  288.              leftTable.clear();//清空分組數據  
  289.              rightTable.clear();//清空分組數據  
  290.                
  291.                
  292.              /** 
  293.               * 將不同文件的數據,分別放在不同的集合 
  294.               * 中,註意數據量過大時,會出現 
  295.               * OOM的異常 
  296.               *  
  297.               * **/  
  298.                
  299.              for(CombineEntity ce:values){  
  300.                    
  301.                  this.secondPart=new Text(ce.getSecondPart().toString());  
  302.                    
  303.                    
  304.                  //左表  
  305.                    
  306.                  if(ce.getFlag().toString().trim().equals("0")){  
  307.                      leftTable.add(secondPart);  
  308.                        
  309.                  }else if(ce.getFlag().toString().trim().equals("1")){  
  310.                        
  311.                      rightTable.add(secondPart);  
  312.                        
  313.                  }  
  314.                    
  315.                    
  316.                    
  317.                    
  318.              }  
  319.                
  320.              //=====================  
  321.              for(Text left:leftTable){  
  322.                    
  323.                  for(Text right:rightTable){  
  324.                        
  325.                      output.set(left+"\t"+right);//連接左右數據  
  326.                      context.write(key, output);//輸出  
  327.                  }  
  328.                    
  329.              }  
  330.                
  331.                
  332.                
  333.               
  334.         }  
  335.           
  336.     }  
  337.       
  338.       
  339.       
  340.       
  341.       
  342.       
  343.       
  344.       
  345.     public static void main(String[] args)throws Exception {  
  346.           
  347.            
  348.       
  349.       
  350.          //Job job=new Job(conf,"myjoin");  
  351.          JobConf conf=new JobConf(Semjoin.class);   
  352.            conf.set("mapred.job.tracker","192.168.75.130:9001");  
  353.             conf.setJar("tt.jar");  
  354.             
  355.             
  356.             //小表共用  
  357.             String bpath="hdfs://192.168.75.130:9000/root/dist/a.txt";  
  358.             //添加到共用cache里  
  359.         DistributedCache.addCacheFile(new URI(bpath), conf);  
  360.           
  361.           Job job=new Job(conf, "aaaaa");  
  362.          job.setJarByClass(Semjoin.class);  
  363.          System.out.println("模式:  "+conf.get("mapred.job.tracker"));;  
  364.            
  365.            
  366.          //設置Map和Reduce自定義類  
  367.          job.setMapperClass(JMapper.class);  
  368.          job.setReducerClass(JReduce.class);  
  369.            
  370.          //設置Map端輸出  
  371.          job.setMapOutputKeyClass(Text.class);  
  372.          job.setMapOutputValueClass(CombineEntity.class);  
  373.            
  374.          //設置Reduce端的輸出  
  375.          job.setOutputKeyClass(Text.class);  
  376.          job.setOutputValueClass(Text.class);  
  377.            
  378.       
  379.          job.setInputFormatClass(TextInputFormat.class);  
  380.          job.setOutputFormatClass(TextOutputFormat.class);  
  381.            
  382.        
  383.          FileSystem fs=FileSystem.get(conf);  
  384.            
  385.          Path op=new Path("hdfs://192.168.75.130:9000/root/outputjoindbnew4");  
  386.            
  387.          if(fs.exists(op)){  
  388.              fs.delete(op, true);  
  389.              System.out.println("存在此輸出路徑,已刪除!!!");  
  390.          }  
  391.            
  392.            
  393.       FileInputFormat.setInputPaths(job, new Path("hdfs://192.168.75.130:9000/root/inputjoindb"));  
  394.       FileOutputFormat.setOutputPath(job, op);  
  395.          
  396.       System.exit(job.waitForCompletion(true)?0:1);  
  397.         
  398.         
  399.            
  400.            
  401.            
  402.            
  403.            
  404.            
  405.            
  406.                   
  407.                    
  408.           
  409.           
  410.           
  411.     }  
  412.       
  413.       
  414.       
  415.   
  416. }  
Java代碼  收藏代碼
  1. package com.semijoin;  
  2.   
  3. import java.io.BufferedReader;  
  4. import java.io.DataInput;  
  5. import java.io.DataOutput;  
  6. import java.io.FileReader;  
  7. import java.io.IOException;  
  8. import java.net.URI;  
  9. import java.util.ArrayList;  
  10. import java.util.HashSet;  
  11. import java.util.List;  
  12.   
  13. import org.apache.hadoop.conf.Configuration;  
  14. import org.apache.hadoop.filecache.DistributedCache;  
  15. import org.apache.hadoop.fs.FileSystem;  
  16. import org.apache.hadoop.fs.Path;  
  17. import org.apache.hadoop.io.LongWritable;  
  18. import org.apache.hadoop.io.Text;  
  19. import org.apache.hadoop.io.WritableComparable;  
  20.   
  21. import org.apache.hadoop.mapred.JobConf;  
  22. import org.apache.hadoop.mapreduce.Job;  
  23. import org.apache.hadoop.mapreduce.Mapper;  
  24. import org.apache.hadoop.mapreduce.Reducer;  
  25. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;  
  26. import org.apache.hadoop.mapreduce.lib.input.FileSplit;  
  27. import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;  
  28. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;  
  29. import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;  
  30.   
  31. /*** 
  32.  *  
  33.  * Hadoop1.2的版本 
  34.  *  
  35.  * hadoop的半鏈接 
  36.  *  
  37.  * SemiJoin實現 
  38.  *  
  39.  * @author qindongliang 
  40.  *  
  41.  *    大數據交流群:376932160 
  42.  *  搜索技術交流群:324714439 
  43.  *  
  44.  *  
  45.  *  
  46.  * **/  
  47. public class Semjoin {  
  48.       
  49.       
  50.       
  51.     /** 
  52.      *  
  53.      *  
  54.      * 自定義一個輸出實體 
  55.      *  
  56.      * **/  
  57.     private static class CombineEntity implements WritableComparable<CombineEntity>{  
  58.   
  59.           
  60.         private Text joinKey;//連接key  
  61.         private Text flag;//文件來源標誌  
  62.         private Text secondPart;//除了鍵外的其他部分的數據  
  63.           
  64.           
  65.         public CombineEntity() {  
  66.             // TODO Auto-generated constructor stub  
  67.             this.joinKey=new Text();  
  68.             this.flag=new Text();  
  69.             this.secondPart=new Text();  
  70.         }  
  71.           
  72.         public Text getJoinKey() {  
  73.             return joinKey;  
  74.         }  
  75.   
  76.         public void setJoinKey(Text joinKey) {  
  77.             this.joinKey = joinKey;  
  78.         }  
  79.   
  80.         public Text getFlag() {  
  81.             return flag;  
  82.         }  
  83.   
  84.         public void setFlag(Text flag) {  
  85.             this.flag = flag;  
  86.         }  
  87.   
  88.         public Text getSecondPart() {  
  89.             return secondPart;  
  90.         }  
  91.   
  92.         public void setSecondPart(Text secondPart) {  
  93.             this.secondPart = secondPart;  
  94.         }  
  95.   
  96.         @Override  
  97.         public void readFields(DataInput in) throws IOException {  
  98.             this.joinKey.readFields(in);  
  99.             this.flag.readFields(in);  
  100.       &
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近在實現最土團購系統的價格排序功能,需要對$oc數組進行擴展,經過測試用下麵的方法即可。 核心代碼如下: 因為是多條件查詢所以需要先判斷是否為空,然後再添加到數組裡面。 推薦:http://www.cnblogs.com/roucheng/p/3528396.html ...
  • 這篇文章主要介紹了Java實現時間動態顯示方法彙總,很實用的功能,需要的朋友可以參考下 本文所述實例可以實現Java在界面上動態的顯示時間。具體實現方法彙總如下: 1.方法一 用TimerTask: 利用java.util.Timer和java.util.TimerTask來做動態更新,畢竟每次更新 ...
  • 函數又叫方法,是實現某項功能或完成某項任務的代碼塊 #include<stdio.h>void show(){ printf("I like c language");}int main(){ show(); return 0;} 上面的show()是自定義函數, int main()的int是要求 ...
  • 分析PHP腳本Xdebug內置分析器能讓你找到腳本中的瓶頸並用額外的工具諸如KcacheGrind或WinCacheGrind工具可視化。 介紹 Xdebug分析器是分析PHP代碼和判斷瓶頸或確定代碼哪裡運行過慢需要使用加速器的強大分析器。Xdebug2的分析器輸出信息以cachegrind相容文件 ...
  • 批量插入sql語句: INSERT INTO table (field1,field2,field3) VALUES ('a',"b","c"), ('a',"b","c"),('a',"b","c") mybatis通過foreach迴圈拼裝瞭如上的sql語句。 一、xml 說明: mysql批量 ...
  • /**寶寶我英語不好,後面註釋拼音 請見諒**/ 1.Linux 開源的操作系統,在伺服器端用戶數量非常大,很多伺服器都是使用Linux系統運行的。 相對windows系統來說具有非常完善的用戶許可權系統,安全繫數非常高 2.Cygwin (cwin) 在windows平臺模擬Linux環境 3.Ap ...
  • 在讀這個模式,頭腦里就浮想兩個問題: 1. JavaScript的原型模式與普遍的原型模式有什麼區別? 2. JavaScript的原型模式與prototype有什麼關係? 原型模式定義 原型模式(創建型設計模式)是用一個對象做模板,克隆出新對象。 另外原型模式中的克隆分為"淺克隆"和"深克隆": ...
  • 在公司ERP項目開發中,遇到批量數據插入或者更新,因為每次連接資料庫比較耗時,所以決定改為批量操作,提升效率。庫存檔點導入時,需要大量數據批量操作。 1:資料庫連接代碼中必須開啟批量操作。加上這句,&allowMultiQueries=true,完整的如下: jdbc:mysql://localho ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...